Designing OOP Step by Step

I read Head First Design Patterns book shown as below. It is pretty good reference of Design Patterns. If you want to be familiar of Design Patterns, i recommend it definitely. This post contains some examples of this book but in a different presentation. When I want to remember fundamentals of Design Patterns, this post will remind me 🙂 I hope that you will benefit from this post.

I will create a class diagram and improve it step by step.

If you don't want to read all paragraphs, you just view class diagrams and code samples step by step. But, i heavily recommend to read and think of this post 🙂

Step 1

Notice that Duck is a superclass. MallardDuck and RedheadDuck are subclasses.

Each subclass (subtype) is responsible for implementing its own display() behaviour(method).

Lots of other Duck subtypes inherit from Duck supertype

Implementation of above Duck class diagram is below.



Step 2

I added Fly() behaviour(method) to Duck Superclass. In addition to this, I added also a new Duck subtype named as RubberDuck in class diagram.

Rubber Ducks don't quack. So, RubberDuck subtype overrides Quack() behavour(method) to Squeak.

Code looks very good 🙂 nooo :))) there is a problem in the following sample. Rubber Ducks shouldn't fly. But, by putting Fly() behaviour(method) in the Duck superclass, all subtypes will have fly() behaviour(method). So, how can we solve this problem? let's try to override Fly() behaviour(method) in Step 3.

Implementation of above Duck class diagram is below.



Step 3

We have one more subtype in the following class diagram named as DecoyDuck. It is becoming more interesting in this point. RubberDuck and DecoyDuck don't use Quack() and Fly() behaviours(methods).

Wooden decoy ducks shouldn't fly and quack. Standard developer behaviour is obvious due to this design 🙂 they just override to do nothing.

How about Rubber ducks? Rubber ducks also shouldn't fly and quack. But, there is a small changes for this subtype. They should sound squeak in Quack() behaviour(method).

Something went wrong again. As I said in Step 2. Rubber duck shouldn't fly. So to do that I just override fly() method to do nothing. But, Decoy duck shouldn't fly and quack. I override both methods to do nothing. This is again a standard developer behaviour. We can not override methods forever 🙂

There should be an OOP technique to solve this problem elegantly. We will try to find out a better one in Step 4.

Implementation of above Duck class diagram is below.



Step 4

If I put fly() and quack() behaviours(methods) in Duck superclass, then every duck subclass will have those behaviours(methods). Some Duck subclasses which shouldn't have fly() and/or quack() behaviours(methods) have to override to do nothing. Himmm i think it will not appear elegant. So, inheritance is not a solution in this case. Because, there may be many Duck subclasses which have different behaviour(methods) combinations.

I took fly() and quack() behaviours out of the Duck superclass and make a Flyable and Quackable interfaces. That way, only duck subclasses that are supposed to fly will implement that interface and have a fly() behaviour(method).

I think, this class diagram look better 🙂 but we need to duplicate fly() and quack() behaviours(methods) for Duck subclasses in which have. There may be many Duck subclasses that have fly() and/or quack() behaviours(methods). This means that we need to write fly() or quack() methods many times. This is a code duplication. whenever you need to modify a behavior, you are forced to track down and change it in all the different subclasses where that behavior(method) is defined.

There is a design principle for just this situation. I think that after you read the following paragraph, you should think about it and maybe you should read more and more 🙂

Identify the aspects of your application that vary and separate them from what stays the same.

Take what varies and "encapsulate" it so it won't affect the rest of your code.

Take the parts that vary and encapsulate them, so that later you can alter or extend the parts that vary without affecting those that don't.

In other words, if you've got some aspect of your code that is changing, say with every new requirement, then you know you've got a behavior that needs to be pulled out and separated from all the stuff that doesn't change.

🙂 presumably still not very clear for many developers. I don't want to translate from English to English but let's try to explain with a more simple way.

According to this Design Principle, it says that if you need to change some part of your code due to every new requirement, then you have got a behaviour that needs to be pulled out and encapsulate(or wrap) in a class then inject from outside.

To be honest 🙂 I am still not convinced. Let's try to explain with Duck demo in this post. Method or function in class is called as behaviour. I had a Duck superclass with a fly(), swim() and quack() behaviours(methods) firstly. This means that all subclasses of Duck superclass will have this behaviours. Initially, it wasn't a problem but when I need to create a RubberDuck subclass, it should have only quack() behaviour not fly() behaviour. I override fly() method and solve this problem. But, when i create DecoyDuck(wood duck), this time it shouldn't have both fly() and quack() behaviours(methods). This problem can continue like this forever. To solve this problem, i took fly() and quack() behaviours out of Duck superclass as Flyable and Quackable interfaces so that RubberDuck subclass will implement only Quackable interface(behaviour) and DecoyDuck subclass will not implement any of these behaviours(Flyable or Quackable). But this way didn't solve my problem. At least, it is not ideal 🙂 because there may be many Duck subclasses which have same fly behaviour or quack behaviour. In this case, i wrote duplicate code as shown in the following code. Notice that MallardDuck and RedheadDuck subclasses have same code in fly() and quack() behaviours(methods). Code is duplicated obviously.

Initially, fly and quack behaviours(methods) are in Duck superclass then I deleted fly and quack behaviours from Duck superclass and add those behaviours as an interface in subclasses of Duck superclass so that every subclass implemented their own fly and quack behaviour. But, this caused code duplication. Finally, i pull the duck behaviour out of the Duck classes in Step 5.

We will try to find out a better one in Step 5.

Implementation of above Duck class diagram is below.



Step 5

The Duck behaviours will be in a separate class that implements a particular behaviour interface so that the Duck class won't need to know any of the implementation details for their own behaviours.

Separating what changes from what stays the same.

We know that fly() and quack() are the parts of the Duck class that vary across ducks.

To separate these behaviours from the Duck superclass, we'll pull both fly() and quack() methods out of the Duck class and create a new set of classes to represent each behaviour.

Implementation of above Duck class diagram is below.



Double brace initialization used 🙂

This is same project but Duck superclass implements FlyBehaviour and QuackBehaviour interfaces.



Leave a Reply