2016-06-14 6 views
2

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

В псевдокоде Java мой класс:

public class MyClass<E> extends Observable 
{ 
    private ArrayList<E> items = new ArrayList<E>(); 

    public MyClass() 
    { 
    } 

    public void add(E e) 
    { 
     this.items.add(e); 

     setChanged(); 
     notifyObservers(); 
    } 

    public void remove(E e) 
    { 
     if(this.items.remove(e)) 
     { 
      setChanged(); 
      notifyObservers(); 
     } 
    } 

    ... 
} 

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

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

Итак, мой вопрос: что является лучшим способом для разработки кода для уведомления объектов-наблюдателей только один раз после массивной модификации наблюдаемого объекта?

+1

В этом случае я считаю, что лучший способ - создать два метода deleteAll и addAll, чтобы вы уведомили наблюдателей в конце операции. –

ответ

2

Я думаю, что у вас есть 2 общие параметры

  • пусть клиентский код явно отключить уведомления
  • инкапсулировать изменения в собственном классе и использовать метод шаблона

Давайте посмотрим на оба варианта. Для моих примеров я буду использовать простой Observer.

public class StdoutObserver implements Observer { 
    @Override 
    public void update(Observable o, Object arg) { 
    System.out.println("Observable changed: " + o); 
    } 
} 

код клиента явно отключает уведомления

public class MyClass<E> extends Observable { 
    private ArrayList<E> items = new ArrayList<E>(); 

    private boolean changing; 

    public void setIsChanging(boolean changing){ 
    this.changing = changing; 
    if(!changing && hasChanged()){ 
     notifyObservers(); 
    } 
    } 

    public void add(E e) { 
    this.items.add(e); 
    notifyChanged(); 
    } 

    public void remove(E e) { 
    if (this.items.remove(e)) { 
     notifyChanged(); 
    } 
    } 

    private void notifyChanged(){ 
    setChanged(); 
    if(!changing){ 
     notifyObservers(); 
    } 
    } 
} 

и клиентский код выглядит следующим образом

MyClass<String> stringMyClass = new MyClass<String>(); 
stringMyClass.addObserver(new StdoutObserver()); 

System.out.println("setting changing: true"); 
stringMyClass.setIsChanging(true); 

stringMyClass.add("C"); 
stringMyClass.add("D"); 
stringMyClass.add("E"); 

System.out.println("setting changing: false"); 
stringMyClass.setIsChanging(false); 

выход будет

setting changing: true 
setting changing: false 
Observable changed: [email protected] 

Проблема с этим решением заключается в том, что код клиента может забыть включить уведомления (или сообщить наблюдаемым, что изменения сделаны). В этом случае уведомления могут быть отключены навсегда.

Инкапсулируйте изменения в собственном классе и использовать метод шаблона

Определить интерфейс для отображения изменений

public interface ObservableChange<T extends Observable> { 
    public void doChange(T observable); 
} 

вашего Observable затем будет использовать метод шаблона для того, чтобы changing состояния сбрасывается при выполнении изменения.

public class MyClass<E> extends Observable { 

    private ArrayList<E> items = new ArrayList<E>(); 

    private boolean changing; 

    public void update(ObservableChange<MyClass2<E>> observableChange) { 
    setIsChanging(true); 
    try { 
     observableChange.doChange(this); 
    } finally { 
     setIsChanging(false); 
    } 

    } 

    private void setIsChanging(boolean changing) { 
    this.changing = changing; 
    if (!changing && hasChanged()) { 
     notifyObservers(); 
    } 
    } 

    public void add(E e) { 
    this.items.add(e); 
    notifyChanged(); 
    } 

    public void remove(E e) { 
    if (this.items.remove(e)) { 
     notifyChanged(); 
    } 
    } 

    private void notifyChanged() { 
    setChanged(); 
    if (!changing) { 
     notifyObservers(); 
    } 
    } 
} 

Клиентский код будет выглядеть следующим образом;

MyClass<String> stringMyClass = new MyClass<String>(); 
stringMyClass.addObserver(new StdoutObserver()); 


stringMyClass.update(new ObservableChange<MyClass<String>>() { 
    @Override 
    public void doChange(MyClass<String> observable) { 
    observable.add("A"); 
    observable.add("B"); 
    observable.add("C"); 
    observable.add("D"); 
    } 
}); 

и выход будет

Observable changed: [email protected] 

Используя метод шаблона гарантирует, что уведомления только отключены до тех пор, как изменения сделаны, потому управления наблюдателями это.

0

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

Это может быть изменен путем предоставления дополнительные методов, как в:

public void doSomeMassiveOperation(args) { 
    do massive things directly on your list 
    notify listeners 

Другие, гораздо более хака, неправильно, не-быть использован подход будет расширять свой интерфейс таким образом:

public void disableNotifications() { 
    this.notificationsEnabled = false; 
} 

public void enableNotificationsAndNotify() { 
    this.notificationsEnabled = true; 
    notify ... 
} 

public void add(E e) { 
    add ... 
    if (notificationsEnabled) { 
    notify 
} 

Смысл: вы разрешаете пользователям вашего класса контролировать «когда» появляются уведомления. Но это в основном означает, что вы переносите эту ответственность на свой клиентский код.

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

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

+0

Массивные операции оказывают серьезное влияние: один из наблюдателей должен сохранить список на диске и затем загрузите его на Google Диск. – Carlo

+0

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

+0

Благодарим вас за предложение. Можете ли вы предоставить мне некоторые ссылки или примеры из них, пожалуйста? – Carlo

0

Вы можете добавить методы MyClass:

public class MyClass<E> extends Observable { 
    public void addAll(Collection<? extends E> e) { 
     this.items.addAll(e); 

     setChanged(); 
     notifyObservers(); 
    } 

    public void removeAll(E e) { 
     if(this.items.removeAll(e)) { 
      setChanged(); 
      notifyObservers(); 
     } 
    } 

    //UPDATE: 
    //you can define general purpose method 

    public void modifyItems(Runnable modifier) { 
     disableNotification(); 
     modifier.run(); 
     enableNotification(); 
     notifyObservers(); 
    } 

    private void disableNotification() { ... } 
    private void enableNotification() { ... } 
} 

... 
myClass.modifyItems(
    () - { 
     myClass.add(item); 
     myClass.remove(item); 
     ... 
); 

два вызова метода notifyObservers() не может вызывать никаких issuies производительности.

+0

Я тоже подумал над этим решением, но мой вопрос был, если это лучший способ справиться с этой проблемой – Carlo

0

Мне нравится Рене Ссылка ответ. Но его «Клиентский код явно отключает уведомления» метод имеет 2 недостатка:

1) stringMyClass.setIsChanging(false) вызов можно избежать по какой-то причине (исключения, плохой код и т.д.)

2) Если некоторые другие отдельных элементов (из сгустка обновление) должно быть добавлено/удалено в процессе обновления массивных сгустков - это также не будет работать

Romeo Sheshi прокомментировал простое и эффективное решение: использовать deleteBulk(), addBulk() методу с notifyChanged() методом в нем.

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