Обозреватель дизайн - как получить доступ к method.invoke области? - PullRequest
1 голос
/ 09 ноября 2010

У меня в настоящее время есть настройка Java Observer / Observable, в которой я включаю какое-то поле в параметре Object Observer.update (например, идентификатор события), чтобы определить, как обрабатывать уведомление Observable.

это создает многословныйкод вроде:

public void update (Observable o, Object arg) {
    if (arg instanceof Event) {
        switch (((Event)arg).getID()) {
            case EVENT_TYPE_A:
                // do stuff...
                break;
            case EVENT_TYPE_B:
                // do stuff...
                break;
            case EVENT_TYPE_C:
                // do stuff...
                break;
        }
    }
}

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

я мог бы передать ссылку на экземпляр, содержащий метод, ноэто пахнет как плохой ООП.

я лаю не на том дереве?или есть чистый способ добиться этого?

Ответы [ 2 ]

2 голосов
/ 02 апреля 2011

Это может быть немного далеко в левом поле, но, поскольку Java 5 и выше имеют обобщенные элементы, традиционные шаблоны наблюдателя и слушателя кажутся немного устаревшими.То есть типы являются лингва-фракой Java в наши дни.События с целочисленными идентификаторами существуют главным образом потому, что операторы switch для констант чрезвычайно эффективны - за счет читабельности и часто требуют, чтобы приведения делали что-нибудь полезное (вы можете знать, что если ID = 23, то объект должен быть MouseEvent, но это лучшеи безопаснее, если вы позаботитесь об этом для информации о типе компилятора и времени выполнения).На современной машине в современной JVM эффективность может не стоить того.

Итак, если вы не состоите в браке с идентификаторами и традиционным шаблоном наблюдения, вы можете рассмотреть что-то вроде этого:

public abstract class Observer<T> {
  private final Class<T> type;

  protected Observer(Class<T> type) {
    this.type = type;
  }

  //implement this method;  if it returns false, the event (object) 
  //is "consumed" and no other observers should be called
  public abstract boolean onEvent(T instance);

  protected final boolean matches(Object obj) {
    return type.isInstance(obj);
  }

  Boolean maybeDispatch(Object o) {
    if (matches(o)) {
      return onEvent(type.cast(o));
    }
    return null;
  }
}

Это дает нам (в буквальном смысле) общий наблюдатель событий;мы включаем тип передаваемого объекта следующим образом:

public class Bus {
  private final List<Observer<?>> observers = new ArrayList<Observer<?>>();

  public void registerObserver(Observer<?> observer) {
    observers.add(observer);
  }

  public <T> void onEvent(T t) {
    Boolean keepGoing;
    for (Observer<?> obs : observers) {
      keepGoing = obs.maybeDispatch(t);
      if (keepGoing != null && !keepGoing.booleanValue()) {
        break;
      }
    }
  }
}

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

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

1 голос
/ 02 апреля 2011

Более чистая реализация будет включать удаление логики того, может ли событие быть обработано наблюдателем / наблюдаемой, фактическому наблюдателю / самой наблюдаемой.Похоже, что ActionScript оставил вам забавное представление о шаблоне Observer.Соблюдайте (не каламбур):

public interface Observer{

  public void update( Event arg );
}

public class Event{

  public int ID;
}

public Button implements Observer{

  public void update ( Event arg ){

     switch (arg.ID){

       case 1:  //Buttonsy handle events of type 1
         //do something useful;
         break;
       default:
         System.out.println("Buttons don't handle events of ID: " + arg.ID);
         break;
     }
  }
}

public ProgressBar implements Observer{

  public void update ( Event arg ){

     switch (arg.ID){

       case 2: //ProgressBars handle events of type 2 and 3
         //do something useful;
         break;
       case 3:
         //do something else useful;
         break;
       default:
         System.out.println("Progress bars don't handle events of ID: " + arg.ID);
         break;
     }
  }
}


public class Subject{

 private ArrayList<Observer> allUIControls;

 public registerControl( Observer control ){

   allUIControls.add( control );
 }

 public void updateControls ( Event arg ) {

   foreach ( Observer control in allUIControls ){

     //pass the event to each UI control, and let the EVENT decide if it can understand the Event.ID
     //its not the job of Subject to decide if the Observer is fit to handle the event. THIS IS NOT THE OBSERVER pattern.
     control.update( arg );
   }
 }
}
...