2010-07-14 3 views
4

У меня есть абстрактный класс 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); 

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

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

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

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

Спасибо !!

+0

Образец, который вы ищете, называется «способностью преодолевать» ... –

+0

Стратегия? –

+0

Спасибо всем за ваши ответы. Я, наконец, использовал список тестов if-then-else, например. – idrosid

ответ

3

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

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

пример кода:

class Adapter { 

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

} 
+0

Спасибо за ответ. Я думал о чем-то подобном.Создайте реализацию AbstractProcessor, которая имеет перегруженные методы для каждого подкласса A и метода ввода с операторами if-then-else. Внутри, если я могу иметь явное приведение к конкретному подклассу A. Таким образом, подклассы Процессора могут обрабатывать новые типы A, и я могу добавить процессор без каких-либо изменений в A или подклассах. Однако мое беспокойство заключается в том, что он просто не выглядит очень OO, чтобы иметь огромные блоки if-then-else, которые проверяют тип. Мне было интересно, есть ли лучший подход. – idrosid

+0

Вы можете поместить отображение в файл свойств или XML, тогда у вас не будет большого списка if-then-else. Или ... позвольте мне сначала попытаться закодировать его ... – nanda

+0

+1 Адаптер - лучший выбор здесь, чем расширение основного класса и использование простого полиморфизма, поскольку он помогает хранить «служебный» код (регистрация, печать, рисование, .. .) из основного класса. –

4

Переместить код в тип, который изменяется и использовать полиморфизм. См. 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); 
    } 
} 
+0

Спасибо за ответ. Проблема в том, что я хотел бы иметь много реализации процессора. Чтобы сделать вещи более конкретными, класс A представляет собой виджет пользовательского интерфейса, а Processor - средство визуализации для виджетов. Я хочу иметь возможность добавлять дополнительные средства визуализации (например, больше скинов). Я также хочу добавить больше виджетов (путем подклассификации существующих виджетов) и иметь возможность обновлять только некоторые из рендерингов. Итак, оба подкласса A и Процессора меняются, поэтому предлагаемый вами подход не решает проблему. – idrosid

+2

Проблема заключается в том, что вам нужно поместить много «служебного» кода в классы, которые не должны касаться этих сервисов (регистрация, печать, рисование, ...). Использование адаптера столь же гибко и сохраняет «служебный» код из основных классов. –

0

Это дальнейшее совершенствование использования адаптера.Это решение нуждается в библиотеке Отражения от: 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 
1

Вы могли бы позволить себя классы вернуть процессор

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

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

1

Как о создании реализации процессора для каждого объекта, затем мы регистрируем их в 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 разделены). - Может быть более одного процессора данного объекта - Отображение процессоров может быть изменено во время выполнения

0

Метод шаблона.

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

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

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

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

Смежные вопросы