Очень обычно у меня есть ситуация, где данный объект должен будет иметь многих слушателей. Например, я мог бы иметь
class Elephant {
public void addListener( ElephantListener listener ) { ... }
}
но у меня будет много таких ситуаций. Таким образом, у меня также будет a Tiger
объект это будет иметь TigerListener
s. Теперь, TigerListener
s и ElephantListener
s очень отличаются:
interface TigerListener {
void listenForGrowl( Growl qrowl );
void listenForMeow( Meow meow );
}
в то время как
interface ElephantListener {
void listenForStomp( String location, double intensity );
}
Я нахожу, что всегда должен продолжать повторно реализовывать широковещательный механизм в каждом классе животных, и реализация всегда является тем же. Существует ли предпочтительный шаблон?
Я думаю, вы делаете это правильно, поскольку ваши интерфейсы имеют семантическую ценность и выражают то, что они слушают (например, рычание и мяуканье вместо топания). При общем подходе вы можете повторно использовать широковещательный код, но вы можете потерять удобочитаемость.
Например, существует java.beans.PropertyChangeSupport
, которая представляет собой служебную программу для реализации Оберсервера, отслеживающего изменения значений. Он выполняет широковещательную передачу, но вам все равно необходимо реализовать метод в своем доменном классе и передать его объекту PropertyChangeSupport. Методы обратного вызова сами по себе бессмысленны, а транслируемые события основаны на String:
public interface PropertyChangeListener extends java.util.EventListener {
void propertyChange(PropertyChangeEvent evt);
}
Другой - java.util.Observable
, который обеспечивает механизм широковещания, но это тоже не самое лучшее, imho.
Мне нравится ElephantListener.onStomp ()
Вместо того, чтобы каждый Listener
имел определенные методы для каждого типа события, вы можете его отправить, измените интерфейс, чтобы он принимал общий класс Event
. Затем вы можете подклассифицировать Событие
для определенных подтипов, если вам нужно, или сделать так, чтобы оно содержало состояние, такое как , двойная интенсивность
.
TigerListener и ElephentListener затем становятся
interface TigerListener {
void listen(Event event);
}
Фактически, затем вы можете дополнительно преобразовать этот интерфейс в простой Listener
:
interface Listener {
void listen(Event event);
}
Ваши реализации Listener
могут затем содержать логику, которая они нужны для конкретных событий, о которых они заботятся
class TigerListener implements Listener {
@Overrides
void listen(Event event) {
if (event instanceof GrowlEvent) {
//handle growl...
}
else if (event instance of MeowEvent) {
//handle meow
}
//we don't care about any other types of Events
}
}
class ElephentListener {
@Overrides
void listen(Event event) {
if (event instanceof StompEvent) {
StompEvent stomp = (StompEvent) event;
if ("north".equals(stomp.getLocation()) && stomp.getDistance() > 10) {
...
}
}
}
}
Ключевыми отношениями между подписчиком и издателем является то, что издатель может отправлять события подписчикам, не обязательно, что он может отправлять ему определенные типы событий - этот тип рефакторинг выталкивает эту логику из интерфейса в конкретные реализации.
Другой вариант - Whiteboard Pattern. В этом случае издатель и подписчик отделены друг от друга, и ни один из них не будет содержать никакого широковещательного кода. Они оба просто используют механизм обмена сообщениями для pub/sub, и ни один из них не имеет прямой связи с другим.
Это обычная модель для обмена сообщениями в платформе OSGi.