zoukankan      html  css  js  c++  java
  • Observer一种行为模式

    Observer被称为一种行为模式,用来构成运行中的对象间的关系。在“四人帮”的设计模式声明中提供的定义如下:

    Observer定义了对象间的一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

    Java类库的java.util包中通过Observable类和Observer接口,实现了一个Subject-Observer模式的非通用版本。Observable类包含注册观察者的addObservers方法,用来象征观察对象发生改变的setChanged方法和发生任何改变时用来通知所有观察者的notifyObservers等方法。其中,notifyObservers方法可以接收一个任意类型的对象参数,这个参数将被广播给所有观察者。Observer接口将指定由notifyObservers调用的更新方法。这个方法需要两个参数:第一个参数是Observable的类型,这个是发生改变的内容;第二个是对象类型,是广播的参数。

    当然我们也可以创建属于自己的类,而不是使用类库提供的类。但是当别人来维护我们的代码时,情况会不会很糟糕呢?所以我们还是用类库提供的例子。

    让我们来看一个示例。假设我们家里有四个成员:妈妈、爸爸、一个婴儿和一条狗。当小婴儿哭了的时候,妈妈帮他换尿布,所以我们有一个Baby类的被观察对象和一个Mother类作为Baby的观察者:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class Baby extends Observable {
       private String name;
       public Baby(String name) {
        this.name = name;
       }
       public void babyIsCrying() {
        setChanged();
        notifyObservers(name);
       }
    }
    public class Mother implements Observer {
       @Override
       public void update(Observable subject, Object param) {
        String babyName = (String) param;
        System.out.println("Let's change that diaper on " + babyName + "!");
      }
    }

    当狗叫的时候,爸爸会带它出去走走。所以Dog是另一个被观察者,而Father是它的观察者:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class Dog extends Observable {
       private String name;
       public Dog(String name) {
        this.name = name;
       }
       public void barks() {
        setChanged();
        notifyObservers(name);
       }
    }
    public class Father implements Observer {
       @Override
       public void update(Observable o, Object arg) {
        String dogName = (String) arg;
        System.out.println(dogName + ", let's go to a walk!");
       }
    }

    乍一看,所有的事情都可以正常运行。但是,如果有人理解错了关系,把Mother作为了Dog的观察者(编译器不会报错,运行的时候会默默的为小狗换尿布,并且甚至不会警告我们犯了这么一个严重的错误)

    让我们来验证一下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public class TestingLegacy {
       public static void main(String[] args) {
       Baby david = new Baby("David");
        Mother mom = new Mother();
     
        Dog rover = new Dog("Rover");
        Father john = new Father();
     
        // test mother-baby relationship
        david.addObserver(mom);
        david.babyIsCrying();
        // test father-dog relationship
        rover.addObserver(john);
        rover.barks();
     
        // delete observers to test wrong relatinships
        david.deleteObservers();
        rover.deleteObservers();
     
        System.out.println("Add a wrong relationship and test.");
     
        // add the father as the baby's observer
        david.addObserver(john);
        // add the mother as the dog's observer
        rover.addObserver(mom);
     
        david.babyIsCrying();
        rover.barks();
       }
    }

    控制台输出的结果如下:

    1
    2
    3
    4
    5
    Let's change that diaper on David!
    Rover, let's go to a walk!
    Add a wrong relationship and test.
    David, let's go to a walk!
    Let's change that diaper on Rover!

    为了确保一个Subject-Observer关系被很好的建立,我们应该用Java 5的最好的功能之一:泛型。通过泛型可以让更多的bug在编译时就可以被检测出来,这样可以增加代码的稳定性。但这里有一个小问题,因为Obervable和Observer是Java docs中的一部分,我们不能也不应该改变它们。所以,我们可以去增加绑定,使用泛型签名来创建它们的子类而不是它们本身。通过泛型签名来编译泛型对象,但是运行的代码是子类文件。当代码还没有发布或者由别人来维护代码的时候,这个技术是很合适。

    所以,我们可以通过类型参数S(Subject)和O(Observer)来替换Observable和Observer。这样在Observer的更新方法里,可以调用Subject所支持的所有方法而不需要预先进行类型转换。同样的,在Object的方法签名中我们也常常有机会用到泛型。所以,我们应该增加一个类型参数、一个使它和参数类型一致的参数。

    这是两个泛型的第一版本的存根:

    1
    2
    public class Observable<O extends Observer<?, A>, A> {..}
    public interface Observer<S extends Observable<?, A>, A> {...}

    现在我们创建一个Mother-Baby关系,就可以正常运行了。如果是Mother-Dog会怎样呢?编译是正常的,但是这次在运行的时候抛出了ClassCastExeption(类型转换异常)

    1
    2
    class WrongBoundedBaby extends Observable<WrongBoundedMother, String>
    class WrongBoundedMother implements Observer<Dog, String>
    1
    2
    3
    4
    5
    Exception in thread "main" java.lang.ClassCastException: bounds.WrongBoundedBaby cannot be cast to bounds.Dog
      at bounds.WrongBoundedMother.update(WrongBoundedMother.java:1)
      at java.util.Observable.notifyObservers(Observable.java:142)
      at bounds.WrongBoundedBaby.babyIsCrying(WrongBoundedBaby.java:28)
      at bounds.TestingLegacy.main(TestingLegacy.java:38)

    所以,这显然还不够好,直到运行的时候才检测到了这个Bug。我们需要S在Observable范围内,那样它就可以作为一个参数传递给Observer;并且我们要求O在Observer的范围内,这样就可以作为一个参数传递给Observable。

    1
    2
    class Observable<S extends Observable<S, O, A>, O extends Observer<S, O, A>, A>
    interface Observer<S extends Observable<S, O, A>, O extends Observer<S, O, A>, A>

    这是我们修改后的最终版本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Baby extends Observable<Baby, Mother, String>
    class Mother implements Observer<Baby, Mother, String>
    class Dog extends Observable<Dog, Father, String>
    class Father implements Observer<Dog, Father, String>
     
    // COMPILE- error when adding the father as the baby's observer
    // david.addObserver(john);
    // COMPILE- error when adding the mother as the dog's observer
    // rover.addObserver(mom);

    在”The Java Generics and Collections”中定义了这种相互递归的类型绑定。

    结论:Observer是一种具有很多实现的优秀设计模式,但是对于初级开发人员很容易受到蒙蔽而误用。增加相互递归绑定帮助我们在编译时就发现错误,并且当知道我们可以做得很好时晚上可以睡个好觉。

  • 相关阅读:
    shader之渐变长方体实现(threejs)
    shader之threejs应用
    shader之cesium应用
    pip install -- Failed building wheel for XXX
    pycharm -- 界面乱码
    Android Studio -- 优化速度
    django -- ImageField 上传图片修改头像
    AI -- 回溯法解决四皇后问题
    Android Studio -- 真机测试
    傻瓜函数式编程
  • 原文地址:https://www.cnblogs.com/kuyuyingzi/p/4266318.html
Copyright © 2011-2022 走看看