25

One of the OOP principles I came across is: -Encapsulate what varies.

I understand what the literal meaning of the phrase is i.e. hide what varies. However, I don't know how exactly would it contribute to a better design. Can someone explain it using a good example?

  • See en.wikipedia.org/wiki/Encapsulation_(computer_programming) which explains it well. I think the "what varies" is not correct since you should also sometimes encapsulate constants. – qwerty_so Dec 3 '16 at 8:30
  • I don't know how exactly would it contribute to a better design Encapsulating details is about loose coupling between the "model" and the implementation details. The less tied is the "model" to the implementation details, the more flexible is the solution. And it makes easier to evolve it. "Abstract yourself from the details". – Laiv Dec 3 '16 at 21:37
  • @Laiv So "varies" refers to what evolves during the course of your software life cycle or what changes during the execution of your program or both? – Haris Ghauri Dec 3 '16 at 21:56
  • 2
    @HarisGhauri both. Group together what varies together. Isolate what varies independently. Be suspicious of what you assume will never change. – candied_orange Dec 3 '16 at 22:04
  • 1
    @laiv thinking "abstract" is a good point. It can feel overwhelming to do that. In any one object you're suposed to have one responsibility. The nice thing about that is you only have to think carefully about that one thing here. When the details of the rest of the problem are someone elses problem it makes things easier. – candied_orange Dec 4 '16 at 20:14
30

You can write code that looks like this:

if (pet.type() == dog) {
  pet.bark();
} else if (pet.type() == cat) {
  pet.meow();
} else if (pet.type() == duck) {
  pet.quack()
}

or you can write code that looks like this:

pet.speak();

If what varies is encapsulated then you don't have to worry about it. You just worry about what you need and whatever you're using figures out how to do what you really need based on what varied.

Encapsulate what varies and you don't have to spread code around that cares about what varied. You just set pet to be a certain type that knows how to speak as that type and after that you can forget which type and just treat it like a pet. You don't have to ask which type.

You might think type is encapsulated because a getter is required to access it. I don't. Getter's don't really encapsulate. They just tattle when someone breaks your encapsulation. They're a nice decorator like aspect oriented hook that is most often used as debugging code. No matter how you slice it, you're still exposing type.

You might look at this example and think I'm conflating polymorphism and encapsulation. I'm not. I'm conflating "what varies" and "details".

The fact that your pet is a dog is a detail. One that might vary for you. One that might not. But certainly one that might vary from person to person. Unless we believe this software will only ever be used by dog lovers it's smart to treat dog as a detail and encapsulate it. That way some parts of the system are blissfully unaware of dog and wont be impacted when we merge with "parrots are us".

Decouple, separate, and hide details from the rest of the code. Don't let knowledge of details spread through your system and you'll be following "encapsulate what varies" just fine.

  • 3
    That's really odd. "Encapsulate what varies" to me means to hide away state changes, eg never have global variables. But you answer makes sense too, even if it feels more an answer to polymorphism than encapsulation :) – David Arno Dec 3 '16 at 7:49
  • 2
    @DavidArno Polymorphism is one way to make this work. I could have just cramed the if structure into pet and things would look nice out here thanks to pet's encapsulation. But that would just be moving the mess around instead of cleaning it up. – candied_orange Dec 3 '16 at 7:52
  • 1
    "Encapsulate what varies" to me means to hide away state changes. Nay, nay. I like C.O.'s comment. Derick Elkin's answer goes deeper, read it more than once. As @JacquesB said "This principle is actually quite deep" – radarbob Dec 7 '16 at 15:48
16

"Varies" here means "may change over time due to changing requirements". This is a core design principle: To separate and isolate pieces of code or data which may have to change separately in the future. If a single requirement changes, it should ideally only require us to change the related code in a single place. But if the code base is badly designed, i.e. highly interconnected and logic for the requirement spread out in many places, then the change will be difficult and have a high risk of causing unexpected effects.

Say you have an application which uses sales tax calculation in a lot of places. If the sales tax rate changes, what would you prefer:

  • the sales tax rate is a hardcoded literal everywhere in the application where the sales tax is calculated.

  • the sales tax rate is a global constant, which is used everywhere in the application where the sales tax is calculated.

  • there is a single method called calculateSalesTax(product) which is the only place the sales tax rate is used.

  • the sales tax rate is specified in a configuration file or database field.

Since the sales tax rate may change due to a political decision independent of other requirement, we prefer to have it isolated in a configuration, so it can be changed without affecting any code. But it is also conceivable the logic for calculating sales tax may change, e.g. different rates for different product, so we also like to have the calculation logic encapsulated. The global constant might seem like a good idea, but is actually bad, since it might encourage using the sales tax different places in the program rather than in a single place.

Now consider another constant, Pi, which is also used in a lot of places in the code. Does the same design principle hold? No, because Pi is not going to change. Extracting it to a configuration file or database field does just introduce needless complexity (and everything else being equal, we prefer the simplest code). It does make sense to make it a global constant rather than hardcode it in multiple places to avoid inconsistencies and improve readability.

The point is, if we only look at how the program works now, sales tax rate and Pi are equivalent, both are constants. Only when we consider what may vary in the future, does we realize we have to treat them differently in the design.

This principle is actually quite deep, because it means you have to look beyond just what the code base is supposed to do today, and also consider the external forces which may cause it to change, and even understand the different stakeholders behind the requirements.

  • 2
    The taxes are a good example. Laws and Taxes calcs might change from one day to another. If you are implementing a tax declaration system you are strongly tied to this sort of changes. Also changes from one Locale to another (countries, provinces, ...) – Laiv Dec 4 '16 at 14:23
  • "Pi is not going to change" made me laugh. It's true, Pi is unlikely to change, but suppose you weren't allowed to use it anymore? If some people have their way Pi will be deprecated. Suppose that becomes a requirement? Hope you have a happy Tau day. Nice answer BTW. Deep indeed. – candied_orange Dec 7 '16 at 19:23
14

Both of the current answers seem to only partially hit the mark, and they focus on examples that cloud up the core idea. This is also not (solely) an OOP principle but a software design principle in general.

The thing that is "varying" in this phrase is the code. Christophe is on point in saying that it is usually something that may vary, that is you often anticipate this. The goal is to protect yourself from future changes in the code. This is closely related to programming against an interface. However, Christophe is incorrect to limit this to "implementation details". In fact, the value of this advice is often due to changes in requirements.

This is only indirectly related to encapsulating state, which is what I believe David Arno is thinking of. This advice does not always (but often does) suggest encapsulating state, and this advice applies to immutable objects as well. In fact, merely naming constants is a (very basic) form of encapsulating what varies.

CandiedOrange explicitly conflates "what varies" with "details". This is only partially correct. I agree that any code that varies is "details" in some sense, but a "detail" may not vary (unless you define "details" to make this tautological). There may be reasons to encapsulate non-varying details, but this dictum is not one. Roughly speaking, if you were highly confident that "dog", "cat", and "duck" would be the only types you'd ever need to deal with, then this dictum does not suggest the refactoring CandiedOrange performs.

Casting CandiedOrange's example in a different context, assume we have a procedural language like C. If I have some code that contains:

if (pet.type() == dog) {
  pet.bark();
} else if (pet.type() == cat) {
  pet.meow();
} else if (pet.type() == duck) {
  pet.quack()
}

I may reasonably expect that this piece of code will change in the future. I can "encapsulate" it simply by defining a new procedure:

void speak(pet) {
  if (pet.type() == dog) {
    pet.bark();
  } else if (pet.type() == cat) {
    pet.meow();
  } else if (pet.type() == duck) {
    pet.quack()
  }
}

and using this new procedure instead of the block of code (i.e. an "extract method" refactoring). At this point adding a "cow" type or whatever only requires updating the speak procedure. Of course, in an OO language you may instead leverage dynamic dispatch as alluded to by CandiedOrange's answer. This will happen naturally if you access pet via an interface. Eliminating conditional logic via dynamic dispatch is an orthogonal concern which was part of why I made this procedural rendition. I also want to emphasize that this does not require features particular to OOP. Even in an OO language, encapsulating what varies does not necessarily mean a new class or interface needs to be created.

As a more archetypal example (which is closer to but not quite OO), say we want to remove the duplicates from a list. Let's say we implement it by iterating over the list keeping track of the items we've seen so far in another list and removing any items we've seen. It's reasonable to assume that we may want to change how we keep track of the seen items may for, at least, performance reasons. The dictum to encapsulate what varies suggests that we should build an abstract data type to represent the set of seen items. Our algorithm is now defined against this abstract Set data type, and if we decide to switch to a binary search tree, our algorithm doesn't need to change or care. In an OO language, we may use a class or interface to capture this abstract data type. In a language like SML/O'Caml you might instead capture the Set abstract data type as a module.

For a requirements-driven example, say you need to validate some field with regards to some business logic. While you may have specific requirements now, you strongly suspect that they will evolve. You may encapsulate the current logic in its own procedure/function/rule/class.

Though this is an orthogonal concern which is not part of "encapsulate what varies", it is often natural to abstract out, that is parameterize by, the now encapsulated logic. This typically leads to more flexible code and allows the logic to be changed by substituting in an alternate implementation rather than modifying the encapsulated logic.

  • Oh bitter sweet irony. Yes, this is not solely an OOP issue. You caught me letting a language paradigm detail pollute my answer and rightly punished me for it by "varying" the paradigm. – candied_orange Dec 7 '16 at 19:43
  • "Even in an OO language, encapsulating what varies does not necessarily mean a new class or interface needs to be created" - it's hard to imagine a situation where not creating a new class or interface would not violate SRP – taurelas Aug 20 '18 at 16:01
11

"Encapsulate what varies" refer to the hiding of implementation details which may change and evolve.

Example:

For example, suppose class Course keeps track of Students that can register(). You could implement it with a LinkedList and expose the container to allow iteration on it:

class Course { 
    public LinkedList<Student> Atendees; 
    public bool register (Student s);  
    ...
}

But this is not a good idea:

  • First, people could lack of good behavior and use it as self-service, directly adding students to the list, without going through the register() method.
  • But even more annoying: this creates a dependency of the "using code" to the inner implementation details of the used class. This might prevent future evolutions of the class, for instance if you'd prefer to use an array, a vector, a map with the seat number, or your own persistent data structure.

If you encapsulate what varies (or rather said, what might vary), you keep freedom for both the using code and the encapsulated class to evolve each other on their own. This is why it's an important principle in OOP.

Additional reading:

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.