Solid OOP design part III

Welcome to Part III. In this lesson, I will cover a few concepts related to The Liskov Substitution Principle. I will refer to this as Liskov to be short. What this principle means is that Objects in your code should be replaceable with instances of their subtypes without altering the correctness of that program.

Clear as mud?

Let's look at what this means in terms of the project we've been working on in the previous parts of this series. You can find the link to the code on: https://github.com/cegrif01/menu_maker/tree/part-three-anti-pattern

This should be what your MenuMaker.php class should look like at this point:

MenuMaker.php at the start of part 3

Before we move on, take a moment to reflect. Look at the code snippet above. If you fully understand the lesson so far, you are well on your way to learning how to program a very complex application in any language. Yes, any language. Once you understand these principles, you can apply them to any object oriented programming language.

Remember how bad the code was when we started:

MenuMaker.php when we began

Pretty impressive progress if you ask me. These changes may not seem like a big deal, but keep in mind the SOLID principles become more useful as your application grows.

Feature:

In the previous lesson, I brought up a few concerns that may arise if we want to add even more types of exporters. Not that we are going to add more, but we want to keep our application open. The feature for this lesson is to make this possible using the Liskov Substitution Principle.

Liskov and Interfaces:

The first thing I want to do is tidy up our application. I moved all the classes that are involved in Exporting to a folder called Exporters.

Updated directory structure:

updated directory structure

We also have to update the require statement in MenuMaker.php so that we point to the ExporterFactory class.

MenuMaker.php updated require statement

There's no set rule for how to structure your application. I think that common sense is the winner on this one. If you have a bunch of files that deal with Exporting, I think it's a good idea to group them together so that if you look this code a year later, you'll be able to relearn the structure faster.

Now let's play around with interfaces.

What is an interface?

“Object interfaces allow you to create code which specifies which methods a class must implement, without having to define how these methods are handled.” ~ http://www.php.net/manual/en/language.oop5.interfaces.php

Interfaces are one of those topics that is easier to understand by practical example than by definition. Basically we would like to have a type called Exportable. Any class that implements Exportable will be FORCED to implement exportable behavior.

Notice the emphasis on the word “FORCED”. If we create an Exportable interface and allow both the Html Exporter and the Csv Exporter implement exportable behavior, then we must force both of these classes to implement this behavior. Let's take a look at what I mean.

First we need to create the interface so that our Csv and Html exporters can implement this behavior.

Exportable.php
Exportable Interface

Then we will make our CsvExporter implement the Exportable interface.

CsvExporter.php (the hard coded file location value should definitely be held in a config file or passed in as a parameter)
CsvExporter.php

HtmlExporter.php (Be patient, we're going to get some logic in here soon)

HtmlExporter.php

On a side note:
I have to be honest with you, if our application had no chance of future expansion, I would tell you not to bother with an interface. This is one of those dogma verses common sense issues. If you are developing a mid to large sized application with 10+ developers, the benefits of using interfaces really shine. If you are knocking out a quick app for your friends blog, don't feel like you have to apply everyone of the SOLID principles. For the sake of this lesson, just pretend this app is going to be huge!

What the interfaces do is guarantee that each Exportable type has the export() method on it. Going through the effort of doing this, allows us to add more exportable types besides the one we have. We can have a PdfExporter that implements the same interface but has the behavior of rendering the output to a pdf file.

How NOT to use interfaces
The structure of this lesson is slightly different. I showed you how to use interfaces without showing you the anti-pattern. I thought it would make more sense to show how not to use interfaces after showing you how to use them. Let's now look how not to use interfaces.

Liskov Anti-pattern
The benefits of Liskov shine whenever we try do something crazy. Let's say that another developer years later decides to add a parameter to the export method, but only for HtmlExporter.

adding parameter to HtmlExporter

Before, we implemented the interface, this was perfectly okay. However, It would have really caused problems in the polymorphic array that we used in the MenuMaker.php file.

failed polymorphic array because of liskov violation

What do you think would happen if we tried to do this?

You guessed it, stuff breaks:

non-matching interface error

Violating Liskov does not always result in an error. In php there's no concept of strong return types like there is in languages such as Java and C#. While adding an additional parameter to a subtype that's not in the parent implementation will cause a definite error, returning different values may or may notr cause a problem. Keep this in mind when thinking about Liskov.

In the next part of this lesson we will look at the Integration Segregation Principle.