µObjects: Encapsulation

Maintainability through limiting change with Encapsulation

Our goal as software developers is to produce code that can be easily maintained. This is frequently a challenge; as writing good code is hard.
µObjects is a way of Object Oriented Programming that I've found gives us this. There are a number of ways that µObjects improve the maintainability of code; but how do we create the objects to get these wins? There's a number of guidelines in place for creating the µObjects that give you these. Let's look at one of the foundational ideas of Object Oriented Programming - Encapsulation.

The topics of interest to us about encapsulation are the death of Data Bag classes and not returning your privates.
We'll cover a couple high level concepts for this which will help drive code to be more OO; and will start leading into why µObjects.


Be respectful.

This is the core of how to consider an object and treat it with encapsulation; be respectful to the object.

Wha????

I've gotten a few odd looks when I've used this to describe how to write code, "Be respectful to the object". I follow up with some additional details.

Anthropomorphize[^1] the object you're interacting with. Treat it like it's a person. This has been espoused for years to facilitate Object Oriented Programming style.
Object Thinking[^2] talks about it; I expect earlier books do as well; it's just the reference book that I know does it; and is one of the earlier I have.
Fred George[^3] also talks about doing this in his OOP Bootcamp training. It's a powerful technique to give the responsibility to the object.

I use the example of eating out with a friend and them paying the bill.

  • Direct Access : Reach into their pocket, take their wallet, pay.
  • Getter/Setter : Give me your wallet, pay.
  • OOP : Give them the bill and ask if they can pay.

No Getters or Setters

This leads into one of the major concepts for Encapsulation - No access to the class variables.
This is the whole of encapsulation really. No external access to the class variables. Not even pretend. Creating a new string; copying to a new array; returning a new pointer - Whatever it is; we don't give out data.
Get this concept down; your data doesn't go out; and that will help with most of the additional concepts that OOP has and µObjects as well.

I've covered one aspect of encapsulation; we don't give back our data. What this forces us to do is ... well... Be Polite. We need to be polite to our object and ask them to do something for us.
I want my friend to pay; I give him the bill and ask. He then has the opportunity to look at the bill and tell me to piss off. Or I can ask him to pay his part, and I get back a new bill with my amount to pay.
Otherwise we'd get his wallet, have to check the amount; then pays all or some. I'll assume his wallet is chained (reference) so me leaving it on the table isn't going to lose it. Over all though; pretty damn rude.

Be polite to your objects; ask them to do things for you. A simpler; and probably more likely situation is getting a "User" object's name.
No.
That's getting their data. Ask them to do something with the name - Why do you want the name? What are you going to do with it?
Have the object do it. Going to put it into a label? Pass the label to the object.
There's some tradeoffs; but it's the goal; having the data never leave the object. Resist it as much as you possibly can; then resist it some more.

This is going to be a new concept; you're going to do it wrong. That's ok - get better.
No getter/setters has another effect that leads into µObjects; but that'll be in another post; you'll experience it yourself as you work through the concept.

Law of Demeter

Another aspect of limiting Getters and Setters is that running a foul of the Law of Demeter[^4] isn't possible.
This isn't about just a trainwreck of dot's. It's about tight coupling. If an object holds "something" we need to ask something of; then we ask the object we hold. We don't get it's internal state and it's internals and then it's ... well... it's internals all the way down. Nope. If the object gives us something back, it's never the object it has a reference to. The request is either delegated or a new instance created.

This is the Law of Demeter. Basically boils down to; Don't return class variables.
A form, that will look very similar, but is very different, will be something that builds an object to return.
An example is to ask questions about a person's age. If the person holds a birthdate (date object of sometype) then when you want to ask questions of the person's age; such as "Are you a minor?" you can put that as a method on the person; which makes the person do a lot of stuff. The class will become huge and unmaintainable - the person class will have A LOT of reasons to change; this is bad code. It is the wrong way to create an object.
What we do in this case - Have an Age method. This creates a new Age object and returns it.

public Age Age() => new Age(_birthdate);

This is a builder method[^5] and we can repeated call builder methods on the objects.

user.Age().At18().At21().AtBirth();

Each of these is returning a new Age object.

class Age{
  public Age(DateTime birthdate) : this(birthdate, DateTime.Now){}
  public Age(DateTime birthdate, DateTime date){ ... }
  public Age At18(){
     return new Age(_birthDate, _birthdate.AddYears(18));
  }public Age AtBirth(){
     return new Age(_birthDate, _birthdate);
  }
  public bool IsMinor(){
     //Not 100% sure on the syntax here
     return _birthdate.Minus(_date) >= new TimeSpan(13,0,0,0);
  }
}

We're not accessing levels of private variables; which is the violation of the Law of Demeter.

No '-er' and '-or' classes.

The last style to maintain high maintainability is to avoid the evil that is classes that end in -er and -or. These normally need access to the private data; or at least know what the private data is. Think of the simplest of -ers; the Builder. This knows everything the class needs; it has intimate knowledge of the class. This decreases maintainability.
I have them; I find that sometimes I lack the correct architecture or conceptualization to be able to get rid of them. They smell. I know. I leave them in until I find the correct way to remove them. As long as it stays smelly; I'll keep looking for a good solution.

With these practices in place; you're well on your way towards building great µObjects.

Show Comments