设计模式-观察者模式
1. OOA and OOD 1.1 模拟程序
- 小孩在睡觉
- 醒来后要求吃东西
- 思路1
一个睡着的小孩(Kid), 一个人(Parent)专门看着这个小孩醒没醒(thread)。 - 代码实现
- 思路1
package com.alan.planA;
class Child implements Runnable {
private boolean flag;
public boolean isWakenUp() {
return flag;
}public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
}
}class Dad implements Runnable {
private Child c;
public Dad(Child c) {
this.c = c;
}public void run() {
while (!c.isWakenUp()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
feed(c);
}private void feed(Child c2) {
System.out.println("feed child.");
}
}public class FeedChildSimulator {
public static void main(String[] args) {
Child c = new Child();
Dad d = new Dad(c);
Thread thread1 = new Thread(d);
Thread thread2 = new Thread(c);
thread1.start();
thread2.start();
}
}
- 思路1的方式可行,但会无端的耗费太多的CPU资源,如果是一个人看孩子的话,那基本上没有时间干其他的事情了。所以,引申出思路2。
- 思路2
监听孩子醒了这件事情。主动监听会消耗CPU资源。最好的设计就是在小孩胳膊上绑一绳,他一醒过来,自动拉一下那个绳子就可以了,这时候Parent过来喂他就可以了。反客为主。 - 代码实现
package com.alan.planB;
class Child implements Runnable {
private boolean flag;
private Dad d;
public Child(Dad d) {
this.d = d;
}public void isWakenUp() {
flag = true;
d.feed(this);
}public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
isWakenUp();
}
}class Dad {
public void feed(Child c2) {
System.out.println("feed child.");
}
}public class FeedChildSimulator {
public static void main(String[] args) {
Dad d = new Dad();
Child c = new Child(d);
Thread thread = new Thread(c);
thread.start();
}
}
- 思路三
不同醒了的方式,他爸爸处理的方式也应该是不同的。事情的发生是包含着一些具体情况的。当孩子醒了的时候,应该把事情的具体情况(事件<事情的发生时间,地点等>)告诉他爸爸。该怎么做,用什么样的设计方法。 - 代码实现
package com.alan.planC;
class WakenUpEvent {
private long time;
private String location;
private Child source;
public WakenUpEvent(long time, String location, Child source) {
super();
this.time = time;
this.location = location;
this.source = source;
}public long getTime() {
return time;
}public void setTime(long time) {
this.time = time;
}public String getLocation() {
return location;
}public void setLocation(String location) {
this.location = location;
}public Child getSource() {
return source;
}public void setSource(Child source) {
this.source = source;
}}class Child implements Runnable {
private Dad d;
public Child(Dad d) {
this.d = d;
}public void isWakenUp() {
d.actionToWakenUp(new WakenUpEvent(System.currentTimeMillis(), "bed", this));
}public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
isWakenUp();
}
}class Dad {
public void actionToWakenUp(WakenUpEvent wakenUpEvent) {
// 根据时间,地点,事件源来具体做出处理
System.out.println("feed child.");
}
}public class FeedChildSimulator {
public static void main(String[] args) {
Dad d = new Dad();
Child c = new Child(d);
Thread thread = new Thread(c);
thread.start();
}
}
- 如果此时,当小孩醒了,除了他爸爸要做出响应之外,他爷爷,他奶奶...也要做出响应,应该如何是好,按照思路3的方式,小孩手上的线会越来越多。这说明思路3的这种设计也是不好的,接下来就引出思路4。
- 思路4
将变化的事件进行抽象,将绳子都按顺序绑在一个棍子上,将监听所有孩子醒了的这件事情的所有监听者做成一个集合。 - 代码实现
package com.alan.planD;
import java.util.ArrayList;
import java.util.List;
class WakenUpEvent {
private long time;
private String location;
private Child source;
public WakenUpEvent(long time, String location, Child source) {
super();
this.time = time;
this.location = location;
this.source = source;
}public long getTime() {
return time;
}public void setTime(long time) {
this.time = time;
}public String getLocation() {
return location;
}public void setLocation(String location) {
this.location = location;
}public Child getSource() {
return source;
}public void setSource(Child source) {
this.source = source;
}}class Child implements Runnable {
private List wakenUpListeners = new ArrayList();
public void addWakenUpListener(WakenUpListener wul) {
wakenUpListeners.add(wul);
}public void isWakenUp() {
for (WakenUpListener l : wakenUpListeners) {
l.actionToWakenUp(new WakenUpEvent(System.currentTimeMillis(), "bed", this));
}
}public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
isWakenUp();
}
}class Dad implements WakenUpListener {
public void actionToWakenUp(WakenUpEvent wakenUpEvent) {
// 根据时间,地点,事件源来具体做出处理
System.out.println("feed child.");
}
}class GrandFather implements WakenUpListener {
public void actionToWakenUp(WakenUpEvent wakenUpEvent) {
// 根据时间,地点,事件源来具体做出处理
System.out.println("hug child.");
}
}/**
* 实现这个接口的类,一定会监听WakenUp醒过来的这件事
*/
interface WakenUpListener {
// 对这样的事件进行响应,事件本身和事件源脱离 ,灵活度最高
public void actionToWakenUp(WakenUpEvent wakenUpEvent);
}public class FeedChildSimulator {
public static void main(String[] args) {
Dad d = new Dad();
GrandFather gf = new GrandFather();
Child c = new Child();
c.addWakenUpListener(d);
c.addWakenUpListener(gf);
Thread thread = new Thread(c);
thread.start();
}
}
- 基于JDK中Observer接口和Observable类实现观察者模式
package design.pattern.behavior.observer;
import java.util.Observable;
public class ConcreteSubject extends Observable {
private int state;
public int getState() {
return state;
}public void setState(int state) {
this.state = state;
this.setChanged();
this.notifyObservers();
}
}package design.pattern.behavior.observer;
import java.util.Observable;
import java.util.Observer;
public class MyObserver implements Observer {private int state;
public int getState() {
return state;
}public void setState(int state) {
this.state = state;
}public void update(Observable o, Object arg) {
state = ((ConcreteSubject)o).getState();
}
}package design.pattern.behavior.observer;
public class Client {
public static void main(String[] args) {
MyObserver o1 = new MyObserver();
MyObserver o2 = new MyObserver();
MyObserver o3 = new MyObserver();
System.out.println(o1.getState());
System.out.println(o2.getState());
System.out.println(o3.getState());
ConcreteSubject s = new ConcreteSubject();
s.addObserver(o1);
s.addObserver(o2);
s.addObserver(o3);
s.setState(3000);
System.out.println("state changed...");
System.out.println(o1.getState());
System.out.println(o2.getState());
System.out.println(o3.getState());
}
}
1.2 弄清楚用户到底要什么, 具体什么要求。
- 甲方: 我说:“我要一房子”。
- 乙方: 上去直接把房子盖了出来。正正方方的。
- 甲方: 我说我要的是一碉堡。
- 乙方: 此时要哭的心都有了。
此时的设计一定会完蛋,因为这是你以为的房子,不是客户想要的。在真正设计一个软件系统的时候,最重要的事情是什么啊?- 弄明白客户的具体需求到底是什么样子的!
你要的房子要建成一个什么样子的呢? - 注意:越是钻到技术中拔不出来的人,这方面越是欠缺。当你的代码量有一定量的时候,你会注意到,要做好一个软件系统,首先你要保证第一点,就是你在开头上不能错。开头错了就是南辕北辙了。所以最重要的是弄明白客户的要求到底是什么。弄明白别人要什么的这个过程叫做分析。
- 分析(要做什么)是弄明白客户要干什么,要弄明白你的软件系统要实现的功能是什么
- 设计(这个功能怎么实现): 要达成同一功能,会有不同的方法。
- 确定功能 > 用啥样的结构(合适的,具体问题具体分析)去实现这个功能。
- 灵活一点,再灵活一点。
- 设计模式刚开始可以往死里学,熟悉招式,知道其使用场景;下一个层次是想办法把招式穿起来,灵活运用;最高境界就是设计模式的名字忘了,让我设计,我依然可以设计的很到位。心中无招,手中也无招。但是以上都是建立在每一招你都非常熟悉的基础之上。
- 以后在遇到问题时候,首先要想清楚问题的本身你有没有搞清楚。如果没有弄清楚,首先不要一下子就钻到技术层面去。确定需求的时候用什么语言其实已经不重要了。
- 所以,一个真正的软件项目,首先第一步要做的是确定这个需求。怎么确定需求?客户不一定明白自己想要什么?婚姻的不幸就是由小小需求的不确定慢慢积累起来的。
- 面向对象设计:
怎样从需求中探索出我所需要的类,需不需要加各种各样的辅助类,到底哪些是实体类,哪些是管理的类,哪些是工具类。需不需要由各种的线程,需要什么样的架构等等。
实体类(属性):找需求文档中的名词。
方法:动词。
一个类的属性进行只让自己读写。 - 在软件的设计过程中,如果仅考虑到当前,这个设计会没有弹性,不具备可扩展性。预想到会有变化可以设计得出来,否则设计不出来。透彻的理解需求,想想需求未来会发生什么样子的变化。所以预想不到未来的需求,你的设计就做不到位,这是很重要的一点。假如说,预想的需求过了头了,也不行,叫过度设计(over design)。在一段时间内预想到一定时间的变化就够了,不要想太多了。设计功能的时候要想到你将来的扩展点在什么地方。
- 将变化的事件进行抽象。
- 做到Main方法不变,读配置文件的形式。
- AWT
- 用设计模式,是有成本的
- 企业开发中实现需求,在有限时间完成功能最重要。
- 先将功能实现,然后重构(refactor)。
- 弄明白客户的具体需求到底是什么样子的!
推荐阅读
- --木木--|--木木-- 第二课作业#翼丰会(每日一淘6+1实战裂变被动引流# 6+1模式)
- 设计模式-代理模式-Proxy
- 【译】Rails|【译】Rails 5.0正式发布(Action Cable,API模式等)
- java静态代理模式
- VueX(Vuex|VueX(Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式)
- Kotlin基础(10)-代理模式在kotlin中的使用
- 长谈的确是这个时代需要的一种模式
- 《读_Head_First_有感》_“命令模式”
- 洗洗睡了|洗洗睡了 | 休息的时候,千万注意切换你的行为模式
- 设计模式【15】--从审批流中学习责任链模式