一、背景                    

请模拟下面情形:
小孩在睡觉,醒来后要求吃东西
 
代码:
class Child{
private boolean wakenUp = false; void wakeUp(){
wakenUp = true;
} public boolean isWakenUp() {
return wakenUp;
}
public void setWakenUp(boolean wakenUp) {
this.wakenUp = wakenUp;
}
} class Dad implements Runnable{
Child c; public Dad(Child c){
this.c = c;
} @Override
public void run() {
while(!c.isWakenUp()){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} feed(c);
} private void feed(Child c) {
System.out.println("feed child");
} } public class Test { public static void main(String[] args) {
Child d = new Child();
new Thread(new Dad(d)).start();
} }

上面代码运行起来是有问题的,永远等在那里,因为child永远不会醒过来;

那么,想让小孩5s之后醒过来怎么做?

二、让小孩5s之后醒          

每一个小孩就是一个单独的线程;
 
代码:
class Child implements Runnable{
private boolean wakenUp = false; void wakeUp(){
wakenUp = true;
} public boolean isWakenUp() {
return wakenUp;
}
public void setWakenUp(boolean wakenUp) {
this.wakenUp = wakenUp;
} @Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.wakeUp();
}
} class Dad implements Runnable{
Child c; public Dad(Child c){
this.c = c;
} @Override
public void run() {
while(!c.isWakenUp()){
System.out.println("child is't wake up");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} feed(c);
} private void feed(Child c) {
System.out.println("feed child");
} } public class Test { public static void main(String[] args) {
Child d = new Child();
new Thread(d).start();
new Thread(new Dad(d)).start();
} }

console:

child is't wake up
child is't wake up
child is't wake up
child is't wake up
child is't wake up
feed child
这个小程序就模拟完了;也就是说要做完这个功能就已经做完了;
但是有什么不合理的地方?
小孩一直在睡着,如果采取现在这种编程模式,Dad就一直监视,无法干其他事情了,就只能在这里死死的盯着他;1s钟盯一眼,太累了;
这个累的意思就是,CPU的资源无端的被消耗了,CPU没事老是在这循环着;如果小孩3小时不醒就得循环3小时;这种编程的方式起码在效率上有问题;
 
那么,怎么修正这个问题呢?
 
 
 
三、第二版的设计              
第二版的设计,不要让Dad在这浪费时间了;
Dad是主动的检测这个小孩有没有醒;主动的监听是非常消耗CPU资源的;
让小孩醒过来的时候自己调我这方法就完了;反过来让小孩监控他Dad;
 
代码:
class Child implements Runnable{
private Dad d; public Child(Dad d){
this.d = d;
} void wakeUp(){
d.feed(this);
} @Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.wakeUp();
}
} class Dad{
public void feed(Child c) {
System.out.println("feed child");
}
} public class Test { public static void main(String[] args) {
Dad d = new Dad();
Child c = new Child(d);
new Thread(c).start();
} }

过5s后,console打印:feed child

同样的功能,这种方式比上面的要效率高一些,消耗的CPU要少一些;

但是作为设计来讲,在一个软件项目之中的设计,如果只考虑到当前这种设计不具有弹性,也就是不具有可扩展性;
什么叫做没有弹性?
比方说设计一个新闻系统,发表一篇文章需要经过三级审查,编辑审了,副主编审,再主编审;如果不符合要求或者含有反动等信息就不能发表出去;
这时候你在程序里就写了三级,假如说将来级别增加了,需要四级审查、五级...怎么办?改源代码就很累;
考虑到可扩展性就可以用一个参数做配置,在配置文件里写3就是三级审查,写4就是四级审查....这样就有弹性一些;
 
上面代码,考虑一个问题:小孩可能会在不同的时间段醒来、不同的地点醒来,针对于不同醒来的事件,Dad的处理方式应该是不同的;这件事情的发生是包含着一些具体情况的,应该把不同的情况告诉他Dad,怎么样把事情的各种各样的信息告诉他Dad呢?
该用什么样的设计方法?
 
 
 
四、对于事件的处理            
对于Dad来说,要根据事件的具体情况,才能做出具体的选择,也就是说作为Child来说,有责任把这件事情的具体情况通知你的监测人;
可以这么写,在Child里面增加什么时候醒来的时间time、醒来的地点loc,然后传给feed()里面的child c;
 
 可以这么写:
class Child implements Runnable{
private Dad d;
String time;
String loc; public Child(Dad d){
this.d = d;
} void wakeUp(){
time = "晚上";
loc = "床上";
d.feed(this);
} @Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.wakeUp();
}
} class Dad{
public void feed(Child c) {
System.out.println("child wake up time :" + c.time);
System.out.println("child wake up location :" + c.loc);
System.out.println("feed child");
}
} public class Test { public static void main(String[] args) {
Dad d = new Dad();
Child c = new Child(d);
new Thread(c).start();
}
}

console:5s后打印:

child wake up time :晚上
child wake up location :床上
feed child
但是:仔细分析,作为time它应该是小孩的属性吗?loc应该是小孩的属性吗?
不是,它们应该是事件本身的属性;是事件发生时候的时间、地点。
所以,面向对象设计的一条最基础的原则:最合适的属性应该放在最合适的类里面;
 
所以:
1)再增加一个事件类:
2)并且,child的wakeUp()方法里面调用Dad的feed()方法,已经写死了,假如child醒过来不想让Dad喂他,而是让Dad带他出去玩呢?
那么feed方法就不合适了。更灵活的方法是:child一醒过来发出一件事,就让Dad对这件事做出反应,只要Dad做出正确的反应就ok了。
所以feed方法换为ActionToWakenUp;

代码:

/**
* 醒过来的事件
*/
class WakenUpEvent{
private long time;
private String loc;
private Child source; //发生事件的源对象 public WakenUpEvent(long time, String loc, Child source) {
super();
this.time = time;
this.loc = loc;
this.source = source;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
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;
} void wakeUp(){
d.ActionToWakenUp(new WakenUpEvent(System.currentTimeMillis(), "bed", this));
} @Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.wakeUp();
}
} class Dad{
public void ActionToWakenUp(WakenUpEvent e) {
System.out.println("child wake up time " + e.getTime());
System.out.println("child wake up location " + e.getLoc());
System.out.println("feed child");
}
} public class Test { public static void main(String[] args) {
Dad d = new Dad();
Child c = new Child(d);
new Thread(c).start();
}
}

console,5s后打印:

child wake up time 1529505152384
child wake up location bed
feed child

上面的设计其实还有问题:

假如说不止Dad,小孩醒过来爷爷也要做出一点反应,该怎么办呢?怎么样让更多的人对这件事做出反应?

五、第三种设计方法          

现在问题是怎么让监听事件的这些个人Dad、GrandFather、奶奶,以及对事件做出响应,怎么能做到比较好的扩展,不需要在Child类里面改来改去;
因为在Child里面改的话:
不但要增加private GrandFather gf;
还需要在wakeUp方法里面增加GrandFather的ActionToWakenUp方法;
还需要修改Child的构造方法,Child(Dad d, GrandFather gf)
...
 
修改为如下:
import java.util.ArrayList;
import java.util.List; public class Test {
public static void main(String[] args) {
Child c = new Child();
c.addWakenUpListener(new Dad());
c.addWakenUpListener(new GrandFather());
new Thread(c).start();
}
} /**
* 发出事件的主体
*/
class Child implements Runnable{
private List<WakenUpListener> WakenUplisteners = new ArrayList<WakenUpListener>(); public void addWakenUpListener(WakenUpListener l){
this.WakenUplisteners.add(l);
} void wakeUp(){
for(WakenUpListener listener : WakenUplisteners){
listener.ActionToWakenUp(new WakenUpEvent(System.currentTimeMillis(), "bed", this));
}
} @Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.wakeUp();
}
} //对应醒事件的监听器接口
interface WakenUpListener {
public void ActionToWakenUp(WakenUpEvent e);
} //Dad监听器
class Dad implements WakenUpListener{
public void ActionToWakenUp(WakenUpEvent e) {
System.out.println("event time: "+e.getTime() +",event location: "+e.getLoc());
System.out.println("feed child");
}
}
//GrandFather监听器
class GrandFather implements WakenUpListener{
public void ActionToWakenUp(WakenUpEvent e) {
System.out.println("event time: "+e.getTime() +",event location: "+e.getLoc());
System.out.println("hug child");
}
} /**
* 醒事件
*/
class WakenUpEvent{
private long time;
private String loc;
private Object source; //发生事件的源对象 public WakenUpEvent(long time, String loc, Object source) {
super();
this.time = time;
this.loc = loc;
this.source = source;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
public Object getSource() {
return source;
}
public void setSource(Object source) {
this.source = source;
}
}

console:

event time: 1529508249412,event location: bed
feed child
event time: 1529508249413,event location: bed
hug child

java设计模式-Observe的更多相关文章

  1. java设计模式- (1)单例模式

    参加校园招聘的笔试,发现公司都会考一些java设计模式,所以上网查询相关内容,总结常用的几种单例模式. 单例模式(Singleton Pattern)是 Java中最简单的设计模式之一.这种类型的设计 ...

  2. JAVA 设计模式 桥接模式

    用途 桥接模式 (Bridge) 将抽象部分与实现部分分离,使它们都可以独立的变化. 桥接模式是一种结构式模式. 结构

  3. java设计模式 策略模式Strategy

    本章讲述java设计模式中,策略模式相关的知识点. 1.策略模式定义 策略模式,又叫算法簇模式,就是定义了不同的算法族,并且之间可以互相替换,此模式让算法的变化独立于使用算法的客户.策略模式属于对象的 ...

  4. Java设计模式之行为型模式

    行为型模式共11种:策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式. 策略模式:策略模式的决定权在用户,系统本身提供不同 ...

  5. Java设计模式(三) 抽象工厂模式

    原创文章,同步发自作者个人博客,转载请注明出处 http://www.jasongj.com/design_pattern/abstract_factory/ 抽象工厂模式解决的问题 上文<工厂 ...

  6. Java设计模式(十二) 策略模式

    原创文章,同步发自作者个人博客,http://www.jasongj.com/design_pattern/strategy/ 策略模式介绍 策略模式定义 策略模式(Strategy Pattern) ...

  7. Java设计模式(二) 工厂方法模式

    本文介绍了工厂方法模式的概念,优缺点,实现方式,UML类图,并介绍了工厂方法(未)遵循的OOP原则 原创文章.同步自作者个人博客 http://www.jasongj.com/design_patte ...

  8. Java设计模式(一) 简单工厂模式不简单

    摘要:本文介绍了简单工厂模式的概念,优缺点,实现方式,以及结合Annotation和反射的改良方案(让简单工厂模式不简单).同时介绍了简单工厂模式(未)遵循的OOP原则.最后给出了简单工厂模式在JDB ...

  9. Java设计模式(十三) 别人再问你设计模式,叫他看这篇文章

    原创文章,转载请务注明出处 OOP三大基本特性 封装 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的属性和方法只让可信的类操作,对不可信的进行信息隐藏. 继承 继承是指这样一种能力,它可以使 ...

随机推荐

  1. POJ2425 A Chess Game(SG函数+记忆化深搜)

    题目链接:传送门 题目大意: 在一个有N个点的拓扑图上(拓扑图以邻接表的形式输入),放M个棋子(棋子与棋子之间无关,可以重合). 两人轮流移动棋子,每次只能移动一个棋子经过一条边. 问先手是否必胜. ...

  2. root/base/stem概念

    The verb root (or base form): 1.is the same as the infinitive不定式 (e.g., to dive, to jump, to wonder) ...

  3. hihocoder1490 Tree Restoration 模拟

    There is a tree of N nodes which are numbered from 1 to N. Unfortunately, its edges are missing so w ...

  4. 【mybatis源码学习】mybtias基础组件-占位符解析器

    一.占位符解析器源码 1.占位符解析器实现的目标 通过解析字符串中指定前后缀中的字符,并完成相应的功能. 在mybtias中的应用,主要是为了解析Mapper的xml中的sql语句#{}中的内容,识别 ...

  5. Singer 学习十 同步模式

    sync 模式是属于tap 的操作,同步模式下,tap 需要提交 schema. record .state message, singer 指南对于每种 类型有详细的说明 streams 每个str ...

  6. JSON字符串转C#实体Class类

    在项目开发过程中,经常需要和不同部门或者不同的组员一起协同工作,但对方写的json返回的结果集,我们需要用,那么如何来生成对应的类代码和实体对象呢?于是参考了网上的做法,做一个简单的字符串转实体类的功 ...

  7. 5 个关于 API 中日期和时间设计规则

    规则 #1 使用ISO-8601格式作为你的日期格式 ISO 8601 解决了很多问题,包括: 自然排序 - 简单和优雅,免去多余的工作即可实现排序 时区偏移 - 代表用户的地点和时区在日益增长的全球 ...

  8. 谈谈在 .Net 平台上的 软件生态 和 软件生产力

    我们可以先看看这篇文章 : <看 StackOverflow 如何用 25 台服务器撑起 5.6 亿的月 PV>    http://www.nowamagic.net/librarys/ ...

  9. 突破本地离线存储的JS库 localforage

    localforage 简介 项目地址 https://github.com/localForage/localForage API中文地址 https://localforage.docschina ...

  10. redis 学习资料

    redis 学习资料 网址 Redis 教程(菜鸟教程) http://www.runoob.com/redis/redis-tutorial.html Redis 命令参考 http://redis ...