Question

I have been using a pattern in a lot of places (mainly C#) that I would like to know the name of.

Here is an example of it in C#:

public enum ThingType
{
    A,
    B,
    C
}

public interface IThing
{
    ThingType Type
    { get; }
}

public class ThingA : IThing
{
    public ThingType Type => ThingType.A;
}

public class ThingB : IThing
{
    public ThingType Type => ThingType.B;
}

public class ThingC : IThing
{
    public ThingType Type => ThingType.C;
}

As long as all implementations of IThing have a corresponding member in the enum, I can safely cast to the actual type of an IThing after checking the value of IThing.Type.

public void HandleThing(IThing thing)
{
    switch(thing.Type)
    {
        case ThingType.A:
            ThingA a = (ThingA)thing;
            // Doing something with a...
            break;

        case ThingType.B:
            ThingB b = (ThingB)thing;
            // Doing something with b...
            break;

        case ThingType.C:
            ThingC c = (ThingC)thing;
            // Doing something with c...
            break;
    }
}

I apologize if this question is a duplicate, I went through a few pages of search results for multiple different search phrases and I couldn't find this question already.

Was it helpful?

Solution

This pattern is called “type discriminator”.

It was very useful before OOP languages, for example to simulate polymorphic types with the help of discriminated unions. It is still heavily used and justified:

  • in relational database when several different types of records are grouped in a same table (aka single table inheritance on the database side, because records have a very similar structure and differ mainly in their behavior);
  • in heterogeneous and distributed developments when several languages are used, and you cannot count on cross-language polymorphism: objects are then serialized in one language and deserialized in another;
  • in event-driven programming, when the event generation and the event processing is decoupled.

This pattern is however not to be recommended in OOP as a first choice if you're not in one of the situation above. It might lead to an antipattern when it encourages people to think in a case-based manner with lots of specific details instead of abstracting the problem and using a truly polymorphic design:

  • The discriminator forces you to manually implement the polymorphism.
  • Whenever you add a new type, you’ll have to update your enum and inspect/extend every module or function where the enum is used. So it’s not easily extensible.
  • On the contrary, truly polymorphic design allows you to derive a new class, and everything you need to change is in that class. If the base class is used 1000 times across hundreds of modules, you won’t have to worry at all as long as you respect the interface and its contract.
Licensed under: CC-BY-SA with attribution
scroll top