发布订阅观察者

2019-04-13  本文已影响0人  伟光_d040


Q1? 为什么叫观察者模式?

表明了只能观察,只能get,不能set,这是一种规范,一种提倡,当然使用反射可以随意搞.

以下举例说明:

1.此处代码框架实现,观察者众多.采用接口或注解方式均可,

```

//被观察者统一接口,如果不统一,则无法循环调用,导致中间层无法动态添加被观察者

//但是,是否需要动态添加被观察者并不是观察者模式的核心功能和要点

public interface Passer {

void react(SignNow signNow, TrafficLights trafficLights);

}

```

2.此处代码框架实现,被观察者一个.

```

import java.util.Vector;

public class TrafficLights {

//红绿灯的编号

    private Stringcode ;

    //红绿灯的尺寸

    private Integersize;

    private SignNowsignNow;

    //动态持有被观察则,可以随时增删,不是核心功能.

    private Vectorpassers;

    //被观察者充当中间层,依次调用观察者的方法.

    public void send(SignNow signNow){

this.signNow =signNow;

        passers.stream()

.forEach(passer -> {

passer.react(signNow,this);

        });

    }

public StringgetCode() {

return code;

    }

public void setCode(String code) {

this.code = code;

    }

public IntegergetSize() {

return size;

    }

public void setSize(Integer size) {

this.size = size;

    }

public SignNowgetSignNow() {

return signNow;

    }

public void setSignNow(SignNow signNow) {

this.signNow = signNow;

    }

public VectorgetPassers() {

return passers;

    }

public void setPassers(Vector passers) {

this.passers = passers;

    }

}

```

其中代表主要信息类的SignNow简单用两个字段表示,实际情况下可能是对应实体类DTO等.

```

//被观察者的一部分,也可以认为是经常改变的一部分,甚至是观察者的主要观察点.

public class SignNow {

private Stringcolor;

    private Integerduration;

    public SignNow(String color, Integer duration) {

this.color = color;

        this.duration = duration;

    }

public StringgetColor() {

return color;

    }

public void setColor(String color) {

this.color = color;

    }

public IntegergetDuration() {

return duration;

    }

public void setDuration(Integer duration) {

this.duration = duration;

    }

}

```


以上都是框架写的,接下来是程序员的工作了,我写在一个地方了,实在懒得新建class了,

其实可以用函数式编程的

```

//程序员写代码的区域,本包其余三个类是框架完成的区域

public class CoderContext {

//此处是三个观察者,真正的被调用端,也是程序员自由发挥的空间

    public static class Zhangsanimplements Passer{

@Override

        public void react(SignNow signNow, TrafficLights trafficLights) {

System.err.println(signNow.hashCode());

            System.out.println("zhangsan :what? it is "+signNow.getColor());

        }

}

public static class Lisiimplements Passer{

@Override

        public void react(SignNow signNow, TrafficLights trafficLights) {

System.err.println(signNow.hashCode());

            System.out.println("lisi : very happy to see this color "+signNow.getColor());

        }

}

public static class Wangwuimplements Passer{

@Override

        public void react(SignNow signNow, TrafficLights trafficLights) {

System.err.println(signNow.hashCode());

            System.out.println("wangwu : I am strange focus on duration : "

                    +signNow.getDuration()+" and size :"+trafficLights.getSize());

        }

}

//此处由初始化理应由配置声明完成或者框架完成

    @Before

    public void setUp(){

trafficLights =new TrafficLights();

        trafficLights.setPassers(new Vector(){{

add(new Zhangsan());

            add(new Lisi());

            add(new Wangwu());

        }});

        trafficLights.setCode("030201");

        trafficLights.setSize(100);

    }

private TrafficLightstrafficLights;

    //此处是调用端 一行代码搞定

    @Test

    public void test01(){

//红灯停三十秒钟,假设交通指挥中心 主动手动发信号

        trafficLights.send(new SignNow("red",30));

    }

}

```

以下是运行结果,我们看一下

zhangsan :what? it is red

lisi : very happy to see this color red

wangwu : I am strange focus on duration : 30 and size :100

2012232625

2012232625

2012232625


重点是分析,本质上被观察者,无论是单纯的信息类SignNow 还是被整个观察者对象,

他们的引用都会被给到zhangsan,lisi,wangwu中去.而循环是有顺序的,如果zhangsan在前面

,他的react反应是看到红灯,生气了,用某种意念力改变灯的颜色 即运行了这么一行

```

public static class Zhangsanimplements Passer{

@Override

    public void react(SignNow signNow, TrafficLights trafficLights) {

System.err.println(signNow.hashCode());

        System.out.println("zhangsan :what? it is "+signNow.getColor());

        signNow.setColor("green");

    }

}

```

那么李四看到的灯将是绿色的.所以就造成了程序出错.原vector在张三之前的人不会被干扰,在之后的都会被干扰.

这是应该避免的现象,如何避免

成本最低的方法,程序员均遵循观察者模式理念写代码,被观察对象均不要取set.

对于初级程序员怎么办? 可以用接口限制提示

如此类中 可以设计react接口将传入信息类如SignNow做一层包装,包装类通常被成为事件.事件的意思也是,已经发生了,只能看,不能改变,时间一去不复返,只能读不能写.如下SignNowEvent 有两个方法 GetSignColor 和GetSignDuration
```

public class SignNow implements SignNowEvent{

@Override

public String getSignColor() {

return this.color;

}

@Override

public Integer getSignDuration() {

return this.duration;

}

//新增部分

}

```

同样要定义被观察者接口 reactEx(SignNowEvent  e)

```

//此处是三个观察者,真正的被调用端,也是程序员自由发挥的空间

public static class Zhangsanimplements Passer{

@Override

    public void react(SignNow signNow, TrafficLights trafficLights) {

System.err.println(signNow.hashCode());

        System.out.println("zhangsan :what? it is "+signNow.getColor());

        signNow.setColor("green");

    }

@Override

    public void reactEx(SignNowEvent signNowEvent) {

System.out.println("zhangsan :what? it is "+signNowEvent.getSignColor());

    }

}

public static class Lisiimplements Passer{

@Override

    public void react(SignNow signNow, TrafficLights trafficLights) {

System.err.println(signNow.hashCode());

        System.out.println("lisi : very happy to see this color "+signNow.getColor());

    }

@Override

    public void reactEx(SignNowEvent signNowEvent) {

System.out.println("lisi : very happy to see this color "+signNowEvent.getSignColor());

    }

}

public static class Wangwuimplements Passer{

@Override

    public void react(SignNow signNow, TrafficLights trafficLights) {

System.err.println(signNow.hashCode());

        System.out.println("wangwu : I am strange focus on duration : "

                +signNow.getDuration()+" and size :"+trafficLights.getSize());

    }

@Override

    public void reactEx(SignNowEvent signNowEvent) {

System.out.println("wangwu : I am strange focus on duration : "

                +signNowEvent.getSignDuration());

    }

}

```

这样,初级程序员就在写代码时候发现signNowEvent只有两个get方法,没有set方法,就不会乱更改了.当然用强制转化子类或实现类可以set改变.只是温柔善意的限制.



除此之外呢

clone,对的

为何不循环发布的时候就发布一个副本呢.这样彼此互不关心.互不干扰可以随意set.

重写循环vector调用

```

public class SignNowimplements SignNowEvent,Cloneable {

@Override

    protected Objectclone()throws CloneNotSupportedException {

return super.clone();

    }

```

```

public void cloneAndSend(SignNow signNow){

this.signNow = signNow;

    passers.stream()

.forEach(passer -> {

try {

SignNow s = (SignNow)signNow.clone();

                }catch (CloneNotSupportedException e) {

e.printStackTrace();

                }

passer.react(signNow,this);

            });

}

```

这样设计之后观察者端就可以随意set了.不过这并不是观察者模式的本意

观察者模式的本意是,尽量通过接口Event将被观察者的实体类向上转型提示程序员只能调用

或者程序员遵循观察者模式中只get不set的原则,如有需要,观察者回调函数中自行需要时去clone.


Q2?为何很无聊的提出clone?

clone有浅拷贝深拷贝,在vue react等单向数据流中,往往会涉及到,有一种拷贝方式是先

JSON.stringfy()将对象序列化成字符串,然后反序列化结果就是一次信息拷贝.

所以,java clone 也可以使用objectMapper这样玩.

提到对象->字符串->对象,就不得不说文体两开花,http请求等远程调用,或者应用程序之间或者跨网络传递对象,就是如此.

所以一旦是不同应用程序或网络远程调用,那么序列化反序列化是必然的.

此时观察者模式似乎自带了clone效果,不再受限于只能get不能set,也由此改了个名字叫

发布订阅模式,

自己订阅的报纸,想撕了也是可以的嘛!

下一回说书进入springapplicationContext.pulishEvent 和Websocket-Stomp和Rabbitmq的发布订阅模式

上一篇 下一篇

猜你喜欢

热点阅读