What design pattern allows you to abstract away functionality based on runtime types?

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

I also have an interface that does something in instances of the above class hierarchy (e.g. prints them).

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

I hope this example clarifies the situation:

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); } } 

This example prints "Default Processing". The problem is that the method that will be executed is selected based on the interface compilation time type. Is there a way (or a design pattern) for this program to print “Special Processing” without adding a list to line 14

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

for every class that needs special treatment?

I looked at visitor , but this does not seem to be an improvement, because I do not want to “foul” the processor interface with the methods for each subclass A, as more subclasses of A.

In other words, I want the implementation of the interface:

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

Thanks!!

+4
source share
6 answers

How about creating an adapter that accepts the object you want to process and return a processor for that object?

 if A -> return ProcessorA if B -> return ProcessorB 
Code example

:

 class Adapter { Processor getProcessor(Object o) { if (o instance of A) { return new ProcessorA(); } else if ... } } 
+3
source

Move the code to a type that changes and uses 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); } } 
+4
source

You can let the classes themselves return a processor

 interface Widget { Processor getProcessor(); } interface Processor { void process(Widget w); } abstract class WidgetA implements Widget { Processor getProcessor() { return new Processor() { void process(Widget w) {// do magic default process stuff} }; } } class WidgetB extends WidgetA { // uses default processor } class WidgetC extends WidgetA { Processor getProcessor() { return new Processor() { void process(Widget w) {// do magic widget C process stuff} }; } } 

However, for a different cover story, it might be better to create a factory processor that returns the right processor depending on the widget, for different skins, which you could then create a ProcessorFactory that depends on which skin is used

 interface ProcessorFactory { Processor getWidgetAProcessor(); .... } abstract class WidgetA implements Widget { Processor getProcessor() { return factory.getWidgetAProccesor(); } void setProcessorFactory(ProcessorFactory pf) { this.factory = pf; // move method signature also to interface } } 

note: this is just an idea, certainly not the best solution, I think

+1
source

How to create a Processor view for each object, then we register them in a CompositeProcessor, for example.

 public class ProcessorB implements Processor { public void process(A input) { // do something here for B. } } public class ProcessorC implements Processor { public void process(A input) { // do something here for C} } // add more processors if needed public class CompositeProcessor implements Processor { private Map<Class,Processor> processors; public CompositeProcessor(Map<Class,Processor> processors) { this.processors=processors; } public void process(A input) { for (Map.Entry<Class<?>,Processor> entry : processors.entries()) { if (entry.getKey().isAssignableFrom(input.getClass()) { entry.getValue().process(input); return; } } // do default processing here } } 

Now use CompositeProcessor in the Runner class.

Note. I did not compile the above code, I just typed this editor, so there are probably some errors, but you get the idea :).

Some advantages: - the processor is separate from the class that it processes (for example, A and ProcessorA are separate). - There may be more than one processor of a given object - The display of processors can be changed at run time

+1
source

This is a further improvement in the use of the adapter. This solution needs a reflection library from: http://code.google.com/p/reflections/

Advantage:

  • no if then else with instanceof
  • no configuration

Inconvenience:

  • Need Reflections Library
  • May be slow at the beginning

It's him:

 import java.lang.reflect.ParameterizedType; public abstract class Processor<T> { private final Class<T> processedClass; public Processor() { ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass(); processedClass = (Class<T>) parameterizedType.getActualTypeArguments()[0]; } public Class<T> getProcessedClass() { return processedClass; } protected abstract void process(T message); } public class A { } public class B { } public class ProcessorA extends Processor<A> { @Override protected void process(A message) { System.out.println("Processing object A"); } } public class ProcessorB extends Processor<B> { @Override protected void process(B message) { System.out.println("Processing object B"); } } import java.lang.reflect.Constructor; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.reflections.Reflections; public class Adapter { private Map<Class<?>, Processor<Class<?>>> mapping = new HashMap<Class<?>, Processor<Class<?>>>(); public Adapter() throws Exception { Reflections r = new Reflections(""); Set<Class<? extends Processor>> subTypesOf = r.getSubTypesOf(Processor.class); for (Iterator iterator = subTypesOf.iterator(); iterator.hasNext();) { Class<? extends Processor> c = (Class<? extends Processor>) iterator.next(); Constructor<? extends Processor> constructor = c.getConstructor(); Processor p = constructor.newInstance(); mapping.put(p.getProcessedClass(), p); } } public <T> Processor<T> getProcessor(T obj) { return (Processor<T>) mapping.get(obj.getClass()); } } public class Main { public static void main(String[] args) throws Exception { Adapter adapter = new Adapter(); A a = new A(); adapter.getProcessor(a).process(a); B b = new B(); adapter.getProcessor(b).process(b); } } 

Result:

 14:01:37.640 [main] INFO org.reflections.Reflections - Reflections took 375 ms to scan 4 urls, producing 222 keys and 919 values Processing object A Processing object B 
0
source

Template method.

The base class implements the default behavior, derived classes implement the specific behavior.

 Class BaseWithTemplateMethod { void process() { // Default behavior goes here } } Class DerivedWithSpecific extends BaseWithTemplate { @override void process() { // Specific behavior goes here } } 

You can make many variations of this topic, for example, encapsulate behavior in another class, configure instances at runtime, what specific behavior they use, mainly using composition. This is especially useful for Java and other languages ​​without multiple inheritance.

0
source

Source: https://habr.com/ru/post/1315645/


All Articles