1
00:00:01,040 --> 00:00:03,140
Let's implement a second decorator class

2
00:00:03,140 --> 00:00:04,530
so we can see how to use multiple

3
00:00:04,530 --> 00:00:07,440
decorators together and how they interact.

4
00:00:07,440 --> 00:00:09,160
For this example, I'm going to implement a

5
00:00:09,160 --> 00:00:11,840
decorator that caches our weather request.

6
00:00:11,840 --> 00:00:13,400
So if the same weather conditions or

7
00:00:13,400 --> 00:00:15,370
forecast is requested for the same city in

8
00:00:15,370 --> 00:00:17,560
a given period of time, we'll use a value

9
00:00:17,560 --> 00:00:19,220
pulled from our cache instead of making

10
00:00:19,220 --> 00:00:22,190
another API call. This is another common

11
00:00:22,190 --> 00:00:24,420
use of the decorator pattern, to cache a

12
00:00:24,420 --> 00:00:26,290
call that goes out to an external service

13
00:00:26,290 --> 00:00:28,770
or system or otherwise takes a long time

14
00:00:28,770 --> 00:00:30,970
to execute. Using the decorator to

15
00:00:30,970 --> 00:00:32,520
implement caching gives you a good

16
00:00:32,520 --> 00:00:34,400
separation of concerns and is a very

17
00:00:34,400 --> 00:00:37,630
elegant solution to this problem. So I'm

18
00:00:37,630 --> 00:00:38,720
going to move pretty quickly through the

19
00:00:38,720 --> 00:00:40,780
initial class setup because we saw that in

20
00:00:40,780 --> 00:00:43,410
the previous clip. First, I'll go ahead

21
00:00:43,410 --> 00:00:46,580
and create the decorator class, and then

22
00:00:46,580 --> 00:00:48,330
I'm going to implement the IWeatherService

23
00:00:48,330 --> 00:00:51,100
interface. I'll add a member variable for

24
00:00:51,100 --> 00:00:53,410
the object that I am wrapping, and I will

25
00:00:53,410 --> 00:00:56,660
create my constructor. Now what I need to

26
00:00:56,660 --> 00:00:59,060
do is to add a cache object, and for this

27
00:00:59,060 --> 00:01:01,060
demo, I'm just going to use .NET Core's

28
00:01:01,060 --> 00:01:03,740
IMemoryCache. So let me add another member

29
00:01:03,740 --> 00:01:06,560
variable for that. And then I need to add

30
00:01:06,560 --> 00:01:08,470
that to my constructor. So when this

31
00:01:08,470 --> 00:01:10,520
decorator object gets created, I can have

32
00:01:10,520 --> 00:01:12,710
that cache object handed to me. So there

33
00:01:12,710 --> 00:01:16,260
we go. Now, let's go implement the caching

34
00:01:16,260 --> 00:01:18,020
in our methods, and like before, I'll

35
00:01:18,020 --> 00:01:20,910
start with the GetCurrentWeather method.

36
00:01:20,910 --> 00:01:22,910
I'll start by defining a cacheKey that I

37
00:01:22,910 --> 00:01:25,590
want to use. And I'll use some C# string

38
00:01:25,590 --> 00:01:28,580
interpolation for that. So now what I want

39
00:01:28,580 --> 00:01:30,060
to do is to check the cache to see if we

40
00:01:30,060 --> 00:01:31,310
have the current weather for this

41
00:01:31,310 --> 00:01:33,530
location. And the way that I do that is

42
00:01:33,530 --> 00:01:37,110
with an if statement, and then on my cache

43
00:01:37,110 --> 00:01:40,240
object I say TryGetValue. And I'll want a

44
00:01:40,240 --> 00:01:42,370
CurrentWeather object here for the generic

45
00:01:42,370 --> 00:01:46,410
type, and then I pass in my cacheKey, and

46
00:01:46,410 --> 00:01:48,100
then this second parameter is an out

47
00:01:48,100 --> 00:01:50,473
variable. So if I say out var

48
00:01:50,473 --> 00:01:52,980
currentWeather, if there's a value in the

49
00:01:52,980 --> 00:01:55,310
cache for this cacheKey, then it's going

50
00:01:55,310 --> 00:01:56,980
to go ahead and put that value in this

51
00:01:56,980 --> 00:02:00,030
variable. And if there was, I can simply

52
00:02:00,030 --> 00:02:03,260
return that. If there was not a value in

53
00:02:03,260 --> 00:02:05,370
the cache, then I want to come down here

54
00:02:05,370 --> 00:02:08,170
and I'll need an else block. And then what

55
00:02:08,170 --> 00:02:09,730
I'll want to do is I'll want to call

56
00:02:09,730 --> 00:02:12,780
GetCurrentWeather on that inner object. So

57
00:02:12,780 --> 00:02:14,160
this is where the wrapped service is

58
00:02:14,160 --> 00:02:17,290
getting called. Then, once I have that

59
00:02:17,290 --> 00:02:20,100
value, I'll want to add that to the cache.

60
00:02:20,100 --> 00:02:23,280
So I'll do that here, and I'll cache that

61
00:02:23,280 --> 00:02:26,350
value for 30 minutes, and then I'll go

62
00:02:26,350 --> 00:02:29,200
ahead and return this value. So this is a

63
00:02:29,200 --> 00:02:30,860
little bit of a different implementation

64
00:02:30,860 --> 00:02:32,910
this time. In this case, if we already

65
00:02:32,910 --> 00:02:34,990
have the value in the cache, then we never

66
00:02:34,990 --> 00:02:36,760
need to call the wrapped WeatherService

67
00:02:36,760 --> 00:02:39,170
object. We can short circuit the call and

68
00:02:39,170 --> 00:02:40,870
just return directly the value that we

69
00:02:40,870 --> 00:02:43,270
have. And this is a perfectly valid thing

70
00:02:43,270 --> 00:02:45,700
to do. In some cases when you intercept a

71
00:02:45,700 --> 00:02:48,640
call, you don't need to go any further.

72
00:02:48,640 --> 00:02:50,640
It's only in our second case when we don't

73
00:02:50,640 --> 00:02:52,630
have the necessary data in the cache that

74
00:02:52,630 --> 00:02:54,340
we need to keep calling down the stack of

75
00:02:54,340 --> 00:02:56,790
our IWeatherService objects, and we see

76
00:02:56,790 --> 00:03:00,140
that behavior there. So now, like before,

77
00:03:00,140 --> 00:03:01,870
I'm going to go ahead and copy and paste

78
00:03:01,870 --> 00:03:04,250
in an implementation for the GetForecast

79
00:03:04,250 --> 00:03:07,090
method. And then I'm going to go back up

80
00:03:07,090 --> 00:03:08,990
to the HomeController so we can wire up

81
00:03:08,990 --> 00:03:12,730
this additional decorator. I could come in

82
00:03:12,730 --> 00:03:14,290
here and wrap these two existing

83
00:03:14,290 --> 00:03:16,080
constructors with the caching decorator

84
00:03:16,080 --> 00:03:18,050
constructor that I just created, and that

85
00:03:18,050 --> 00:03:20,690
would work perfectly fine. However, I'm

86
00:03:20,690 --> 00:03:22,230
not going to do that because when you

87
00:03:22,230 --> 00:03:24,340
start to get three or four layers of these

88
00:03:24,340 --> 00:03:26,480
decorators, it can get a little bit hard

89
00:03:26,480 --> 00:03:28,870
to read and see what's going on. So

90
00:03:28,870 --> 00:03:30,320
instead, I'm going to paste in some

91
00:03:30,320 --> 00:03:32,900
different code here. And you can see what

92
00:03:32,900 --> 00:03:34,620
I'm doing here. I'm just constructing

93
00:03:34,620 --> 00:03:36,120
things from the inside out, and I'm

94
00:03:36,120 --> 00:03:38,200
passing each object to the subsequent

95
00:03:38,200 --> 00:03:40,660
object's constructor. So this is building

96
00:03:40,660 --> 00:03:43,090
that onion from the inside out. I just

97
00:03:43,090 --> 00:03:44,710
find this code a little bit easier to

98
00:03:44,710 --> 00:03:47,740
read. So now let's go ahead and run this

99
00:03:47,740 --> 00:03:51,090
so we can step through it. And again, I'm

100
00:03:51,090 --> 00:03:52,950
landing in the Index action on the

101
00:03:52,950 --> 00:03:56,530
HomeController. If I step into this code,

102
00:03:56,530 --> 00:03:57,780
now you'll see that I'm in the caching

103
00:03:57,780 --> 00:04:00,500
decorator that we just wrote. And since

104
00:04:00,500 --> 00:04:02,270
the app just started I expect the cache to

105
00:04:02,270 --> 00:04:04,210
be empty. And indeed we've jumped down

106
00:04:04,210 --> 00:04:07,260
here to the else block. So now we'll step

107
00:04:07,260 --> 00:04:10,090
into the next GetCurrentWeather call. And

108
00:04:10,090 --> 00:04:11,880
you can see that now we're in our logging

109
00:04:11,880 --> 00:04:13,910
decorator, because remember, the logging

110
00:04:13,910 --> 00:04:16,150
decorator was the next or the middle layer

111
00:04:16,150 --> 00:04:18,820
of the onion. So I'll step through that

112
00:04:18,820 --> 00:04:20,980
here, and now into the next

113
00:04:20,980 --> 00:04:23,990
GetCurrentWeather call. And now I'm in my

114
00:04:23,990 --> 00:04:26,590
concrete WeatherService class. This will

115
00:04:26,590 --> 00:04:28,440
make the API call, I'm going to actually

116
00:04:28,440 --> 00:04:30,830
step out of this and let it do it, and

117
00:04:30,830 --> 00:04:32,450
you'll see that I'm back up in the logging

118
00:04:32,450 --> 00:04:35,640
decorator and stepping through. Now I'm

119
00:04:35,640 --> 00:04:38,340
back up in the caching decorator. And so

120
00:04:38,340 --> 00:04:40,700
now I'll go ahead and let all this run

121
00:04:40,700 --> 00:04:44,400
through. What you can see there is that

122
00:04:44,400 --> 00:04:46,270
wrapping or layering effect was very

123
00:04:46,270 --> 00:04:47,970
apparent because first we started out in

124
00:04:47,970 --> 00:04:50,060
our caching decorator, we moved down into

125
00:04:50,060 --> 00:04:52,020
the logging decorator, and finally ended

126
00:04:52,020 --> 00:04:55,540
up in the concrete WeatherService class.

127
00:04:55,540 --> 00:04:56,830
So I'm going to do one more thing, I'm

128
00:04:56,830 --> 00:04:58,570
going to refresh this page and run through

129
00:04:58,570 --> 00:05:01,560
everything again. And I'll step into this

130
00:05:01,560 --> 00:05:03,730
method, and you can see that I'm in the

131
00:05:03,730 --> 00:05:06,480
caching decorator again. But this time as

132
00:05:06,480 --> 00:05:08,790
I step through this, we already have data

133
00:05:08,790 --> 00:05:11,470
for Chicago. So in this case, I'm just

134
00:05:11,470 --> 00:05:13,210
going to return right back out. We're not

135
00:05:13,210 --> 00:05:14,720
going to call down into those other

136
00:05:14,720 --> 00:05:17,900
objects. So again, another use of

137
00:05:17,900 --> 00:05:19,940
decorators. But you should start to be

138
00:05:19,940 --> 00:05:21,450
able to see how you can layer these

139
00:05:21,450 --> 00:05:23,580
individual decorator classes together with

140
00:05:23,580 --> 00:05:26,230
each one focused on a specific task, but

141
00:05:26,230 --> 00:05:28,020
building up to the overall functionality

142
00:05:28,020 --> 00:05:29,960
that you desire. And this is one of the

143
00:05:29,960 --> 00:05:31,900
great strengths of the decorator pattern

144
00:05:31,900 --> 00:05:33,440
is how we can layer this functionality

145
00:05:33,440 --> 00:05:35,510
together and add in new functionality as

146
00:05:35,510 --> 00:05:40,000
needed, or even resequence that functionality as we need to.

