1

I have a conceptual question about SW design.

I prepared the following example:

One abstract base class:

internal abstract class BaseFamilyObject
{
    //
    //...
    //

    internal abstract IEnumerable<Device> GetCompatibleDevices(OtherDeviceClass otherDevClass);

    //
    // other methods
    //
}

Six object families inheriting from the base class:

internal class FamilyObjectA : BaseFamilyObject // 5 more families
{
    //
    // other methods
    //

    internal override IEnumerable<Device> GetCompatibleDevices(OtherDeviceClass otherDevClass)
    {
        // do something and return list
        return new List<Device>();
    }

    //
    //...
    //
}

The method GetCompatibleDevices is basically implemented in all six object families. This method is called generically at different places without knowing which specific object is calling (template method pattern).

Now another family is to be added, which however requires another two parameters in the method as decision criterion in order to return the correct list.

I can make it easy for myself and add the parameters to the signature in the base class. But I don't feel comfortable with introducing those to the other families, that don't need/use them.

internal class FamilyObjectNew : BaseFamilyObject // 5 more families
{
    //
    // other methods
    //

    internal override IEnumerable<Device> GetCompatibleDevices(OtherDeviceClass otherDevClass, bool criteria1, bool criteria2)
    {
        // do something and return list
        return new List<Device>();
    }

    //
    //...
    //
}

Do you have any other solutions for the problem? Maybe the basic decision of the template method pattern wasn't optimal?

3

The moment you wrote bool criteria1, alarm bells should have started ringing in your head: you have a boolean parameter/ flag argument being added to your code. This is a strong code smell and likely indicates that something is going wrong. When you considered bool criteria2 those alarm bells should have started screaming.

In reality, this new class wants to get a list of compatible devices based on one of four conditions. To put it another way, you have four different ways of getting a list of compatible devices. So have four different ways: use four separate classes, each of which represents one of those four states covered by your two booleans.

That way you avoid those boolean parameters and you avoid complicating existing code with unneeded parameters.

  • In addition to the answer. The template pattern is not necessarily wrong. If need arguments have to be added, that's Ok. The problem comes when some concrete classes need'em and others don't. It probably means that the abstraction (hierarchy of classes) must be reviewed. The problem with bool arguments is another issue with several drawbacks, As @David answered, they are a code smell. – Laiv Apr 30 at 16:06
0

I agree that adding criteria1 and criteria2 to BaseFamilyObject.GetCompatibleDevices whether it needs them or not is not a very good idea. If I understand correctly criteria1 and criteria2 do some kind of filtering on the devices that are to be returned by GetCompatibleDevices. Perhaps it's not actually the responsibility of BaseFamilyObject or its subclasses to know how to apply these criteria?

I see three potential solutions:

  1. Leave it to the caller of GetCompatibleDevices to filter the list of devices it gets in return.

  2. Move all the necessary logic for applying criteria to the otherDevClass passed to GetCompatibleDevices. It seems to me that all it should take to determine whether two devices are compatible is the devices themselves and not any external criteria. But if not, perhaps you can make something like a ConstrainedDevice that wraps another device, but also enforces your criteria.

  3. Create something like a DeviceCriteria class that you pass in as a parameter in place of the two booleans that is responsible for applying these criteria.

All these should let you have a the same signature for GetCompatibleDevices everywhere, which is more appropriate depends on your business needs.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.