Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inheritance vs type flags

I've done some searching (maybe I'm not describing my problem well enough) but haven't been able to find an answer for this

Let's say you have the following POCO:

public class StandardObject
{
   public string A {get;set;}
   public string B {get;set;}
}

Somewhere else in the program there is some logic to handle StandardObjects. There exists a case where sometimes a StandardObject may need to be handled differently. We know this when we create the StandardObject however none of the current properties can be used to determine this further down the chain. One approach would be to add and set a flag or type enum on StandardObject and check this when handling the objects. E.G.

if(standardObject.IsSpecial){...}

if(standardObject.ObjectType == StandardObjectTypeEnum.Special){...}

This doesn't seem like the best approach though.

The other option would be to create a derived class:

public class SpecialObject : StandardObject { }

So now rather than checking a property, we can check the type. E.G.

if(standardObject.GetType() == typeof(SpecialObject)){...}

(Depending on what we were doing, the type checking might be implemented differently)

Note that SpecialObject does not add to or change StandardObject in any way. It is essentially the same object. This approach has the advantage of being more flexible (E.G. We could add some additional properties to SpecialObject) but in reality it won't change. It will always be identical.

To me inheritance seems like the better approach. Type flags seem like code smell and going forward inheritance seems more like the correct OOP approach. The thing I'm not sure about here is that, given that StandardObject and SpecialObject are the same, is it bad practice to do this? Or are there any reasons why this should be avoided?

There is a similar question here:

Chess piece hierarchy design: inheritance vs type fields

However most of the discussion seems to focus on the chess problem rather than what is considered good design

EDIT:

Encapsulation seems to be the popular solution. The reason why I've avoided encapsulation is best described in the example below:

  • StandardObject is essentially a DTO
  • There is a list of StandardObject.
  • The list gets processed, StandardObject gets serialized
  • The serialized data is sent somewhere over any number of different protocols
  • One of the protocols requires certain parameters to be set if StandardObject is "special"

Given that the additional logic for "special" cases is only required by one of a number of different mechanisms that process StandardObject, the logic for this does not seem like it belongs anywhere near StandardObject

like image 492
wdavo Avatar asked Oct 07 '22 17:10

wdavo


2 Answers

They are both code smells. If you have special handling that must be accomplished differently between two related types, have them both implement some virtual method or interface operation that allows them to handle the special case (or not, if it's not necessary).

like image 179
Randolpho Avatar answered Oct 11 '22 02:10

Randolpho


You could also encapsulate your "handling" logic using a strategy pattern by having an abstract base class where you could also put shared implementation. Or you could call a derived implementation and the inherited implementation as well.

public abstract class Vehicle
{
    private MovementStrategy movementStrategy;

    protected Vehicle(MovementStrategy strategy)
    {
        this.movementStrategy = strategy;
    }

    public void Move()
    {
        movementStrategy.Move();
    }

    public virtual void CommonAlert()
    {
        Console.WriteLine("Base generic vehicle alert");
    }
}

public class Car : Vehicle
{
    public Car(MovementStrategy movementStrategy)
        : base(movementStrategy)
    {
    }

    public override void CommonAlert()
    {
        Console.WriteLine("Car says 'Honk!'");
    }
}

public class Elevator : Vehicle
{
    public Elevator(MovementStrategy movementStrategy)
        : base(movementStrategy)
    {
    }

    public override void CommonAlert()
    {
        Console.WriteLine("Elevator says 'Ding!'");
        base.CommonAlert();
    }
}

public abstract class MovementStrategy
{
    public abstract void Move();
}

public class CarMovementStrategy : MovementStrategy
{
    public override void Move()
    {
        Console.WriteLine("Car moved");
    }
}

public class ElevatorMovementStrategy : MovementStrategy
{
    public override void Move()
    {
        Console.WriteLine("Elevator moved");
    }
}

Then, for your main program, a sample use:

class Program
{
    static void Main(string[] args)
    {
        Vehicle elevator = new Elevator(new ElevatorMovementStrategy());
        Vehicle car = new Car(new CarMovementStrategy());

        elevator.Move();
        car.Move();

        elevator.CommonAlert();
        car.CommonAlert();

        Console.Read();

    }
}
like image 45
Bryan Crosby Avatar answered Oct 11 '22 01:10

Bryan Crosby