Pages

Friday, February 26, 2021

When doing composition, forget about what you inherited!

      I'm revisiting the first chapter from Head First Design Patterns, which is my favorite one so far.

It promotes the composition against inheritance through the strategy pattern, and to put it together, here are the main points of the two approaches as I got from the chapter.

Choosing full inheritance (no polymorphism) may be good if you are looking for 

- Code reuse: Code can be written once and shared among subclasses.

but you should think twice before using inheritance for only that purpose because, on the other hand, this can be at a cost, because:

- Every subclass will share the "default" behavior, with no possible customization, classes lose the singularity advantage.

The first solution that comes to mind is employing polymorphism through using interfaces or abstract classes, that fixes the full inheritance issue so:

Behavior can be customized (any subclass can have its own version of the base class/ interface).

But here are the issue with polymorphism:

- Change is costly, where you have to go through all implementations to change the behavior.

- There may be duplications.

So the strategy pattern comes into play if you want to avoid the red points.

The problem is that you don't know if your implementation needs full inheritance or be overridable. because when inheriting implementation from the base class you're stating strongly, that the subclass will never change in a different direction, and when using polymorphism you're stating that the subclass will always be different than the base class, the problem is that; you're always referencing the base class in a way or another: changes are done with respect to the base class.

In the strategy pattern behaviors of subclasses have their own personality that shouldn't be tied to the base class(1), and you compose them into the subclasses and you can change this behavior in runtime.

Let us have the example used in the HFDP book:

you want to implement different types of ducks in the app, each with fly and quack behaviors,

so here's is the IFlyBehavior

public interface IFlyBehavior
{
    void Fly();
}

IQuackBehavior:

public interface IQuackBehavior
{
    void MakeQuack();
}

The Duck abstract class:

    public abstract class Duck
    {
        public void Display()
        {
            Console.WriteLine($"{Name} Duck");
        }

        public IFlyBehavior flyBehavior;
        public IQuackBehavior quackBehavior;
        public abstract string Name { get; }

        public void Fly()
        {
            flyBehavior.Fly();
        }

        public void Quack()
        {
            quackBehavior.MakeQuack();
        }
    }


For every type of duck, there may be different fly/quack behavior, with the strategy pattern, we create the needed behavior class, implementing the IFlyBehavior or IQuackBehavior and attach it to the concrete duck class, for example, the DecoyDuck implementation looks like this:


public class DecoyDuck : Duck
{
    public DecoyDuck()
    {
        flyBehavior = new FlyNoWay(Name);
        quackBehavior = new MuteQuack(Name);
    }

    public override string Name => "Decoy";
}

this fixes the inheritance issues, except for one issue I found later,  which I named the title of this blog post after, do you see it?...

It's the Name property being translated to the behavior class from the base class:

        flyBehavior = new FlyNoWay(Name);
        quackBehavior = new MuteQuack(Name);

with this translation, all the benefits of the composition are put in danger because now the supposedly inheritance-free components are no longer free, they are tied to the base class with this little weight. 
I found this issue when I wanted to change how I attach behaviors, so instead of initializing behaviors in the subclass constructor, I wanted to add them from outside through two public methods: SetFlyBehavior and SetQuackBehavior to change behaviors in runtime. and this means I will need to send the duck name, for example:

var duck = new DecoyDuck();
duck.SetFlyBehavior(new FlyNoWay("Decoy"))

I'm stating Decoy twice as the type and as the string name, I didn't feel OK with this, the name of the duck is loosely coupled with its type, and they need to be maintained together.

The fix is simple; I removed the name from the IFlyBehavior and made it a separate component without being connected to the duck properties. with this change, I can compose any behavior without worrying about the Duck class.
This is all about it, a good lesson on how strategy pattern with composition can be employed.

How to do code reviews correctly

Introduction Code review is a special event in the life cycle of software development, it's where ownership is transferred from the deve...