Stefanos Kargas
Stefanos Kargas

Reputation: 11073

Special services for different types of models in OOP

This question is language-agnostic for OOP languages.

I have two classes: DeviceA and DeviceB, which inherit from model class Device. I need to implement two services DeviceAService and DeviceBService, which inherit from DeviceService. My language does not support generics and I need to implement methods with a Device parameter in DeviceService, that will be overridden in DeviceAService and DeviceBService and implemented differently according to the type of the Device. Then I want to call these functions from the generic DeviceService.

What is the best practice to design this? Is the use of if and check on class type non-avoidable here? Another idea to optimally realize this design?

Upvotes: 1

Views: 85

Answers (1)

Mark Seemann
Mark Seemann

Reputation: 233197

If I understand the question correctly, you have an abstract base class like this:

public abstract class DeviceService
{
    public abstract void DoSomething(Device device);
}

You want to implement DeviceAService and DeviceBService so that their behaviour differs based on the type of Device.

Polymorphism

The most object-oriented design is to use polymorphism. Since Device is already polymorphic, put the behaviour that you want to differ into a polymorphic method, and call it from your service:

public override void DoSomething(Device device)
{
    // Perhaps do something first...
    device.DoThePolymorphicThing();
    // Perhaps do something else after...
}

Implement DoThePolymorphicThing differently in DeviceA and DeviceB.

Open world

If you can't change the API of the Device base class, and you need to also support other implementations than DeviceA and DeviceB, you'll probably need to perform a run-time check in DoSomething:

public override void DoSomething(Device device)
{
    if (device is DeviceA)
        // Do something...
    else if (device is DeviceB)
        // Do something else...
    else
        // remember to handle the case where it's neither A nor B
}

We can call this type of scenario an 'open world', because Device is open for extensibility.

Closed world

If, on the other hand, you know that you'll only ever going to have DeviceA and DeviceB, the Visitor design pattern is just what you need:

public interface IDeviceVisitor
{
    void VisitA(DeviceA device);
    void VisitB(DeviceB device);
}

public abstract class Device
{
    public abstract void Accept(IDeviceVisitor visitor);
    // Other members go here if you need them...
}

public class DeviceA : Device
{
    public override void Accept(IDeviceVisitor visitor)
    {
        visitor.VisitA(this);
    }

    // Other overrides, if needed, go here...
}

// Define DeviceB in the same way...

You can now implement the service like this:

public override void DoSomething(Device device)
{
    // Perhaps do something first...
    device.Accept(new MyDeviceVisitor());
    // Perhaps do something else after...
}

You can think of this scenario as the 'closed world' scenario, because you can't add a third type of device without changing the Visitor API (and thus breaking existing implementations).

Upvotes: 1

Related Questions