An Interest In:
Web News this Week
- March 27, 2024
- March 26, 2024
- March 25, 2024
- March 24, 2024
- March 23, 2024
- March 22, 2024
- March 21, 2024
How to Write Code That Embraces Change
Writing code, which is easy to change is the Holy Grail of programming. Welcome to programming nirvana! But things are much more difficult in reality: source code is difficult to understand, dependencies point in countless directions, coupling is annoying, and you soon feel the heat of programming hell. In this tutorial, we will discuss a few principles, techniques and ideas that will help you write code that is easy to change.
Some Object-Oriented Concepts
Object-oriented programming (OOP) became popular, due to its promise of code organization and reuse; it utterly failed in this endeavor. We’ve been using OOP concepts for many years now, yet we continue to repeatedly implement the same logic in our projects. OOP introduced a set of good basic principles which, if properly used, can lead to better, cleaner code.
Cohesion
The things that belong together should be kept together; otherwise, they should be moved elsewhere. This is what the term, cohesion, refers to. The best example of cohesion can be demonstrated with a class:
class ANOTCohesiveClass {private $firstNumber;private $secondNumber;private $length;private $width;function __construct($firstNumber, $secondNumber) {$this->firstNumber = $firstNumber;$this->secondNumber = $secondNumber;}function setLength($length) {$this->length = $length;}function setHeight($height) {$this->width = $height;}function add() {return $this->firstNumber + $this->secondNumber;}function subtract() {return $this->firstNumber - $this->secondNumber;}function area() {return $this->length * $this->width;}}
This example defines a class with fields that represent numbers and sizes. These properties, judged only by their names, do not belong together. We then have two methods, add()
and substract()
, which operate on only the two number variables. We further have an area()
method, that operates on the length
and width
fields.
It’s obvious that this class is responsible for separate groups of information. It has very low cohesion. Let’s refactor it.
class ACohesiveClass {private $firstNumber;private $secondNumber;function __construct($firstNumber, $secondNumber) {$this->firstNumber = $firstNumber;$this->secondNumber = $secondNumber;}function add() {return $this->firstNumber + $this->secondNumber;}function subtract() {return $this->firstNumber - $this->secondNumber;}}
This is a highly cohesive class. Why? Because every section of this class belongs with one another. You should strive for cohesion, but beware, it’s can be difficult to achieve.
Orthogonality
In simple terms, orthogonality refers to the isolation or elimination of side effects. A method, class or module that changes the state of other unrelated classes or modules is not orthogonal. For example, an airplane’s black box is orthogonal. It has its inner functionality, inner power source, microphones and sensors. It has no effect on the airplane it resides in, or in the exterior world. It only provides a mechanism to record and retrieve flight data.
An example of one such non-orthogonal system is your car’s electronics. Increasing your vehicle’s speed has several side-effects, such as increasing radio volume (among other things). Speed is not orthogonal to the car.
class Calculator {private $firstNumber;private $secondNumber;function __construct($firstNumber, $secondNumber) {$this->firstNumber = $firstNumber;$this->secondNumber = $secondNumber;}function add() {$sum = $this->firstNumber + $this->secondNumber;if ($sum > 100) {(new AlertMechanism())->tooBigNumber($sum);}return $sum;}function subtract() {return $this->firstNumber - $this->secondNumber;}}class AlertMechanism {function tooBigNumber($number) {echo $number . 'is too big!';}}
In this example, the Calculator
class’s add()
method exhibits unexpected behavior: it creates an AlertMechanism
object and calls one of its methods. This is unexpected and unwanted behavior; library consumers will never expect a message printed to the screen. Instead, they only expect the sum of the provided numbers.
class Calculator {private $firstNumber;private $secondNumber;function __construct($firstNumber, $secondNumber) {$this->firstNumber = $firstNumber;$this->secondNumber = $secondNumber;}function add() {return $this->firstNumber + $this->secondNumber;}function subtract() {return $this->firstNumber - $this->secondNumber;}}class AlertMechanism {function checkLimits($firstNumber, $secondNumber) {$sum = (new Calculator($firstNumber, $secondNumber))->add();if ($sum > 100) {$this->tooBigNumber($sum);}}function tooBigNumber($number) {echo $number . 'is too big!';}}
This is better. AlertMechanism
has no effect on Calculator
. Instead, AlertMechanism
uses whatever it needs in order to determine if an alert should be issued.
Dependency and Coupling
In most cases, these two words are interchangeable; but, in some cases, one term is preferred over another.
So what is a dependency? When object A
needs to use object B
, in order to perform its prescribed behavior, we say that A
depends on B
. In OOP, dependencies are extremely common. Objects frequently work with and depend on one another. So, while eliminating dependency is a noble pursuit, it is nearly impossible to do so. Controlling dependencies and reducing them is, however, preferable.
The terms, heavy-coupling and loose-coupling, usually refer to how much an object depends on other objects.
In a loosely-coupled system, changes in one object have a reduced effect on the other objects which depend on it. In such systems, classes depends on interfaces instead of concrete implementations (we will talk more about that later). This is why loosely-coupled systems are more open to modifications.
Coupling in a Field
Let’s consider an example:
class Display {private $calculator;function __construct() {$this->calculator = new Calculator(1,2);}}
It’s common to see this type of code. A class, Display
in this case, depends upon the Calculator
class by directly referencing that class. In the above code, Display
‘s $calculator
field is of type Calculator
. The object that field contains is a result of directly calling Calculator
‘s constructor.
Coupling by Accessing the Other Class Methods
Review the following code for a demonstration of this kind of coupling:
class Display {private $calculator;function __construct() {$this->calculator = new Calculator(1, 2);}function printSum() {echo $this->calculator->add();}}
The Display
class calls the Calculator
object’s add()
method. This is another form of coupling, because one class accesses the other’s method.
Coupling by Method Reference
You can couple classes with method references, too. For example:
class Display {private $calculator;function __construct() {$this->calculator = $this->makeCalculator();}function printSum() {echo $this->calculator->add();}function makeCalculator() {return new Calculator(1, 2);}}
It’s important to note that the makeCalculator()
method returns a Calculator
object. This is a dependency.
Coupling by Polymorphism
Inheritance is probably the strongest form of dependency:
class AdvancedCalculator extends Calculator {function sinus($value) {return sin($value);}}
Not only can AdvancedCalculator
not do its job without Calculator
, but it couldn’t even exist without it.
Reducing Coupling by Dependency Injection
One can reduce coupling by injecting a dependency. Here’s one such example:
class Display {private $calculator;function __construct(Calculator $calculator = null) {$this->calculator = $calculator ? : $this->makeCalculator();}// ... //}
By injecting the Calculator
object through Display
‘s constructor, we reduced Display
‘s dependency upon the Calculator
class. But this is only half of the solution.
Reducing Coupling with Interfaces
We can further reduce coupling by using interfaces. For example:
interface CanCompute {function add();function subtract();}class Calculator implements CanCompute {private $firstNumber;private $secondNumber;function __construct($firstNumber, $secondNumber) {$this->firstNumber = $firstNumber;$this->secondNumber = $secondNumber;}function add() {return $this->firstNumber + $this->secondNumber;}function subtract() {return $this->firstNumber - $this->secondNumber;}}class Display {private $calculator;function __construct(CanCompute $calculator = null) {$this->calculator = $calculator ? : $this->makeCalculator();}function printSum() {echo $this->calculator->add();}function makeCalculator() {return new Calculator(1, 2);}}
You can think of ISP as a higher-level cohesion principle.
This code introduces the CanCompute
interface. An interface is as abstract as you can get in OOP; it defines the members that a class must implement. In the case of the above example, Calculator
implements the CanCompute
interface.
Display
‘s constructor expects an object that implements CanCompute
. At this point, Display
‘s dependency with Calculator
is effectively broken. At any time, we can create another class that implements CanCompute
and pass an object of that class to Display
‘s constructor. Display
now depends only on the CanCompute
interface, but even that dependency is optional. If we pass no arguments to Display
‘s constructor, it will simply create a classic Calculator
object by calling makeCalculator()
. This technique is frequently used, and is extremely helpful for test-driven development (TDD).
The SOLID Principles
SOLID is a set of principles for writing clean code, which then makes it easier to change, maintain and extend in the future. They are recommendations that, when applied to source code, have a positive effect on maintainability.
A Little History
The SOLID principles, also known as Agile principles, were initially defined by Robert C. Martin. Even though he did not invent all of these principles, he was the one who put them together. You can read more about them in his book: Agile Software Development, Principles, Patterns, and Practices. SOLID’s principles cover a wide array of topics, but I will present them in as simple a way as I’m capable. Feel free to ask for additional details in the comments, if needed.
Single Responsibility Principle (SRP)
A class has a single responsibility. This may sound simple, but it can sometimes be difficult to understand and put into practice.
class Reporter {function generateIncomeReports();function generatePaymentsReports();function computeBalance();function printReport();}
Who do you think benefits from this class’ behavior? Well, an accounting department is an option (for the balance), the finance department may be another (for income/payment reports), and even the archiving department could print and archive the reports.
There are four reasons why you might have to change this class; each department may want their respective methods customized for their needs.
The SRP recommends breaking such classes into smaller, beahvior-specific classes, each having just one reason to change. Such classes tend to be highly cohesive and loosely coupled. In a sense, SRP is cohesion defined from the point of view of the users.
Open-Closed Principle (OCP)
Classes (and modules) should welcome the extension of their functionality, as well as resist modifications of their current functionality. Let’s play with the classic example of an electric fan. You have a switch and you want to control the fan. So, you could write something along the lines of:
class Switch_ {private $fan;function __construct() {$this->fan = new Fan();}function turnOn() {$this->fan->on();}function turnOff() {$this->fan->off();}}
Inheritance is probably the strongest form of dependency.
This code defines a Switch_
class that creates and controls a Fan
object. Please note the underscore after “Switch_”. PHP doesn’t allow you to define a class with the name “Switch.”
Your boss decides that he wants to control the light with the same switch. This is a problem, because you have to change Switch_
.
Any modifications to existing code is a risk; other parts of the system may be affected and require even further modification. It is always preferable to leave existing functionality alone, when adding new functionlaity.
In OOP terminology, you can see that Switch_
has a strong dependency on Fan
. This is where our problem lies, and where we should make our changes.
interface Switchable {function on();function off();}class Fan implements Switchable {public function on() {// code to start the fan}public function off() {// code to stop the fan}}class Switch_ {private $switchable;function __construct(Switchable $switchable) {$this->switchable = $switchable;}function turnOn() {$this->switchable->on();}function turnOff() {$this->switchable->off();}}
This solution introduces the Switchable
interface. It defines the methods that all switch-enabled objects need to implement. The Fan
implements Switchable
, and Switch_
accepts a reference to a Switchable
object within its constructor.
How does this help us?
First, this solution breaks the dependency between Switch_
and Fan
. Switch_
has no idea that it starts a fan, nor does it care to. Second, introducing a Light
class will not affect Switch_
or Switchable
. Do you want to control a Light
object with your Switch_
class? Simply create a Light
object and pass it to Switch_
, like this:
class Light implements Switchable {public function on() {// code to turn ligh on}public function off() {// code to turn light off}}class SomeWhereInYourCode {function controlLight() {$light = new Light();$switch = new Switch_($light);$switch->turnOn();$switch->turnOff();}}
Liskov Substitution Principle (LSP)
LSP states that a child class should never break the functionality of the parent class. This is extremely important because consumers of a parent class expect the class to behave in a certain way. Passing a child class to a consumer should just work and not affect the original functionality.
This is confusing at first glance, so let’s look at another classic example:
class Rectangle {private $width;private $height;function setWidth($width) {$this->width = $width;}function setHeigth($heigth) {$this->height = $heigth;}function area() {return $this->width * $this->height;}}
This example defines a simple Rectangle
class. We can set its height and width, and its area()
method provides the rectangle’s area. Using the Rectangle
class could look like the following:
class Geometry {function rectArea(Rectangle $rectangle) {$rectangle->setWidth(10);$rectangle->setHeigth(5);return $rectangle->area();}}
The rectArea()
method accepts a Rectangle
object as an argument, sets its height and width, and returns the shape’s area.
In school, we’re taught that squares are rectangles. This hints that if we model our program to our geometrical object, a Square
class should extend a Rectangle
class. How would such a class look like?
class Square extends Rectangle {// What code to write here?}
I have a hard time figuring out what to write in the Square
class. We have several options. We could override the area()
method and return the square of $width
:
class Rectangle {protected $width;protected $height;// ... //}class Square extends Rectangle {function area() {return $this->width ^ 2;}}
Note that I changed Rectangle
‘s fields to protected
, giving Square
access to those fields. This looks reasonable from a geometrical point-of-view. A square has equal sides; returning the square of width is reasonable.
However, we have a problem from a programming point-of-view. If Square
is a Rectangle
, we should have no problem feeding it into the Geometry
class. But, by doing so, you can see that Geometry
‘s code doesn’t make much sense; it sets two different values for height and width. This is why a square is not a rectangle in programming. LSP violated.
Interface Segregation Principle (ISP)
Unit tests should run fast – very fast.
This principle concentrates on breaking large interfaces into small, specialized interfaces. The basic idea is that different consumers of the same class should not know about the different interfaces – only the interfaces the consumer needs to use. Even if a consumer does not directly use all the public methods on an object, it still depends on all the methods. So why not provide interfaces with that only declare the methods that each user needs?
This is in close accordance that interfaces should belong to the clients and not to the implementation. If you tailor your interfaces to the consuming classes, they will respect ISP. The implementation itself can be unique, as a class can implement several interfaces.
Let’s imagine that we implement a stock market application. We have a broker that buys and sells stocks, and it can report its daily earnings and losses. A very simple implementation would include something like a Broker
interface, a NYSEBroker
class that implements Broker
and a couple of user interface classes: one for creating transactions (TransactionsUI
) and one for reporting (DailyReporter
). The code for such a system could be similar to the following:
interface Broker {function buy($symbol, $volume);function sell($symbol, $volume);function dailyLoss($date);function dailyEarnings($date);}class NYSEBroker implements Broker {public function buy($symbol, $volume) {// implementsation goes here}public function currentBalance() {// implementsation goes here}public function dailyEarnings($date) {// implementsation goes here}public function dailyLoss($date) {// implementsation goes here}public function sell($symbol, $volume) {// implementsation goes here}}class TransactionsUI {private $broker;function __construct(Broker $broker) {$this->broker = $broker;}function buyStocks() {// UI logic here to obtain information from a form into $data$this->broker->buy($data['sybmol'], $data['volume']);}function sellStocks() {// UI logic here to obtain information from a form into $data$this->broker->sell($data['sybmol'], $data['volume']);}}class DailyReporter {private $broker;function __construct(Broker $broker) {$this->broker = $broker;}function currentBalance() {echo 'Current balace for today ' . date(time()) . "\n";echo 'Earnings: ' . $this->broker->dailyEarnings(time()) . "\n";echo 'Losses: ' . $this->broker->dailyLoss(time()) . "\n";}}
While this code may work, it violates the ISP. Both DailyReporter
and TransactionUI
depend on the Broker
interface. However, they each only use a fraction of the interface. TransactionUI
uses the buy()
and sell()
methods, while DailyReporter
uses the dailyEarnings()
and dailyLoss()
methods.
You may argue that
Broker
is not cohesive because it has methods that are unrelated, and thus don’t belong together.
This may be true, but the answer depends on the implementations of Broker
; selling and buying may be strongly related to current losses and earnings. For example, you may not be allowed to buy stocks if you are losing money.
You may also argue that Broker
also violates SRP. Because we have two classes that use it in different ways, there may be two different users. Well, I say no. The only user is probably the actual broker. He/she wants to buy, sell, and view their current funds. But again, the actual answer depends on the whole system and business.
ISP is surely violated. Both UI classes depend on the whole Broker
. This is a common problem, if you think interfaces belong to their implementations. However, shifting your point-of-view can suggest the following design:
interface BrokerTransactions {function buy($symbol, $volume);function sell($symbol, $volume);}interface BrokerStatistics {function dailyLoss($date);function dailyEarnings($date);}class NYSEBroker implements BrokerTransactions, BrokerStatistics {public function buy($symbol, $volume) {// implementsation goes here}public function currentBalance() {// implementsation goes here}public function dailyEarnings($date) {// implementsation goes here}public function dailyLoss($date) {// implementsation goes here}public function sell($symbol, $volume) {// implementsation goes here}}class TransactionsUI {private $broker;function __construct(BrokerTransactions $broker) {$this->broker = $broker;}function buyStocks() {// UI logic here to obtain information from a form into $data$this->broker->buy($data['sybmol'], $data['volume']);}function sellStocks() {// UI logic here to obtain information from a form into $data$this->broker->sell($data['sybmol'], $data['volume']);}}class DailyReporter {private $broker;function __construct(BrokerStatistics $broker) {$this->broker = $broker;}function currentBalance() {echo 'Current balace for today ' . date(time()) . "\n";echo 'Earnings: ' . $this->broker->dailyEarnings(time()) . "\n";echo 'Losses: ' . $this->broker->dailyLoss(time()) . "\n";}}
This actually makes sense and respects the ISP. DailyReporter
depends only on BrokerStatistics
; it doesn’t care and does not have to know about any selling and buying operations. TransactionsUI
, on the other hand, knows only about buying and selling. The NYSEBroker
is identical to our previous class, except it now implements the BrokerTransactions
and BrokerStatistics
interfaces.
You can think of ISP as a higher-level cohesion principle.
When both UI classes depended on the Broker
interface, they were similar to two classes, each having four fields, of which two were used in a method and the other two in another method. The class would have not been very cohesive.
A more complex example of this principle can be found in one of Robert C. Martin’s first papers on the subject: The Interface Segregation Principle.
Dependency Inversion Principle (DIP)
This principle states that high-level modules should not depend on low-level modules; both should depend on abstractions. Abstractions should not depend upon details; details should depend upon abstractions. Put simply, you should depend on abstractions as much as possible and never on concrete implementations.
The trick with DIP is that you want to reverse the dependency, but always want to keep the flow of control. Let’s review our example from the OCP (the Switch
and Light
classes). In the original implementation, we had a switch directly controlling a light.
As you can see, both dependency and control flow from Switch
toward Light
. While this is what we want, we do not want to directly depend on Light
. So we introduced an interface.
It’s amazing how simply introducing an interface makes our code respect both DIP and OCP. As you can see no, class depends on the concrete implementation of Light
, and both Light
and Switch
depend on the Switchable
interface. We inverted the dependency, and the flow of control was unchanged.
High Level Design
Another important aspect of your code is your high-level design and general architecture. An entangled architecture produces code that is hard to modify. Keeping a clean architecture is essential, and the first step is understanding how to separate your code’s different concerns.