[FITYMI] Open and Closed Principle – Okay, now I have to Refactor

Wow. It’s been a whole month since my last SOLID OOP post. I was doing a good job alternating between maybe useful topics and totally useless ones until I got to this: the O in SOLID. Open and Closed Principle. Oh shit whaddup? Oh fuck the topic is so dry I can’t figure out how to write about it.

There were a lot of things that happened this past month – a trip to Spain and Berlin, grueling job interviews, a new play through of Dragon Age: Inquisition. After all that, I still can’t figure out how to make this topic more accessible. I guess it’s just time to man up and discuss this thing like an adult. Fuck it, I hate being an adult.

The Open and Closed principle states the following:

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification

Now I know a large part of my readership (i.e. my family) isn’t composed of computer scientists and software developers, so I’m going to explain a few basic things before fully exploring this sentence. Also, note that the example I will make is just for the sake of demonstrating the principle. It is NOT a good design for an actual production system, but is enough to prove a point – even for experienced software development professionals IMHO.

The word “extension” in OOP is usually associated with inheritance or subclassing. Wow, more words that would probably mean nothing to you. Maybe an example will be better. Let’s say you made a class called TaxPayer, who is someone who pays taxes. You would write code that will define what the tax payer is (e.g. information like first name, last name, tax identification number) and what it can do.Let’s say it’s just a bag of information for now.

tumblr_odwr5stojs1vzbr0vo1_540

After a while you find out you need different kinds of tax payers because there are different considerations when paying taxes. For example, you’ll have different considerations between single and married tax payers. A way to do this is to extend the original TaxPayer by creating subclasses, namely SingleTaxPayer and MarriedTaxPayer. When you extend a class, the subclasses inherit all the properties of the parent – meaning they can do whatever the parent can do without you writing any additional code for it. You can then add information and behavior to the child without affecting the parent. The child can do everything the parent can, but not the other way around.

[SIDE TOPIC] Why would you do this instead of just adding a civil status field in the TaxPayer class? Well, maybe you have some cases where only MarriedTaxPayers are accepted. For example, if the country is suddenly faced with an aging population and wants to promote procreation, they can create a program that will allow married tax payers to apply for a family starter loan which offers better rates. If you’re coding that, you’ll know at compile time that it will only accept married tax payers, instead of writing a pesky “if-statement” that will need to check if the applicant is single or married. More security and less typing for you.

Going back to the definition of OCP, it says that your class should be open for extension but closed for modification. If you’re like me, the first time you saw that you said to yourself “what the fuck is up with this rule?” You can always extend a class unless it is marked in some special way as infertile and could not have children. Unless you had access to the specific source code, you also generally can’t modify parent class behavior.

If you search for a simpler way of explaining the essence of this principle, you’d get stuff like “you should try to write code that doesn’t need to change even when requirements change”. Big fucking whoop. If requirements change, you will definitely need to make some code changes. The fact that you extended a class meant you made code changes. Did they just create this ambiguous rule so they can make the word SOLID instead of just SLID?

It wasn’t until later that I realized how to look at this from the correct angle (I think).

Now, since I specifically named our class as TaxPayer, it definitely needs to pay taxes. Do you know how to pay taxes? I myself only have a vague notion of how to do it. My experience is that it generally revolves around the efforts of a tax collector. It’s not necessarily the BIR, or the IRS, or the Finanzamt, or whatever institution your country has. It’s that someone who knows what to do. For example in my previous experience, someone from the company asks how much you are earning, and then provides an appropriate form for you to fill out and submit. Based on the information on that form, your tax is calculated and paid.

Here’s an example. Let’s say we have a TaxCollector class who collects taxes from tax payers. Maybe by at the start of the project, there wasn’t any distinction between the kinds of people who pay taxes, so by default a TaxPayer pays 32% of his salary. Pseudocode incoming.

class TaxCollector {
    double CollectTax (TaxPayer[] suckers) {
        double taxCollection = 0;
        for each sucker in suckers {
            taxCollection = taxCollection + (sucker.Income * 0.32);
        } 
        return taxCollection;
    }
}

But then the requirements changed **key in dread music** – because of some pricks protesting in the streets, the government was forced to provide tax breaks for married people, thus reducing the total revenue for building mansions for politicians farm-to-market roads. This prompted you as the developer to extend the TaxPayer class, and to modify how the taxes are collected.

class TaxCollector {
    double CollectTax (TaxPayer[] suckers) {
        double taxCollection = 0;
        for each sucker in suckers {
            if (sucker is SingleTaxPayer)
                taxCollection = taxCollection +
                                (sucker.Income * 0.32);
            else if (sucker is MarriedTaxPayer)
                taxCollection = taxCollection +
                                (sucker.Income * 0.25);
        } 
        return taxCollection;
    }
}

Ok. Now what would happen if the government decided to entice foreign investors by offering an even lower tax rate for their employees if they built regional operating headquarters (ROHQ) in the country? Well you’d subclass TaxPayer again to have ROHQTaxPayer type, and update your code once more.

class TaxCollector {
    double CollectTax (TaxPayer[] suckers) {
        double taxCollection = 0;
        for each sucker in suckers {
            if (sucker is SingleTaxPayer)
                taxCollection = taxCollection +
                                (sucker.Income * 0.32);
            else if (sucker is MarriedTaxPayer)
                taxCollection = taxCollection +
                                (sucker.Income * 0.25);
            else if (sucker is ROHQTaxPayer)
                taxCollection = taxCollection +
                                (sucker.Income * 0.15);
        } 
        return taxCollection;
    }
}

What if your country then decides to adopt a tax scheme similar to the one Germany is implementing, with six different TaxPayer subclasses, none of which fit your original definitions, and none of which share the same tax rates you originally applied? Well, you’d need to create those subtypes, and then update your tax collection method with the necessary if/else conditions each and every time you make a change.

Is there a better way, you ask? Yes of course.

In software development, as in governments and governance, there is often merit in giving power to the people and not keeping them in the dark. What if we changed our TaxPayer definition to this?

Not the proper way to mark classes and methods as abstract in UML

You can think of the word abstract to mean something like “not completely defined”. The TaxPayer class itself is not completely defined because it has a method, “PayTaxes” that has no code or implementation behind it. It knows that it needs to be able to pay taxes, but it does not know how. Thankfully, its children do.

It is up to the children now to define how they will pay their taxes. Here are some examples in pseudocode:

class SingleTaxPayer extends TaxPayer {
    double PayTaxes () {
        return income * 0.32;
    }
}
class MarriedTaxPayer extends TaxPayer {
    double PayTaxes () {
        return income * 0.25;
    }
}
class ROHQTaxPayer extends TaxPayer {
    double PayTaxes () {
        return income * 0.15;
    }
}

If the people know how to pay their taxes properly, how do you need to code the TaxCollector then?

class TaxCollector {
    double CollectTax (TaxPayer[] suckers) {
        double taxCollection = 0;
        for each sucker in suckers {
            taxCollection = taxCollection +
                            sucker.PayTaxes();
        } 
        return taxCollection;
    }
}

Simple. Easy. Does not discriminate between the types of tax payers. Does not need to change whenever a new tax rule or classification is introduced.

My epiphany with OCP breaks it down in two. When looking at extension – you look at the TaxPayer. You make an abstract class that defines an expected behavior but does not implement it. You make it open for extension even more explicitly by making it abstract because it’s children have no choice but to extend and implement it. In other words, the TaxPayer by itself will not have any purpose if it did not have any children.

By doing so, you make other parts of your code – in this case the TaxCollector – closed for modification. These are the parts of your code that consume your abstract class. This results in less things to test, and less things to worry about when you introduce changes.

Knowing OCP and the rest of the SOLID principles are good in theory, but how can you really put them into practice? I mean, you don’t know what new requirements and changes tomorrow will bring. You can’t really plan for everything, design everything, right at the start of the project. Changes will come and you’ll just do whatever is necessary to adapt.

Well, not really. You can plan and design just what you need when you need it, but when new requirements come, you should review what you’ve done and change it for the better. That’s refactoring. Pro Tip: include refactoring effort in your task estimates. A lot of developers don’t refactor and just opt for quick fixes just because they feel they don’t have the time to do it properly. Knowing these principles would allow you to more easily fix what you may have broken. More importantly, knowing their benefits would allow you to defend the effort for refactoring from your pesky customers.

Did this boring, technical post bum you out? Maybe try a lighter topic – like bachelor parties.


Fake it’til You Make It – SOLID OOP

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s