Aaron Proctor

The incoherent braindump of a software developer

Dependency Inversion Principle

This is part 5 of a series of blog posts on the SOLID principles – five fundamental principles of object-oriented software design.


The Dependency Inversion Principle states that:

A. High level modules should not depend upon low level modules. Both should depend upon abstractions.

B. Abstractions should not depend upon details. Details should depend upon abstractions.

The two parts to the dependency inversion principle might seem a little obtuse but understanding this principle is the key to writing decoupled code.

Dependency inversion is not just about working with abstractions rather than implementations; this should be a given already. The principle specifically concerns the ownership of these abstractions.

In a traditional, procedurally-written application, high level code tends to depend on low level implementations. For example, suppose we add code to render our game board to the screen. The physical rendering is encapsulated in the Viewport class, and a method added to our Gameboard object:

Clearly this violates SRP at the very least, so as we’ve already seen this should be factored out into a separate class:

We have solved some issues, but we still depend on the Viewport class – and create this dependency ourselves. As we’ve seen, this sort of dependency hierarchy can lead to brittle code.

We should already be comfortable enough to work with interfaces to come up with a reasonable abstraction.

In isolation that’s fine; indeed what we’ve done here is dependency injection, which is a result of (but not the same as) dependency inversion. However, what happens when the rendering process needs to change? A developer working on that might consider that the IViewport interface needs to change, since the change is relating to the viewport.

The problem with changing the interface, of course, is that everything which depends on it needs to change. This means we need to change the important, high-level code (in this case Renderer) just because a lower-level detailed implementation (the Viewport) needs to change.

This concept is the key to understanding the Dependency Inversion Principle – the IViewport abstraction is not owned by the viewport, it’s owned by the renderer. It is the rendering process that should determine how it needs to talk to the viewport, rather than the viewport dictating to the renderer how it needs to be called.

In our case the abstraction – the underlying truth of the process which does not change – is that when the game wishes to render the gameboard in the current frame, it needs to determine what needs to be rendered, and give that information to the viewport. This is a high-level policy, rather than an implementation detail, and the driver of that high level policy (the Renderer class) should not change because some detail changes (the Viewport class).

Interface Segregation Principle

This is part 4 of a series of blog posts on the SOLID principles – five fundamental principles of object-oriented software design.


The Interface Segregation Principle

Clients should not be forced to depend upon interfaces that they do not use.

The Interface Segregation Principle is all about making your interfaces smaller and more focused. There’s little point in exposing large numbers of methods in a system to all clients – indeed, doing so can lead to a tightly coupled system which is difficult to change and test.

Consider the dependency which the Player class has on the IGameBoard interface. Is this really necessary? The only GameBoard related services needed by the player is the MovePlayer method – is there any need to give the Player object access to the size of the gameboard? Any time you give a client access to more services than it needs, you make it more difficult to modify and increase dependencies.

We can factor out the MovePlayer functionality from the IGameBoard interface and into a new, more specific interface:

And have the GameBoard class implement our new interface:

Now the Player class no longer depends on the entire GameBoard interface and there’s no danger of other developers breaking the Player class by seemingly unrelated changes to the GameBoard.

Similarly, any other clients of the GameBoard class can be treated the same way. Now we no longer have a monolithic “God Interface” to work with the game board system. Just like with classes, large and omnipotent interfaces are harder to maintain.

Liskov Substitution Principle

This is part 3 of a series of blog posts on the SOLID principles – five fundamental principles of object-oriented software design.


The Liskov Substitution Principle

Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

Following the Open-Closed principle invariably results in inheritance hierarchies. While this is not automatically a problem, ill-considered and frivolous use of inheritance can cause more problems than it solves.

The Liskov Substitution Principle can help us think about how to build inheritance trees whilst maintaining a consistent object model and adhering to the Open/Closed principle.

The classic and oft-touted example of an LSP violation is the Rectangle class:

Let’s imagine that due to some additional requirements, we need to be able to work with squares as well as rectangles. We know that in Object Oriented design, an inheritance signifies an “is a” relationship – and clearly a square “is a” rectangle, mathematically.

This might seem appropriate but it quickly starts to break down. It doesn’t make sense for us to be able to set the width and height of the square separately, yet in this case we can. Of course we can add logic to the properties in the Square class:

But what happens when we pass a Square into this client function?

We’re left with a square with different-length sides. Instead of shadowing the properties, we could declare them virtual and override them (leaving aside that this means changing the Rectangle class, violating OCP).

This would prevent this issue but is not really addressing the problem: the contract of the rectangle class no longer applies to a square. Setting the height of a rectangle should not affect the width, and if a developer has written code which depends on these conditions it will not function properly with a Square. This is a violation of the Open/Closed Principle – functions which worked before the changes no longer work without modification.

The problem here is in the initial assumption. A Square object is not a Rectangle object, even though in the real world we know that a square is a rectangle. The data model is the same – a rectangle and a square both have a height and a width – but the behaviour of a rectangle does not apply to a square. This is the fundamental lesson of LSP – a derivative must conform to the behaviour that clients expect of the base classes they use.

How does this apply to the real world? Let’s take another look at our game. A developer implements a new type of enemy: the ranged enemy. This can attack our player at range, and will do so if the player moves within range. We could implement this like so:

Suppose there’s another, seemingly unrelated requirement to implement a two-dimensional game board rather than the single row affair we’ve used so far:

Following the Open/Closed principle, we can extend our existing GameBoard class to be 2D, and simply track an additional axis of movement. At first glance this appears to do the trick, other than maybe the CurrentLocation variable now is oddly named.

Plug this new gameboard into our ranged enemy, however, and we might get unexpected behaviour. CurrentLocation is not enough to work out where the player is and whether he’s moved within range: what if he moves to the same row, but 6 columns away?

This is the sort of problem that might not be immediately obvious until further down the line when the mistake is more costly. The clue should have been the odd name of that property: if it is no longer descriptive, it no longer represents the same aspect as in the parent class, and that’s a classic indicator of a Liskov violation.

Open/Closed Principle

This is part 2 of a series of blog posts on the SOLID principles – five fundamental principles of object-oriented software design.


The Open/Closed Principle

The Open/Closed Principle states:

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

This suggests that a class should facilitate new functionality by extension rather than modification. It has obvious advantages when it comes to changing requirements – by leaving existing functionality untouched, it reduces the likelihood of new development causing ripple effects.

But how do we design our object model to achieve this?

Let’s suppose we add a new type of Enemy to the game, the Boss, which is more difficult to kill. We could do this with an Enum:

And then modify our Enemy to have a hit points property and PlayerAttack method to utilise the new type:

Not only have we made the code more complex and error-prone, but we’ve also modified the Enemy class and PlayerAttack class despite those concepts not having changed in the requirements.

Instead, let’s try extending the Enemy class to create a new type, Boss:

Now we have encapsulated the new requirements for a Boss without touching the Enemy class. Using inheritance or abstractions in place of enums is good practice – certainly any time you’re using a ‘switch’ or ‘if’ statement with an enum it should be treated with suspicion.

Single Responsibility Principle

This is part 1 of a series of blog posts on the SOLID principles – five fundamental principles of object-oriented software design.


Single Responsibility Principle

The Single Responsibility Principle states:

Every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class.

Self-explanatory, but how can you tell how many responsibilities a class has? Well Robert Martin, author of the SOLID principles, defines a responsibility as a reason to change. In other words, there should only ever be one reason for a given class to change.

This principle ensures that all classes are smaller, cohesive and tightly focused on providing one single service. This makes it easier to modify the software as any given change is tied to one single class.

Consider the following example showing a Player class in a simple game:

This class has multiple reasons to change. What if the requirements change so that killing an enemy is worth more than one point? What if the critical health threshold of 25 needs to change? What if the method of outputting the health warning changes? What if movement changes?

If any one of these requirements changes, then the entire Player functionality and any of its dependencies will be affected. This code should be refactored so that it has a more clearly defined purpose.

Let’s strip out the Health property and replace it with a class, HealthCounter, that’s responsible for detecting the critical health threshold:

Now this only needs to change if the IsCritical functionality changes.

Displaying the health warning should also be distinct from the Player class, so we can extract a class for this functionality too:

Now if we want to do something more complex with the health alert than just writing to the console, we don’t need to affect unrelated functionality.

Another issue with this class is that it’s responsible for handling player movement and tracking its position. Let’s delegate this responsibility to a specific GameBoard class. That way, the Move method doesn’t need to worry about these intricacies.

The GameBoard class tracks the location of objects in the game world:

Now the Player class is no longer knows the detail of how to move around the board:

Finally, the attacking logic is encapsulated in the player object when it really should be separate. Let’s delegate this to a PlayerAttack class:

And the attack method on the Player object becomes simpler:

Now the PlayerAttack class knows how to perform the attack (which entails retrieving the enemy on the next tile and calling the TakeDamage() method) and so this logic is encapsulated.

Note also here that we’re passing in a way to track the score rather than using control statements in the Playe.Attack() method. This approach is useful to delegate control of the flow of execution (“if (!enemy.Kill())”).

This way, PlayerAttack now contains both the condition (enemy was killed) and statement to execute if the condition holds (increase the score) which encompasses a single responsibility.

Now our final Player class is a higher-level class with a defined responsibility, which is to delegate control of Player aspects to the more specialised classes. However, we are still violating other SOLID principles.

S.O.L.I.D: Five Principles of Good Software Design

The SOLID acronym encompasses five fundamental principles of object-oriented design. Penned by agile and software craftmanship pioneer Robert Martin, the aim of SOLID is to provide some basic guidelines for writing robust, flexible and extensible code.

Although these guidelines are exactly that: guidelines, and so shouldn’t be treated as laws, considering each of the SOLID principles when writing or refactoring code is a good basis for writing software that is easy to maintain.

A brief overview of the five SOLID principles follows. Over the next week I’ll be covering each principle in detail.

Single Responsibility Principle

A class should have one and only one responsibility. More specifically, any class should have only one reason to change.

This is important because it reduces the likelihood that a given class will need to change, and changes introduce bugs and complexity.

Open/Closed Principle

Software entities should be open for extension but closed for modification. This means an entity can be extended without being modified. Code adhering to this principle will only need to change if there are bugs.

This reduces the risk of new errors being introduced by changes to the code base, and limits the extra unit testing required for new functionality.

Liskov Substitution Principle

This states that any client of a class must be able to use a subclass of that class without modification. In other words, if some code uses a base class, you should be able to substitute that class with a subclass without your code knowing about it.

This can be a little difficult to envisage due to it being largely semantic rather than purely syntactic, but careful application of this rule can reduce the complexity of code which relies on inheritance.

Interface Segregation Principle

Many client-specific interfaces are better than one general-purpose interface; that is, no client should depend on methods it does not use.

Larger classes which implement multiple smaller interfaces are more loosely coupled with their clients, resulting in code which is more robust and resilient to change.

Dependency Inversion Principle

Depend on abstractions, not concretions. This means that not only should developers code against interfaces (or abstract classes) rather than concrete implementations, but they should also provide those abstractions at both high- and low-level code.

Following DIP means code is more testable and maintainable.