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的发布订阅模式
网友评论