2012-03-22 3 views
0

Каков наилучший способ обработки различных подтипов абстрактного супертипа в качестве аргумента, например, при обработке событий.Обработка всех подтипов супертипа

ситуация выглядит следующим образом:

супертипе:

public interface MySuperInterface { 
} 

подтип

public class SubclassA implements MySuperInterface { 
} 

Другой подтип

public class SubclassB implements MySuperInterface { 
} 

Некоторые класс, который должен быть в состоянии Handl е любой подтип MySuperInterface

public class MySuperHandler { 

    public void handle(MySuperInterface mysuper) { 
     //do it 
    } 

} 

Моих разных подходов

  1. переключателя/саз в методе обработчика. (Который я не люблю)

  2. метод приема (MySuperHandler) в интерфейсе и отправки к этому методу внутри метода ручки: mysuper.receive (это) (что означает, что интерфейс знает обработчик класс)

  3. Добавление метода ручки для каждого подтипа в классе MySuperHandler (это не гарантирует, что каждый подтип может быть обработано)

но по указанным выше причинам я не довольствуясь этими решениями.

Есть ли какие-нибудь варианты лечения?

благодаря

+1

Если вы принимаете интерфейс в качестве параметра, но все еще нужно знать точный тип за этим интерфейсом, то, возможно, в вашем интерфейсе отсутствуют методы? – MaDa

+0

Спасибо. Я думаю, что добавление некоторых других методов в мой интерфейс (например, isA() или isB()) приведет к решению switch/case, которое я бы хотел избежать. также я не думаю, что это задача интерфейса по уходу за обработкой в ​​классе обработчика. Или я неправильно понял? – zersaegen

ответ

3

Один подход заключается в использовании Visitor Pattern. Это будет выглядеть примерно так:

public interface MySuperInterface { 
    <T> T acceptVisitor(MySuperInterfaceVisitor<T>); 
} 

public interface MySuperInterfaceVisitor<T> { 
    T visitA(SubclassA a); 
    T visitB(SubclassB a); 
} 

public class SubclassA implements MySuperInterface { 
    <T> T acceptVisitor(MySuperInterfaceVisitor<T> visitor) { 
    return visitor.visitA(this); 
    } 
} 

public class SubclassB implements MySuperInterface { 
    <T> T acceptVisitor(MySuperInterfaceVisitor<T> visitor) { 
    return visitor.visitB(this); 
    } 
} 

public class MySuperHandler implements MySuperInterfaceVisitor<Foo>{ 
    Foo visitA(SubclassA a) { 
    // construct Foo from SubclassA instance 
    } 

    Foo visitB(SubclassB a) { 
    // construct Foo from SubclassB instance 
    } 
} 

Это немного похоже на ваш # 2, за исключением интерфейса (и подклассы) не нужно знать о обработчиком. Им просто нужно знать об интерфейсе посетителя. Это хорошо, если вы не хотите, чтобы MySuperInterface и его реализации знали о ваших конкретных обработчиках.

BTW, вместо вызова:

myHandler.handle(myImpl); 

вы бы назвали:

myImpl.acceptVisior(myHandler); 

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

Если вы добавите еще одну реализацию своего интерфейса (MySuperInterface), компилятор заставит вас добавить метод acceptVisitor. Этот метод может либо использовать один из существующих методов visit*, либо вам нужно будет добавить и добавить новый интерфейс посетителя. Если вы сделаете последнее, вы должны затем обновить все реализации посетителя (ака «обработчик»). Это гарантирует, что каждый подтип можно обработать, продвигаясь вперед.

Этот подход более сложный, чем тот, который отвечает на вопрос assylias, и имеет смысл только в том случае, если вы либо хотите разбить связь между реализациями MySuperInterface и кодом вашего обработчика, либо у вас есть сильное желание организовать код вашего обработчика так что весь код для определенного типа обработки «вместе».

Одно общее использование шаблона посетителя - это рендеринг объектов по-разному. Предположим, вы хотите преобразовать объект в PDF или HTML. В вашем интерфейсе можно использовать метод toHTML и toPDF. Недостатком этого подхода является то, что теперь ваши классы зависят от ваших библиотек для генерации HTML и PDF. Кроме того, если кто-то позже хочет добавить новый тип вывода, им необходимо изменить эти основные классы, что может быть нежелательным. С шаблоном посетителя только классы vistior должны знать о библиотеках PDF или HTMl, а новые посетители могут быть добавлены без изменения основных классов. (Но опять же, добавление новых основных классов означает, что вам нужно либо повторно использовать существующий метод visit*, либо вам придется изменить все вариантов реализации посетителя.)

+0

Я не был уверен, что мой подход к отправке (расширенный для посетителя) был слишком большим для этой простой проблемы, но теперь вы подтвердили мои мысли. будет использовать этот подход. Большое спасибо. – zersaegen

+0

еще один маленький вопрос: есть ли способ использовать посетителя, когда я не контролирую реализацию MySuperInterface. например, когда я хочу обрабатывать KeyEvents или что-то подобное? – zersaegen

+0

@zersaegen Шаблон посетителей - это своего рода двойная отправка. Первая отправка осуществляется путем вызова (полиморфного) 'acceptVisitor'. Вторая отправка выполняется acceptVisitor, вызывающим метод 'visit *'. Первая отправка - это тот же подход абстрактного метода, который предлагает assylias, и добавление в интерфейс посетителя дает нам (полезный) дополнительный слой косвенности. Я указываю это, потому что ... –

2

Вашего описания немного расплывчатый, но если у вас есть несколько подклассов, некоторые из которых имеет общее «ручку» поведение, это может работать - если у вас есть только 2 подклассов и не планируете иметь больше в будущем, Конспект шаг, вероятно, нет необходимости:

public interface MySuperInterface { 
    void handle(); 
} 

public abstract AbstractMySuperInterface { 
    public void handle() { 
     //implement default behavior 
    } 
} 

public class SubclassA implements MySuperInterface { 
    //nothing here, just use default behavior 
} 

public class SubclassB implements MySuperInterface { 
    public void handle() { 
     //implement another behavior 
    } 
} 

public class MySuperHandler { 

    public void handle(MySuperInterface mysuper) { 
     mysuper.handle(); 
    } 
} 
+0

спасибо. да, это дополнительный подход. что мне не нравится в этом решении, так это то, что интерфейс определяет, как он в основном обрабатывается. что, если я хотел бы добавить другого обработчика? – zersaegen

+0

В своем вопросе вы упомянули, что вам нужно будет использовать коммутатор - я полагаю, это означает, что каждый подкласс имеет определенное поведение, которое должно быть реализовано на уровне подкласса. Теперь, может быть, ваш метод обработки может быть разделен - одна часть, где объект делает что-то конкретное, и другую часть, которая является общей и не контролируемой объектом. Не зная больше аобута вашего интерфейса и какой дескриптор трудно быть конкретным ... – assylias

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