Какой шаблон проектирования позволяет абстрагировать функциональность на основе типов времени выполнения? - PullRequest
4 голосов
/ 14 июля 2010

У меня есть абстрактный класс A и несколько его реализаций. Я ожидаю, что это будет развиваться со временем, добавляя больше реализаций.

У меня также есть интерфейс, который что-то делает в экземплярах вышеуказанной иерархии классов (например, печатает их).

Я хочу, чтобы реализации интерфейса предоставляли некоторые специальные функции для некоторых подклассов A и функциональность по умолчанию для остальных из них.

Надеюсь, этот пример прояснит ситуацию:

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

}

В примере печатается «Обработка по умолчанию». Проблема состоит в том, что метод, который должен быть выполнен, выбирается на основе типа времени компиляции метода интерфейса. Есть ли способ (или шаблон проектирования), чтобы эта программа печатала «Специальную обработку», не добавляя в строке 14 список

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

для каждого класса, который нуждается в специальной обработке?

Я посмотрел на шаблон посетитель , но это не кажется улучшением, потому что я не хочу «загрязнять» интерфейс процессора процессорами для каждого подкласса A, потому что больше подклассов Будет добавлено.

Другими словами, я хочу, чтобы реализации интерфейса:

  • обеспечивает пользовательскую реализацию метода для определенных подклассов A
  • обеспечивает реализацию по умолчанию для классов, которые будут добавлены в будущем
  • избегайте перечисления всех классов в большом списке if-then-else

Спасибо !!

Ответы [ 6 ]

4 голосов
/ 14 июля 2010

Переместите код в тип, который варьируется, и используйте полиморфизм. См. Открытый Закрытый Принцип .

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);
    }
}
3 голосов
/ 14 июля 2010

Как насчет того, чтобы создать адаптер, который принимает объект, который вы хотите обработать, и возвращает процессор для этого объекта?

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

Пример кода:

class Adapter {

    Processor getProcessor(Object o) {
        if (o instance of A) {
            return new ProcessorA();
        } else if ...
    }

}
1 голос
/ 14 июля 2010

Как насчет создания реализации Процессора для каждого Объекта, затем мы регистрируем их в CompositeProcessor, например,

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

Теперь используйте CompositeProcessor в классе Runner.приведенный выше код, просто набранный в этом редакторе, следовательно, возможно, есть некоторые ошибки, но вы понимаете:).

Некоторые преимущества: - процессор отделен от класса, который он обрабатывает (например, A и ProcessorA разделены).- Может быть более 1 процессора данного объекта - Отображение процессоров может быть изменено во время выполнения

1 голос
/ 14 июля 2010

Вы можете позволить классам самому возвращать процессор

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

Однако для другой истории скина, возможно, было бы лучше создать фабрику процессоров, которая возвращает правильный процессор в зависимости от виджета, для различных скинов вы могли бы создать ProcessorFactory, который зависит от того, какой скин используется

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

примечание: это только идея, конечно, не лучшее решение, я думаю

0 голосов
/ 28 июня 2012

Шаблонный метод.

Базовый класс реализует поведение по умолчанию, производные классы реализуют специфическое поведение.

Class BaseWithTemplateMethod {
  void process() {
    // Default behavior goes here
  }
}

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

Вы можете сделать много вариантов этой темы, например, инкапсулировать поведениев другом классе - конфигурирование экземпляров во время выполнения относительно того, какое конкретное поведение они используют, по сути, используя вместо этого композицию.Это особенно полезно в Java и других языках без множественного наследования.

0 голосов
/ 14 июля 2010

Это дальнейшее улучшение использования адаптера.Для этого решения требуется библиотека отражений из: http://code.google.com/p/reflections/

Преимущество:

  • нет, если тогда еще с instanceof
  • нет конфигурации

Недостаток:

  • Нужна библиотека размышлений
  • Может быть медленной в начале

Это так:

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

}

Результат:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...