Exploring the Observer Pattern Using Functions10:22 with Craig Dennis
Java 8 makes functions a first class citizen. Look at how quickly the pattern can be applied by embracing them.
We saw the power of rapidly being able to implement the pattern 0:00 by looping through subscribers using our parameter as list. 0:03 Well there is, in fact, a growing movement to make that even quicker. 0:07 Functional programming has been around for a long time, but 0:10 it's relatively new to the Java world. 0:13 So there are newer patterns forming that are leaning on this powerful, 0:15 succinct approach. 0:18 Let's give it a whirl. 0:19 If you haven't played around with Java 8's functional programming side of things, 0:22 I hope that this example will encourage you to explore it a bit. 0:25 Now our final project for this workshop is going to explore an Olympics tracking app. 0:29 So we have a class called GoldMedalReporter. 0:33 Let's open that up. 0:37 So under gold-watcher here, there's GoldMedalReporter, and 0:38 it's a pretty bare bones class, right? 0:42 It's just for demo purposes, 0:44 it has a single method, it's called reportWin, and basically, 0:45 it just says the gold medal was awarded to whatever country was best at it, right? 0:49 So, we have all these other tools that we would like to integrate with this, right? 0:53 So, over here under tools, we have this, what is a Twitter client called Tweeter. 0:57 You can imagine that this can x out to the API, but basically, 1:02 it takes whatever message was passed out and sends it to Twitter. 1:05 There's also an LED light bulb that's here and you pass in an array of colors. 1:09 It's using this var args pattern, so what happens is an array will come in here, and 1:14 then we're going to use a Java 8 stream to collect and 1:19 make it comma-separated, basically. 1:23 More in the teachers notes on that if you're interested. 1:26 And finally, we have one of these huge screens that gets fans all worked up, 1:29 called a MegaTron. 1:33 And basically, 1:35 whatever you pass in, it says, Let's hear it for whatever was passed in. 1:36 Yeah, MegaTron. 1:39 Such a funny name for that, that that stuck, MegaTron. 1:41 So, anyway, the method here just roots for whatever you give it, right? 1:45 So, what we want, we want all these different tools to do their 1:49 thing when a gold medal happens, right? 1:53 When one of these wins are reported, we want these tools to do their thing. 1:55 Now it's a fairly specific to the tool type of method that we want to 1:59 take off, right? 2:03 Now we could do this by creating a base class, and 2:04 maybe we could mix in the adapter pattern and make them all look similar. 2:07 Or we could create a specific function for our use case. 2:11 So let's try that first. 2:16 So In the GoldMedalReporter, right, 2:18 we want to first create a way to add an observer-like function. 2:21 Okay, so let's do that, let's just say 2:27 public void, and we'll say addObserver. 2:32 The observer that's gonna be added here is like we just talked about, 2:38 it's a specific function. 2:41 So Java 8 introduced the concept of functional interfaces, 2:43 which allows you to specify an expected method signature. 2:47 So what we're gonna do is we're gonna use a functional interface that expects 2:51 a single string pattern and returns nothing. 2:55 So that type of interface is called a consumer, and 3:00 its type here that you specify is what the first parameter is. 3:04 So that was a string, and we'll call this observer. 3:08 Now we want to provide an easy way to remove these, so 3:21 the typical implementation in removing these is by passing in the observer. 3:24 But, we are going to be providing probably a lambda or 3:29 an anonymous function, so removing it by value is gonna be a little tricky. 3:33 So let's have the registering of observers need to add a key as well. 3:38 So we'll say String key. 3:42 So you'll name it and 3:47 then pass in the function, so it's like naming an anonymous function. 3:49 So we have a key and a value that we want to store. 3:53 That sounds pretty much like a map to me. 3:55 So let's add a new class up here, we'll say private map, and it's gonna be String, 3:58 which is the name of the function that we're going to give it, and Consumer. 4:03 This is the values, right? 4:08 So the value is going to be Consumer that we talked about. 4:09 It is a Java util map, and we'll call it observers. 4:12 And we'll say new HashMap. 4:17 Cool. 4:20 So now in our addObserver what that looks like is observers.put, 4:23 and we'll pass in the key and the observer. 4:30 Here we go. 4:35 So now our map is of name to function. 4:37 And we could very easily now remove as well. 4:41 Right, so we'll say public void removeObserver(String key) and 4:44 we'll say observers.remove, and you pass it a key. 4:52 And now we've got registering and unregistering of functions. 5:01 Now we need to notify the observer. 5:04 Let's make it clear, right? 5:07 Let's let people know that we're using the observer pattern. 5:10 So we'll say, notifyObservers. 5:13 And that's gonna take the country, right? 5:17 That's the string that we wanna pass in. 5:19 We're gonna pass in a country. 5:20 And all right, so we have a map. 5:23 And the values of the map are functions, right? 5:26 So observers.values. 5:29 And for each of those, so we'll get back a function. 5:33 And we want to call the method that was passed in, and 5:39 for a consumer, that's called accept, right. 5:44 And the reason why it's called accept is because it accepts the value that 5:48 was passed in, which in our case is gonna be country. 5:52 So in our reportWin, let's just go ahead and we'll say notifyObservers, 5:59 and we'll also pass in the country there. 6:03 Cool, so again, this is gonna loop through each of the functions that were passed in, 6:06 for each of the functions that's going to call the function, passing it the country. 6:10 Awesome, so let's do the Twitter one first. 6:14 So let's pop back into the examples here, and we have a reporter object, 6:17 so I'm gonna say reporter.addObserver, and let's call this observer tweet. 6:23 I think that makes the most sense. 6:30 So we could get that key out later. 6:32 And now it's asking for a consumer, and remember, 6:34 a consumer is a method that takes a string and returns nothing. 6:36 So we should be able to just say country. 6:42 Now because this is a lambda, 6:45 there is only one value we don't need to parentheses. 6:47 We're gonna say Tweeter.tweet and let's see, 6:50 we'll say country, Just won the Gold! 6:55 #olympics. 7:03 And it doesn't know what Tweeter is, so there we go. 7:07 Let's see if we can make this a prettier. 7:11 Screen is pretty tiny, let's do this, too. 7:14 Bring that up on one line here, and we'll put a semicolon at the end. 7:21 Okay, awesome. 7:25 So we've added observer, that will take a country and then call the tweet. 7:26 So let's do the next one. 7:30 Let's do the Megatron. 7:32 So the key is definitely gonna be megatron, 7:33 addObserver megatron. 7:39 And basically all we want to do, remember, this one just cheers for 7:43 whatever was passed in. 7:47 Basically, all we want to do is just pass in country to this method, right? 7:48 So because the method signature of that display method is actually a consumer, 7:51 right, it's actually the same. 7:56 A method that returns nothing and 7:59 accepts the string, we can just use the method reference, right? 8:01 So if I do megaTron:: that says, 8:04 I'm talking about a specific method, and the method is display. 8:08 So what that will do is it will, when it's observed, 8:13 it will pass the value into this instance. 8:16 Pretty cool, right? 8:19 Okay, and finally let's go ahead and 8:22 add an observer for our lights, LED light. 8:25 So, it's gonna take a country. 8:30 And we have a lights variable here. 8:34 So I wanna say light.flashColors. 8:36 And we need to access another method that's outside of this, 8:40 but that's okay, right? 8:45 We can call methods from inside of a lambda, so 8:46 we'll call colorsByCountry.get country, 8:48 and we'll hope that whatever is passed matches. 8:54 Right now it's only USA and Brazil. 8:57 Other countries do matter, I just didn't take the time to fill that out. 9:00 Just so you know, everybody, you matter. 9:03 And I just wanted to point out that these methods could be as complicated 9:07 as we need. 9:10 But most of the time, they would probably end up looking a lot like this, right? 9:11 They're pretty small little methods. 9:14 No new classes, very light interface, and 9:16 we did a good job of conveying that these are in fact observers, right. 9:19 We have all this addObserver language. 9:23 Let's go ahead and let's run this. 9:25 So I'm gonna click this Run 'Example.main()', and boom, 9:28 the gold medal has been awarded to the USA. 9:33 Let's hear it for the USA. 9:35 USA won the gold, #olympics, flashing red, white and blue. 9:36 Awesome, right? 9:40 We just went for the gold and we won. 9:41 So keep your eye out for 9:43 more patterns being applied in this more declarative style. 9:45 Do remember that these functions are closures, right? 9:48 So any value that's put in here will be captured, will be closed over. 9:51 So this approach, as well as the last one, will keep objects alive and 9:57 safe from garbage collectors. 10:01 So it's very possible to cause a memory leak if you aren't careful, 10:04 of removing observers when they're no longer needed. 10:07 So what do you think? 10:10 Pretty quick to implement, isn't it? 10:11 So now that you've seen the evolution of this pattern over time, 10:13 I hope you've got a really solid understanding of why you should use it, 10:16 as well as how to make the magic happen. 10:20
You need to sign up for Treehouse in order to download course files.Sign up