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:
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
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).
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();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With