Lessons: 24Length: 3.5 hours

Next lesson playing in 5 seconds

Cancel
  • Overview
  • Transcript

3.1 Adapter

Often, when designing complex systems, you will run into a scenario where you have a number of types that are similar, but not exactly the same. To further complicate matters, you will often need to treat all of these similar types the same way, but they may not share a common base type (super class or protocol). In these scenarios, we can take advantage of the adapter pattern to "wrap" existing instances in a common interface to treat them all the same.

3.1 Adapter

Now it's time for us to journey away a little bit from the creational design patterns that we've been spending quite a bit of time with up to this point, and shift to another category. And this category is known as the structural design patterns. Just like the name implies, we're going to be using these patterns to provide some much needed structure to our code and to our applications. As well as being able to increase the performance of our applications and bits of code here in a number of instances. But we'll get to that in a little bit. So as you can see here, I've created a new playground and we're gonna start with the AdapterPattern. And the AdapterPattern is one of my favorite structural patterns. As it really is a simplistic idea, but it's very magical in that it allows us to do some very cool things. So let's kind of level set here and let's talk about a scenario. Let's say that you and your friends or you and your co-workers spend a lot of time exchanging messages and chatting over a number of the different chat protocols or chat applications out there. So we could be talking about Slack or you could talk over Google, or you could use Jabber, or things of that nature. And maybe a number of your friends or co-workers happen to be spread out on a number of these different protocols and clients, and it makes communicating with everybody very difficult. Maybe you spent some time, and you learned about the APIs for a couple of these that most of your friends and colleagues were on. So you created yourself a kind of a master messenger application where you were able to just blast out messages to everybody on all those different clients without having to worry about sending them to all of them individually by hand, manually. So that's kind of a nice little concept. So I'm gonna drop a little bit of code in here, and let's talk about what we're doing here. So what I've done is, I've created this MessageSender protocol, which has a single function in it. And yes, I've kinda boiled this down to very simplistic ideas, but you'll kinda get the picture. So this protocol has a single function called send and I can send a message. So now I can create a number of implementations of this protocol for all the different chat applications out there that I want to interface with. So right now, out of the box I have one for Slack and then I have one for Jabber, which are just going to print out messages so that I send a message, whatever that message was, to Slack, to Jabber. So you can get the point, you can add in as many here as you want. But then the interesting idea here is that I've created this kind of main messenger class which is the one that I'm gnna interface with. Which has all of these different MessageSender implementations registered within it. So that I can simply send one or call one function within this class to send that message out to all of the different registered message senders that are out there. Or at least that I've implemented to this point. So just to kind of get an idea here we can say, let messenger equal to a new instance of my messenger. And once I've done that, I can now say Messenger. And I can simply send to all messengers, I can say Hello there from Derek. So once that all finishes running, you're gonna see down at the bottom that I've sent that message to Slack and to Jabber. So this is a fairly simplistic idea, nothing too crazy going on here. But maybe you have, or I have, written all of these implementations myself and it gets a little tiresome to have to do all of that. So maybe there's another third-party library out there that would allow me to very quickly and easily integrate with another service. So let's say, for example, we're talking about HipChat. So I wanna be able to integrate with HipChat so I can send messages to all of my friends and colleagues on that provider, as well. Now, I'm just going to create a very simplistic version of that right here. But let's just say because this is a third-party implementation, it's gonna look a little bit different. So maybe this is also going to have to take in an identifier, which is gonna be a string. So maybe part of the HipChat protocol is to provide some sort of guid. That is going to be used to track this conversation from me initiating it all the way thought the HipChat system and all the way back to myself, to verify that it was truly me being part of that conversation. So then what we're going to do here, is maybe this print statement is going to look a little bit different. That we sent message, whatever it was, with identifier, whatever that identifier is, and we sent that to HipChat. Now, is this a problem? No it's not really a problem, but the interesting thing here is now this concept of sending messages to HipChat looks different than my implementations of my MessageSender protocol. So how would I integrate this into my application to be able to integrate with this other third-party. Well, it doesn't really fit into my current structure here. So one common way to handle this is to just kind of create a one-off implementation to support this new messaging client. So one thing I could do is after I loop through my nice little code here to send this message out to all of the registered message senders, I could just kind of come down here and say all right, fine. We're just gonna say, let messenger be equal to my HipChatMessenger. And then, from there, I'm simply going to send, using this other kind of send implementation or function signature where I could send that message that I wanted to send. And then I could just use the foundation library here and just create a unique identifier UUID string, like that. And once this all finishes updating and running, you're gonna see that I'm now sending it to all three. Now that'll work, and you're probably looking at that saying, I've written code like that before. Because believe me, I know I have. But I'm gonna show you a much better way. So the problem we're introducing here is that whenever we run into implementations that don't really fit the mold that we want it to, we tend to go outside of our boundaries that we've set up for ourselves, and best practices, and we just kind of start to throw things together. And what we've done here now is we've kind of created a bad practice here now where any time I wanna integrate with another messaging client that doesn't fit my protocol, I'm just gonna start throwing lines down here to start blasting things out. When this is gonna create some really bad spaghetti code and it's gonna create some really nastiness that we really would try to avoid in most circumstances. But you might be saying, but it doesn't fit our protocol, how do we fix it? Well the answer is in the adapter pattern, so I'm gonna show you how to use that right now. So we've kinda used this HipChat messenger, maybe SDK that we got from their website. And we wanna be able to use that messenger the same way we are down here with all the other ones even though it doesn't implement the MessageSender protocol. So the first thing that we wanna do is we wanna create an adapter class. So I'm gonna create a HipChatMessengerAdapter, and that adapter is going to implement our MessageSender protocol just like we want to use for all of our integrations. Now, within this class, we're gonna need to hold onto a private instance of this HipChatMessenger, just like that. And this is going to be equal to a new instance of that HipChatMessenger. So now I have an instance of that and I can use it whenever I need to. But because we are implementing or creating an implementation of the MessageSender protocol, I still need to implement this send function. So let's go ahead and implement the send function. So what does the send function look like? Well now what I want to do is I want to take whatever is being passed into that function and I kind of want to shift it and send it out via this HipChatMessenger. So now I can say HipChatMessenger.send and I can pass that message in there. And now I'm free to do anything that I want or anything that I need to do with anything else that shows up in that function even though its concrete implementation down here doesn't follow along with my MessageSender protocol. So I'll just do what I did before in here. And I'll simply use the UUID, and I will get a uuidString. So that's gonna be that unique identifier. So there, now I've created this HipChatMessenger adapter that is conforming to the protocol that I want. So now what I can do is I can use this adaptor where I would normally be using these message senders. So now I don't have to create this special implementation to handle my HipChat implementation. I can simply come into my messenger class and I can now register my HipChatMessenger adapter class instead. And once this reruns you're gonna see now at the bottom that I'm going to now be able to send all of these messages to Slack, to Jabber, and with this identifier to HipChat, even though that particular implementation initially didn't follow my protocol. I've been able to adapt it or wrap it in an adapter, so that from the outside world, it looks like the implementation follows my MessageSender protocol to the T. And that there is the basic idea behind the adapter, where I can now adapt and exist in class to make it look like another protocol that I want to use and then I can use it just as I would any other instance of those protocols.

Back to the top