Обработка всех подтипов супертипа - PullRequest
0 голосов
/ 22 марта 2012

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

Ситуация следующая:

Супертип:

public interface MySuperInterface {
}

Подтип

public class SubclassA implements MySuperInterface {
}

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

public class SubclassB implements MySuperInterface {
}

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

public class MySuperHandler {

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

}

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

  1. оператор switch / case в методе обработчика. (что мне не нравится)

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

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

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

есть ли варианты справиться с этой ситуацией?

спасибо

Ответы [ 2 ]

3 голосов
/ 22 марта 2012

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

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 и его реализации знали о ваших конкретных обработчиках.

Кстати, вместо вызова:

myHandler.handle(myImpl);

вы бы позвонили:

myImpl.acceptVisior(myHandler);

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

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

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

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

2 голосов
/ 22 марта 2012

Ваше описание немного расплывчато, но если у вас есть несколько подклассов, некоторые из которых имеют общее поведение "дескриптора", это может сработать - если у вас есть только 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();
   }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...