Gulzar
Gulzar

Reputation: 27906

How to create a tree-hierarchical structure with unique behavior for every type combination?

Assume:

  1. There is some module whose interface is IA.
  2. There is some module B, that takes in a parameter an instance of IA, and whose behavior depends upon the type of that IA,

meaning (pseudo code, no specific language, [even though I use Python])

class B{
    IA ia;
    B(IA ia){
        this.ia = ia;
    }
    doStuff(){
        if(type(this.ia)==A1){
             print("A1");
        }
        if(type(this.ia)==A2){
             print("A2");
        }
    }
}

I realize I could add some public method foo to ia, and thus the code would simplify to

class B{
    IA ia;
    B(IA ia){
        this.ia = ia;
    }
    doStuff(){
        this.ia.foo();
    }
}

My question is twofold:

  1. What is the correct design to achieve this if I (for some reason) can't change IA, meaning, I can't add foo()?

  2. What is the correct (scalable) design if I am allowed to change IA but the same problem now repeats for A1, A2, and so on, meaning

the final desired behavior is

class B{
    IA ia;
    B(IA ia){
        this.ia = ia;
    }
    doStuff(){
        if(type(this.ia)==A1){
             if(type(this.ia.iz)==Z1){
                 print("A1Z1");
                 print("unique treatment");
             }
             if(type(this.ia.iz)==Z2){
                 print("A1Z2");
                 print("special treatment");
             }
        }
        if(type(this.ia)==A2){
             if(type(this.ia.iz)==Z1){
                 print("Z1A2");
                 print("one of a kind treatment");
             }
             if(type(this.ia.iz)==Z2){
                 print("Z2A2");
                 print("Wow treatment");
             }
        }
    }
}

and can repeat more times.

Please notice Z1 and Z1 are the same for A1 and A2!. And again, the same can go on, IZ can contain IX of several types, with unique behaviors

I wonder if case 2 is at all separate of modular, in the sense that, the behavior is unique for every type-combination, and no behavior can really be extracted to a more abstract level.

I still don't like the type checking, and wonder if there is something that can be done which looks better.

Upvotes: 1

Views: 37

Answers (2)

GlenPeterson
GlenPeterson

Reputation: 5206

I think what you're looking for is Tagged Union Types. Consider also Do union types actually exist in python?

Some examples might be Kotlin's Sealed Classes and When Statement. Or Scala's Case Classes and Sealed Traits.

If you're using Java, it's going to be a little wordy to set up using Paguro.

interface Wheeled { }

class Beetle implements Wheeled { }

class Truck implements Wheeled { }

// Makes a union class.
static class Beetle_Truck extends OneOf2<Beetle,Truck> {
    private Beetle_Truck(Object o, int n) {
        super(o, Beetle.class, Truck.class, n);
    }

    static Beetle_Truck ofBeetle(Beetle b) {
        return new Beetle_Truck(b, 0);
    }
    static Beetle_Truck ofTruck(Truck t) {
        return new Beetle_Truck(t, 1);
    }
}

// Here's where we use it.
@Test public void testBeetleTruck() {
    Beetle_Truck bt1 = Beetle_Truck.ofBeetle(new Beetle());
    Beetle_Truck bt2 = Beetle_Truck.ofTruck(new Truck());

    assertEquals("b", bt1.match(beetle -> "b",
                                truck -> "t"));

    assertEquals("t", bt2.match(beetle -> "b",
                                truck -> "t"));
}

Note that Beetle and Truck do not need to implement a common interface at all. For your second question, you could just do a OneOf4 similar to OneOf2.

Upvotes: 0

David Soroko
David Soroko

Reputation: 9086

You may want to take a look at the Visitor pattern (https://en.wikipedia.org/wiki/Visitor_pattern). This does assume that you can change IA and friends to implement the accept methods

Upvotes: 1

Related Questions