Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which design pattern allows abstraction of functionality based on runtime types?

I have an abstract class A and several implementations of it. I expect this to evolve over time, by adding more implementations.

I also have an interface that does something at instances of the above class hierarchy (eg print them).

I want implementations of the interface to provide some special functionality for some of the subclasses of A and a default functionality for the rest of them.

I hope this example clarifies things:

abstract class A { }
class B extends A { }
class C extends A { }

interface Processor  {
    public void process(A a);
}

class SimpleProcessor implements Processor {

    //I want this to be called when argument is instance of A or C or any
    //new class that will be added in the future
    public void process(A a) {
        //Line 14
        System.out.println("Default processing");
    }

    //I want this to be called when argument is instance of B
    public void process(B b) {
        System.out.println("Special processing");
    }

}

public class Runner {

    public static void main(String[] args) {
        B b = new B();
        Processor p = new SimpleProcessor();
        p.process(b);
    }

}

The example prints "Default processing". The problem is that the method to be executed is chosen based at the compile-time type of the interface's method. Is there a way (or design pattern) to make this program print "Special processing" without adding at line 14 a list of

if (a instance of B)
  process( (B) a );

for every class that needs special processing?

I had a look at the visitor pattern but it doesn't seem like an improvement because I don't want to "pollute" the Processor interface with methods for every subclass of A because more subclasses of A will be added.

To put it another way, I want the implementations of the interface to:

  • provide custom implementation of the method for specific subclasses of A
  • provide a default implementation for classes that will be added in the future
  • avoid listing all the classes in a large if-then-else list

Thanks!!

like image 396
idrosid Avatar asked Jan 21 '23 18:01

idrosid


1 Answers

Move the code into the type which varies and use polymorphism. See Open Closed Principle.

interface Processable  {
    void process();
}

abstract class A implements Processable {
    public void process() {
        System.out.println("Default processing");
    }
}
class B extends A {
    public void process() {
        System.out.println("Special processing");
    }
}
class C extends A {
    // default implementation inherited from A
}


class SimpleProcessor {
    public void process(Processable p) {
        p.process()
    }
}

public class Runner {
    public static void main(String[] args) {
        B b = new B();
        Processor p = new SimpleProcessor();
        p.process(b);
    }
}
like image 69
Esko Luontola Avatar answered Jan 28 '23 15:01

Esko Luontola