Jay
Jay

Reputation: 1097

Traits vs. abstract classes for code reuse

While working on a project i stumbled across a question relating to the proper way of choosing the correct form of code reuse in terms of traits vs. abstract base classes. I came across the point where i have multiple abstract base classes but can only inherit from one due multiple inheritance restrictions from PHP. Rethinking this makes me questioning my design and so i want to know how others would solve this problem.

Following scenario

In my project i have two kind of calculations

  1. Simple calculations
  2. Grouped calculations which in case are simple calculations just with some more information

Setting up the interfaces

interface SimpleResultInterface{
    public function getValue();
}

interface GroupedResultInterface extends SimpleResultInterface {
    public function getGroupValues();
}

Setting up the base classes

Avoiding code duplication as a good form of design i set up two abstract base classes.

abstract class SimpleResultBase implements SimpleResultInterface{

    public function getValue(){
        return 1;
    }
}

abstract class GroupedResultBase extends SimpleResultBase implements  GroupedResultInterface {

    public function getGroupValues(){
        return array(
            "group1" => 1,
            "group2" => 2,
            "group3" => 3,
        );
    }
}

So the first question here is

  1. Does GroupedResultBase need to inherit SimpleResultBase in terms of expressing the interface relationship from SimpleResultInterface and GroupedResultInterface ?

I have chosen yes because otherwise i would have to duplicate the getValue method from SimpleResultBase

What we have so far

Now i can epxpress

  1. GroupedResultInterface instanceof SimpleResultInterface ( true )
  2. GroupedResultBase instanceof SimpleResultBase ( true )
  3. SimpleResultBase instanceof SimpleResultInterface ( true )
  4. GroupedResultBase instanceof GroupedResultInterface ( true )

Going concrete where the troubles start

At this point there are no concrete classes. Now i want to make two concrete classes which have a relationship together respecting the interface definitons.

class OEESimpleResult extends SimpleResultBase {

}

class OEEGroupedResult extends GroupedResultBase{

}

this works very nicely but what i can't do is this

class OEESimpleResult extends SimpleResultBase {

}

class OEEGroupedResult extends OEESimpleResult,GroupedResultBase {

}

interface GroupedResultInterface extends SimpleResultInterface

As above noted in the interface definiton. Each grouped result is also a simple result. So i would have expected that OEEGroupedResult also is a OEESimpleResult

But when i would do this i can not reuse my abstract base class GroupedResultBase because multiple inheritance is not allowed. This leads me to following questions.

  1. It seems that the class relationship in the abstract classes freezes the inheritance chain down the road. Would it be better to remove the abstract classes and replace them with traits making the concrete classes using them?
  2. What is the right way of providing default implementations if u have multiple interfaces but can only inherit from one base class?
  3. This problem seems very common in oop so are there any best practices and recommendations how to overcome this issue?
  4. Should classes that implement the interfaces also express the relationship in form of inheritance like on the interface level or is the interface realtionship alone enough? In my case should OEEGroupedResult express the relation to OEESimpleResult on the class level like their interfaces on the interface level.

Thank u for your help :)

Upvotes: 3

Views: 1825

Answers (1)

jaro1989
jaro1989

Reputation: 405

As you are working in oop style, you might hear about SOLID principles. So your design breaks the following letters S,I,D with potential to break O and L.

So how do you broke "S": Right here!

class OEEGroupedResult extends OEESimpleResult,GroupedResultBase {

}

Suddenly or luckily php doesn't allow it, but your class would provide some additional separate logic, so you will have at least two reasons to change it. But yeah, it's because of

interface GroupedResultInterface extends SimpleResultInterface {
    public function getGroupValues();
}

Here's your I letter breaks. Solution is pretty simple: think about methods your GroupedResultInterface really needs and put it inside and really means that these methods must do the same job. They are not as you are breaking letter D here:

abstract class SimpleResultBase implements SimpleResultInterface{

    public function getValue(){
        return 1;
    }
}

abstract class GroupedResultBase extends SimpleResultBase implements  GroupedResultInterface {

    public function getGroupValues(){
        return array(
            "group1" => 1,
            "group2" => 2,
            "group3" => 3,
        );
    }
}

Your class class GroupedResultBase depends on concrete method of another class. But you will quickly fix it after interface segregation.

Notice. I'm using traits when I can't change something inside vendor classes. Only. It's just me, but when I think that trait will help me then something is wrong with architecture. It's up to you of course.

Little link: https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)

Have fun!

Upvotes: 2

Related Questions