From the course: Advanced Design Patterns: Design Principles

Encapsulate what varies

- [Instructor] We're going to start with what many consider the most important design principle, encapsulate what varies. Not only is this principle in some sense the most foundational principle, but it's also especially relevant for this advanced design pattern series because this principle is found at work in almost all design patterns. To spell the principle out a little more, this principle suggests that you identify the aspects of your application that vary and separate them from what stays the same. In other words, if you've got some aspect of your code that's changing, say with every new requirement that you get, well, then you know, you've got a behavior that really needs to be pulled out and separated from all the stuff that doesn't change. Why? So that later you can alter or extend the parts that vary, but do it without affecting the parts that don't vary. As simple as that concept is, again, as you're going to see, it really forms the basis for every design pattern, and of course, there are many ways to pull something out of your design to encapsulate it, and that's often where patterns actually come in because patterns demonstrate different methods of separating aspects of your design, that is, all patterns tend to provide some way to let some part of your system vary independently on other parts, and again, as you'll soon see, patterns do this in some very different ways depending on the problem being solved. But even without getting into design patterns right now, let's look at a very simple example of this principle at work. Let's say we have a system for ordering pancakes, and I'm sure you've all seen code similar to this before. Here we have an order pancake method, and we're passing the type of pancake we want to make into that method, and then we have this conditional statement, and that statement determines the correct concrete class, the correct concrete pancake to instantiate, and then after that, with whatever pancake we did instantiate, we're going to cook that pancake, we're going to plate it, we're going to add some butter, and then we're going to return that pancake, so it can be served. Simple enough, but let's think about how this code changes over time. Now there are going to be requests over time to take pancakes off the menu, perhaps they're poor selling, likewise, we're going to have requests to add pancakes to the menu, say there's a new type of pancake or a special. So over time, we're going to end up with all the code that is selecting the type of pancake to instantiate that's going to keep changing with every new requirement that we have, and worse, that code that we're changing, it's in the same file and in the same class with a bunch of important code that's controlling the production of the pancakes themselves, it's cooking the pancake, it's plating it, it's adding the topping on it, and then it's returning it. If we make a mistake updating the menu, we can bring down the entire pancake house because all this code is in the same place. Now our design principle or guideline tells us to encapsulate what varies. In other words, we want to find what is varying and pull it out into it's own self-container thing, so that we can change or alter or update that on its own. Looking at this code, we can see that the conditional code that's choosing the type of pancake to instantiate is actually what is varying, and it varies with each change of the menu, so how do we encapsulate it? How are we going to pull this out? Now there are many ways to think about pulling this code out including the use of some design patterns, which we'll get to later in the series, but for now, we're going to demonstrate encapsulating this code using a very simple technique, and that technique is to take the code that's varying, that conditional code, and we're going to move it out into it's own class, and we're going to call that class a factory, in this case, a simple pancake factory, and it's going to have one method, a method called create pancake, and so we're just going to have one class that has the sole responsibility of making those pancakes of instantiating those objects, and any changes we need to make in the future to that menu can all be done localized within that class. Now if we revisit our order pancake method, all that code that previously determined which pancake to instantiate has obviously been moved out, and now we're only left with our call to the factory to create that pancake. So now our pancake menu can be altered at any time without affecting the rest of our code. Our quick redesign now allows the factory that is responsible for making those pancakes to vary independently of the order pancake method, which is responsible for ordering and serving the pancakes. So what you've seen is a very simple way of encapsulating what varies, and it's called the simple factory pattern. It's actually not a first class design pattern, it's a little more of an idiom, but it does demonstrate well, one method, of encapsulating one varies. It's also a good warmup exercise because in this series, we're going to see some additional real patterns for creating factories, factories that are much more sophisticated in the way they provide flexibility and extensibility. Think of the encapsulate what varies principle as kind of the master principle. It's all about letting one part of the system vary independently of another part, something almost all design patterns do in some way. To make use of encapsulate what varies, always look for code that's changing, often changing with every new requirement, that's your signal, your signal that something needs to be pulled out and encapsulated on its own. As we've already discussed, you're going to see this principle play out in almost all design patterns, so throughout the rest of this series, pay close attention to each pattern, and how it makes use of this principle, that principle of letting some part of the system vary independent of the other parts.

Contents