Posts tagged "oop"

Dependency Inversion Principle (SOLID Part 5)

Part of my SOLID programming principles series.

What is the Dependency Inversion Principle?

The Dependency Inversion Principle can be summed up in one simple quote “design for the interface, not the implementation”. The idea is that software is built by making simple objects and functions, which are then used by more complex objects and functions. This principle is a way to make it so that higher-level components are not dependent on any particular lower level component, thereby making it easier for parts of a program to be extended or switched out.

Benefits

The main benefit of this is another level of modularity in your programs. once an object is no longer dependent on another, either of the two objects can be replaced.

Applying the Dependency Inversion Principle

This is an example from 2 projects I recently had for my data structures class at the University of Georgia. The first project involved parsing a file of text, and providing a word histogram, which would display a list of words from the file, in alphabetical order, along with the number of times that word showed up. These words would need to be stored in a binary search tree. Here is a simplified, psuedocode example of the project:

class BinarySearchTree {
    public getHistogram() {
        //returns histogram
    }
}

class HistogramGUI { private BinarySearchTree wordTree;

  public getHistogram() {
       return this.wordTree.getHistogram();
  }

}

my next project was to again generate a Histogram from a text file, but this time using a 2-3-4 tree. Now I could have re-used code from the previous project, but now I have to re-write the HistogramGUI class to use a different kind of tree. Who knows what my next project is going to be? maybe I will have the same problem yet again, but using some other kind of data structure. The HistogramGUI currently has a depenency on the other class of BinarySearchTree. If we add an interface for being able to generate histograms, I can remove this depenecy, and make the HistogramGUI class more re-usuable.

Here’s the new structure to support both projects:

interface IHistogram {
    public getHistogram();
}

class HistorgramGUI {
  private IHistogram histogramGenerator;

   public setHistogramGeneration(IHistogram generator) {
      this.histogramGenerator = generator;
   }

    public getHistogram() {
     this.histogramGenerator.getHistogram();
   }
} 

class binarySearchTree implements IHistogram {
    public getHistogram() {
        //returns histogram
    }
}

class 234Tree implements IHistogram {
    public getHistogram() {
        //returns histogram
    }
}

now you see that I can use the histogramGUI class with any class that implements IHistogram. If my next project called for the same problem with a B-Tree or a Red-Black tree or a simple array, it no longer matters to the histogramGUI class, as they are all now interchangeable. This gives you all the benefits of composing more complex objects from simpler ones, but allows you more flexibility. the histogramGUI class does not (and should not) care about how the data is generated.

Interface Segregation Principle (SOLID Part 4)

Part of my SOLID programming principles series.

What Is The Interface Segregation Principle?

The interface segregation principle states that interfaces should be broken up in a logical manner, and that complex objects should use multiple interfaces.

Benefits The benefits of this principle is that is allows people to use only the parts of objects that they need, and can prevent having to write dummy methods when implementing an interface. For a quick example, lets’ say you have a SwissArmyKnife class that has a Tool interface:

class SwissArmyKnife implements Tool{
    function cut();
    function useScrewdriver();
    function useFork(); 
}

interface Tool {
    function cut();
    function useScrewdriver();
    function useFork();
}

now if we wanted to build a simple screwdriver class, we would have to write two empty methods that don’t do anything. instead, we would probably want to break this is into multiple interfaces:

class SwissArmyKnife implements Knife,Screwdriver, Fork {
  //same as above
}

interface Knife {
  function cut();
} 

interface Screwdriver {
  function useScrewdriver();
}

interface Fork {
  function useFork();
}

now you can use the swiss army knife as any of these objects, and it can easily be replaced with an object that only does one of these jobs if that is what the code calls for.

Applying the Interface Segregation Principle

The Interface Segregation Principle is really useful for refactoring and breaking down larger objects. This way you can break down the interface first, with minimal changes to existing code. Then, the object can be replaced as needed.

Using Composition Instead of Inheritance

Favoring Composition over inheritance is a quote from the gang of four book about design patterns. Today I want to take a another look at the TeaPot class example I used when talking about the Open/Closed principle.

To review, here is how our object was structured in the previous post.

TeaPot Class

class TeaPot() {

  function TeaPot() {
     //constructor
  }

  function makeTea() {
      boilWater();
      addTeaLeaves();
      seepTea();
      removeTeaLeaves();
      addCondiments();
  }

  function boilWater() { //code to boil water }
  function addTeaLeaves() { //code to add tea leaves }
  function seepTea() { //code to seep tea }
  function removeTeaLeaves() {  //code to remove tea leaves }
  function addCondiments() { //code to add cream and sugar }
 }

In the previous post, we talked about what to do if people wanted honey instead of cream and sugar, so we extended the class and overrode the addCondiments() method in our HoneyTeaPot class. Now let’s say that we wanted to make some tea of a different flavor, so we want to add a different kind of leaf. We again could extend the class, create a EarlGreyTeaPot class which overrides the addTeaLeaves() function. Easy, right? Now, we have someone else come along, and they want earl grey tea with honey.

Now we have a problem.

We could create a EarlGreyWithHoneyTeaPot class, but then we would be duplicating code, since we already have the methods for this in two other classes. We need to abstract out these methods into classes of their own, so that we can re-use the code.

Let’s start with the tea leaves. we’ll create a new LeafAdder interface to define these classes, and a couple of classes that implement them.

Leaf Adding Classes

interface LeafAdder {
    function addTeaLeaves();
}

class RegularLeafAdder implements LeafAdder{
    function addTeaLeaves() { //code to add leaves }
}

class EarlGreyLeafAdder implements LeafAdder{
   function addTeaLeaves() { //code to add earl grey leaves }
}

Now we can do something similar with condiments, by making a CondimentAdder interface and a couple classes that implement it:

Condiment Adding Classes

interface CondimentAdder() {
  function addCondiments();
}

class SugerAdder implements CondimentAdder {
    function addCondiments() { //code to add sugar }
}

class HoneyAdder implements CondimentAdder {
  function addCondiments() { //code to add honey }
}

Now, let’s take a look at re-implementing our TeaPot class using these new objects:

Tea Pot Class (new version)

class TeaPot() {
  private LeafAdder LeafAdder;
  private CondimentAdder CondimentAdder;

  function TeaPot() {
    this->LeafAdder = new RegularLeafAdder();
    this->CondimentAdder = new SugarAdder();
  }

  function makeTea() {
      boilWater();
      addTeaLeaves();
      seepTea();
      removeTeaLeaves();
      addCondiments();
  }

  function boilWater() { //code to boil water }
  function addTeaLeaves() {
    this->LeafAdder->addLeaves(); 
  }
  function seepTea() { //code to seep tea }
  function removeTeaLeaves() {  //code to remove tea leaves }
  function addCondiments() { 
    this->CondimentAdder->addCondiments();
  }
 }

We didn’t have to change the interface of this class, so any code that was relying on it should be unaffected by our changes. We can no also implement new classes without having to write any new logic. so now writing that EarlGreyWithHoneyTeaPot class is easy-peasy:

Earl Grey w/ Honey Class

class EarlGreyWithHoneyTeaPot extends TeaPot {
  function EarlGreyWithHoneyTeaPot() {
    this->LeafAdder = new EarlGreyLeafAdder();
    this->CondimentAdder = new HoneyAdder();
  }
}

and we’re done. This also means that if we come up with a new type of leaf to add or new condiment, we can easily create classes that implement them and existing types. now we can make 4 types of tea. If we added a SplendaAdder, would could now make 6. We could also take this approach to some of the other methods if it was necessary.

Now you may be thinking But we’re still extending TeaPot and making a bunch of new classes, don’t we want to avoid inheritance? This is true, and there is an alternative way to approach this: instead of having many TeaPot sub classes, you can add setters and a new constructor the TeaPot class:

class TeaPot {
  function TeaPot(LeafAdder newLeafAdder, CondimentAdder newCondimentAdder) {
    this->LeafAdder = newLeadAdder;
    this->CondimentAdder = newCondimentAdder;
  }

  function setLeafAdder(LeafAdder newLeafAdder) {
    this->LeafAdder = newLeafAdder;
 }

 function setCondimentAdder(CondimentAdder newCondimentAdder) {
    this->CondimentAdder = newCondimentAdder;
 }

This shows us the second benefit of this: we can change the behavior of TeaPot at runtime!

Liskov Substitution Principle (SOLID Part 3)

Part of my SOLID programming principles series.

What is the Liskov Substitution Principle?

This Liskov Substitution Principle states that you should be able to replace any object with a sub class of that object and have all of the code still function properly. Any code that uses these objects does not need to know or care what it is getting passed.

Benefits

The benefits of this principle really shine when applied along with the Open/Closed principle. If a class is extended into multiple sub-classes, you can still use the same code you used with the main class with these new classes. This way you prevent dependencies and increase the ability to re-use code.

Applying the Liskov Substitution Principle

For this example, suppose you have 2 classes: A rectangle class and a square class that extends it, since a square is a rectangle.

Rectangle Class

class Rectangle {
    private int width;
    private int height;

    function setWidth(width) { this->width = width; }
    function getWidth() { return this->width; }
    function setHeight(height) { this->height = height; }
    function getHeight() { return this->height; }
}

For the square class, we’ll override the setters so that the square class can make sure that it always remains a square.

Square Class

class Square extends Rectangle {

  function setWidth(width) { 
     this->width = width;
     this->height = width;
  }

  function setHeight(height) {
     this->width = height;
     this->height = height;
  }
}

Now, let’s say that we want to write a function that can take a rectangle and change it’s proporitions so that it is a golden rectangle. that function would probably look something like this:

function makeGoldenRectangle(Rectangle r) {
  int newWidth = (int) r->getHeight() * 1.618;
  r->setWidth(newWidth);
}

Now you see that even though this is a perfectly fine construction:

Rectangle r = new Square();

when you pass r into the makeGoldenRectangle() function, you are not going to get the results you expected.

If this is hard to understand with shapes, maybe duckies will be easier:

Open/Closed Principle (SOLID part 2)

Part of my SOLID programming principles series.

What is the Open/Closed Principle?

The Open/Closed Principle states that “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification”. This is mainly accomplished via inheritance, although there are other means. The main idea is that once an object is completed, it should only be modified to correct errors, with additional functionality being handled by subclasses.

Benefits

Changing code is expensive. It can require unit tests, code reviews, and could potentially cause other issues with other code that is reliant on the code you are changing. By designing objects so that they are closed to modification, you can prevent a lot of these problems, and by still writing it in an extendable fashion, you are not losing out on the ability to use added functionality.

Applying the Open / Closed Principle

Applying this principle requires some planning by the developer. Keeping the single responsibility principle in mind here will be helpful, as an object with a singular focus will be less likely to require modification in the future. It’s also important to keep your methods short and sweet. Let’s say you have two TeaPot objects, each with a makeTea function that looks like this:

Object 1

function makeTea() {
   //code to boil water ...
   //code to add tea leaves    
   //code to seep tea
   //code to remove tea leaves
   //code to add cream and sugar
}

Object 2

function makeTea() {
    boilWater();
    addTeaLeaves();
    seepTea();
    removeTeaLeaves();
    addCondiments();
}

function boilWater() { //code to boil water }
function addTeaLeaves() { //code to add tea leaves }
function seepTea() { //code to seep tea }
function removeTeaLeaves() {  //code to remove tea leaves }
function addCondiments() { //code to add cream and sugar }

Now, we’ve decided that we like our tea with honey in it instead of cream and sugar. If we were using the first object, we have a couple options

  • We could replace cream & sugar with honey in the makeTea() function. However, this could possibly piss off some of our fellow tea drinkers.

  • We could add an argument to the makeTea() function that let’s people input what condiments they would like in their tea. The problem here is that now we’ve change the interface of the TeaPot class, and that could affect other code using it. (more on interfaces later this week)

  • We could create a HoneyTeaPot sub-class, and override the makeTea() function. But since we only want to change one part of the function, we are going to end up duplicating a lot of code when we make the new version of the method.

As you can see, trying to make one simple change to this object has caused us a lot of headache. Now let’s try accomplishing this with object 2, that has smaller, simpler methods and is more open to extension:

  • Create a new HoneyTeaPot class, and override the addCondiments() method.

Done! no code duplication, no changing interfaces, and no affecting any other code that uses the TeaPot class.

Single Reponsibility Principle (SOLID part 1)

Part of my SOLID programming principles series.

What is the Single Responsibility Principle?

The single responsibility principle is pretty self-explanatory. When designing objects, they should only serve a single purpose in a program. This also works in the inverse, in that each responsibility of your program should be handled, and completely encapsulated by a single class.

Benefits

There are several benefits to designing software this way. One is maintainability. Single responsibility classes are clearly defined, so when a fix needs to take place, it is easy to find where and how to make the fix, and that this change will have less impact on other parts of the system. Another is that this makes code more reusable, and less tangled together.

Applying the Single Responsibility Principle to Objects

Looking at MVC frameworks is a great way to see how this principle can be applied. You have three types of objects, each that only handle one thing in the system. Models handle data abstraction, controllers handle routing, and views handle the user interface. There are also separate models, views, and controllers for each of the objects in your program. This way, each part of the logic is kept separate, and it is easy to move, extend, and maintain.

Applying the Single Responsibility Principle to Functions

If your function takes a lot of arguments, or has a ton of if/elses, then maybe it needs to be broken up into multiple functions. A good rule of thumb I use is to make sure my functions can be named in a format, and that the verb isn’t something vague like ‘manage’. If your function can’t be named simply, then it probably is doing something to complicated.

SOLID Programming Principles

This week I wanted to take a look at five principles of object oriented programming, known as the SOLID principles, and write about practical applications of each.

Single Responsibility Principle — An object should only serve one purpose.

Open/Closed Principle — An object should be open to extension, but closed for modification.

Liskov Substitution Principle — objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program

Interface Segregation Principle — Many specific interfaces are better than one generic interface

Dependency Inversion Principle — Design to the interface, not the implementation.

I’ll be taking a more in-depth look at each of these this week.

Developer, Musician, Freelance Web Developer, and all around nice guy.

Facebook  |  Twitter  |  LinkedIn  |  Forrst  |  GitHub  |  StackExchange

twitter.com/GSto

view archive



Ask me anything