Не приведен шаблон наблюдателя с использованием дженериков - PullRequest
0 голосов
/ 10 июня 2018

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

public class Subject<T extends Subject> {

    /** suporting stuff for subject */

    private List<Observer<T>> observers = new ArrayList<>();

    protected void doNotify() {
        for(Observer<T> observer : observers) {
            /** This is the line where it gets interesting */
            observer.update((T)this);
        }
    }
}

На практике эта работа, однако, компилятор выдает предупреждение Unchecked cast в строке observer.update((T)this);.

Читая немного об этом,Компилятор прав (неожиданный сюрприз), и его даже считают вонючим кодом, поскольку вы можете написать код, который на самом деле вызывает ClassCastException .

Теперь я ищу решение, которое не вонючееи твердая.Однако мысль о том, что наблюдателю не нужно искать предмет, за которым он наблюдает, мне очень нравится.Кроме того, мне не очень нравится, что наблюдатели должны сами делать актерский состав в своем update().Есть ли у вас какие-либо предложения о том, как это сделать?


Редактировать

Мой наблюдатель объявлен как интерфейс, подобный этому:

public interface Observer<T> {
    void update(T subject);
}

1 Ответ

0 голосов
/ 10 июня 2018

Предисловие: Я бы предложил НЕ использовать дженерики, потому что, заставляя клиентов (наблюдателей) знать точный тип Субъекта (в отличие от наличия неуниверсального класса Субъекта), вы не можете, например.подписать одного и того же наблюдателя на несколько субъектов (разных типов).

В любом случае, существует способ обеспечения безопасности типов.


В вызове observer.update((T)this) вам нужны две вещи: вы хотите передать this наблюдателям;и вы также хотите, чтобы this имел тип T.

. В этот момент, this не гарантированно имеет тип T, конечно, он имеет тип Subject.Но «это» будет иметь тип Т в конкретном Subject классе.Поэтому замените this на getThisSubject() и переместите его вниз в иерархии.В коде:

package stackOv;
import java.util.*;

abstract class Subject<T extends Subject<T>> {
  private List<Observer<T>> observers = new ArrayList<>();
  // returns a reference of this, of type T
  protected abstract T getThisSubject();
  protected void doNotify() {
    for(Observer<T> observer : observers) {
      observer.update(getThisSubject());
    }
  }
  public void addObserver(Observer<T> obs) {
    observers.add(obs);
  }
}

class SubjectA extends Subject<SubjectA> { 
  @Override
  protected SubjectA getThisSubject() {
    return this;
  }
}

interface Observer<T> {
  void update(T subject);
}

class UseSubject {
  public static void main(String[] args) {
    SubjectA sub = new SubjectA();

    Observer<SubjectA> obs = new Observer<SubjectA>() {
      @Override
      public void update(SubjectA subject) {
        //and here we have a reference to the right type
        System.out.println("subj=" + subject);
      }
    };

    sub.addObserver(obs);
    sub.doNotify();
  }
}

Позвольте мне подчеркнуть, что мы связываем Observer типы с Subject типами;они действительно находятся в отношениях один на один.Чтобы избежать этого, можно объявить Subject неуниверсальным и

interface Observer {
  void update(Subject subject);
}

плюс, используя посетитель или другие шаблоны.(но этот ответ уже достаточно длинный).

...