设计模式之Observer

这个模式可以参考awt包中的button极其相关事件的实现

说这个模式之前先用java简单的模拟一个场景吧(小孩在睡觉,醒了之后,爸爸要给他喂东西吃)。

先建两个类。爸爸这个类和小孩这个类,爸爸这个类主要提供一个负责向小孩喂东西的方法;小孩这个类主要提供一个哭的方法和一个什么时候哭的方法。

首先是爸爸这个类:Father.java

package com.yx.zzg.observer;

public class Father {

public void eatToChild(){
System.out.println("eat to child....");
}

}


然后是孩子这个类:

package com.yx.zzg.observer;

public class Child implements Runnable{

private Father f;

private boolean isweakup = false;

public Child(Father f) {
super();
this.f = f;
}

@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
weekUp();
}

public boolean weekUp() {
f.eatToChild();
return isweakup = true;
}

}


然后写一个测试的类:

package com.yx.zzg.observer;

public class Test {
public static void main(String[] args) {
Child c = new Child(new Father());
new Thread(c).start();
}
}


写到这里我们这个场景就模仿完成了,可是我现在需要根据小孩醒的地点的不同,时间的不同,而这个小孩的爸爸所做出来的反应也不同,如半夜醒来的时候可以给她唱个摇篮曲,早上醒来的时候给他喂点粥喝。

一种办法是我们可以在孩子这个类里添加两个属性,然后根据属性来判断他爸爸应该做什么。
但是这样设计有两个不好的地方:
1.地点,时间这两个属性不应该属于孩子这个对象的。
2.孩子醒来的时间和地点不确定,也就是爸爸要做的事情也不明确,可能后来爸爸要做的事情会有很多种。

因此我们可以提取一个孩子哭的这样一个事件类。这个类包含三个属性,哭的时间,地点,以及事件源,时间源设为Object类,是因为现在是孩子醒这个时间,也可能是其他什么东西醒了。

package com.yx.zzg.observer;

public class WeekUpEvent {

private long time;

private String location;

private Object source;

public WeekUpEvent(long time, String location, Object 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 Object getSource() {
return source;
}

public void setSource(Child source) {
this.source = source;
}

}


因此这时我们可以修改我们的孩子类的weekUp方法
这个时候weekUp方法就是这个样子:


public boolean weekUp() {
WeekUpEvent weekupEvent = new WeekUpEvent(System.currentTimeMillis(),
"zhengzhou", this);
if (weekupEvent.getLocation().equals("bj")) {
f.eatToChild();
}
if (weekupEvent.getLocation().equals("hh")) {
f.sing();
}
return isweakup = true;
}


但是这样做因为父亲要做的事情还是不明确,所以后来如果要添加方式的话我们还是要修改我们的weekUp方法,添加if else。这个时候我们应该怎么办呢?

因此这个时候我们应该抽取变的那部分内容,写一个接口


package com.yx.zzg.observer;

public interface WeekUpListener {

public void ActionWeekUp(WeekUpEvent weekUpEvent);

}


然后让爸爸那个类实现这个接口,这样爸爸那个类就可以根据事件的不同做出不同的反应。

package com.yx.zzg.observer;

public class Father implements WeekUpListener {

@Override
public void ActionWeekUp(WeekUpEvent weekUpEvent) {
if (weekupEvent.getLocation().equals("bj")) {
System.out.println("to eat....")
}
if (weekupEvent.getLocation().equals("hh")) {
System.out.println("to sing.....")
}
}

}


这是我们可以修改我们的孩子类为:

package com.yx.zzg.observer;

public class Child implements Runnable {
WeekUpListener weekUpListener;

public Child(WeekUpListener weekUpListener) {
super();
this.weekUpListener = weekUpListener;
}

@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
weekUp();
}

public void weekUp() {
weekUpListener.ActionWeekUp(new WeekUpEvent(System
.currentTimeMillis(), "zhengzhou", this));
}

}



因此我们的客户端就可以这样类写了

package com.yx.zzg.observer;

public class Test {
public static void main(String[] args) {
WeekUpListener wul=new Father();
Child c = new Child(wul);
new Thread(c).start();
}
}


因此这样做如果爸爸那个因素就具有扩展性了,比如如果爸爸那个类里面要根据时间不同提供其他的响应方式,我们就可以新建一个类,并且让这个类实现WeekUpListener 这个接口,在客户端就可以把WeekUpListener 的实现该为另外一个就可以了,其他的都不用任何改动。

但是这样做另外一个问题有出现了,这个时候我想妈妈也同时要做出反应,这个时候我们就可以这样做了,在孩子那个类里面放一个List,这个List用来接收所有的对孩子哭的这个事件的响应。应此孩子类最终就会成为这个样子:

package com.yx.zzg.observer;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Child implements Runnable {

private List<WeekUpListener> weekUpListeners = new ArrayList<WeekUpListener>();

public void addWeekUpListener(WeekUpListener weekUpListener) {
weekUpListeners.add(weekUpListener);
}

@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
weekUp(new WeekUpEvent(System
.currentTimeMillis(), "zhengzhou", this));
}

public void weekUp(WeekUpEvent weekUpEvent) {
for (Iterator<WeekUpListener> iter = weekUpListeners.iterator(); iter
.hasNext();) {
WeekUpListener weekUpListener = iter.next();
weekUpListener.ActionWeekUp(weekUpEvent);
}
}

}


而我们的测试类就会变成这个样子

package com.yx.zzg.observer;

public class Test {
public static void main(String[] args) {
Child c=new Child();
WeekUpListener wul = new Father();
c.addWeekUpListener(wul);
new Thread(c).start();
}
}


如果我要想添加其他人对这个事件的响应,只需要忘Child添加一个对WeekUpListener的实现。

如果想跟灵活一写,我们可以把这些对WeekUpListener实现配置到一个属性文件里,这样如果有新的实现我们连Test类都不需要修改,只需该一下我们的配置文件
如:我们可以在我们项目的src目录下建一个observer.properties文件
内容为:
[quote]
observers=com.yx.zzg.observer.Father,com.yx.zzg.observer.Mather
[/quote]

然后写一个读取资源文件的类:

package com.yx.zzg.observer;

import java.io.IOException;
import java.util.Properties;

public class PropertyMgr {

private static Properties props = new Properties();

static {
try {
//加载项目classpath路径下的properties文件
props.load(Test.class.getClassLoader().getResourceAsStream("observer.properties"));
} catch (IOException e) {
e.printStackTrace();
}
}

public static String getProperty(String key) {
return props.getProperty(key);
}

}



最终测试类为:

package com.yx.zzg.observer;

public class Test {
public static void main(String[] args) {
Child c = new Child();
String[] str = PropertyMgr.getProperty("observers").split(",");
for (String s : str) {
try {
c.addWeekUpListener((WeekUpListener) Class.forName(s)
.newInstance());
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
new Thread(c).start();
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值