这两天在学设计模式,那么从下面这个例子来入手吧!
请模拟下列情形:
- 小孩在睡觉
- 醒来后要求吃东西
1.设计方式一
一个Child类,一个Dad类,Dad时刻监听Child,看其是否醒过来,若醒来则喂奶。
package pack1;
class Child implements Runnable{
private boolean WakeUp=false;
public boolean isWakeUp() {
return WakeUp;
}
public void setWakeUp(boolean wakeUp) {
WakeUp = wakeUp;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(5000);//假设孩子睡5秒钟就醒来
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.WakeUp=true;
}
}
class Dad implements Runnable{
Child c;
Dad(Child c){
this.c=c;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(!c.isWakeUp()){
try {
Thread.sleep(1000);//父亲每1秒就检查一次孩子有没有醒
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
feed(c);
}
private void feed(Child c) {
// TODO Auto-generated method stub
System.out.println("feed child ...");
}
}
public class Design1 {
public static void main(String[] args) {
Child c=new Child();
Dad d=new Dad(c);
Thread threadChild=new Thread(c);
Thread threadDad=new Thread(d);
threadChild.start();
threadDad.start();
}
}
上述设计方式虽然实现了基本的功能,但是由于Dad类一直主动监测Child是否醒来,浪费资源。一种简单的改进方式为,让Child醒来后主动调用Dad的Feed()方法,具体实现如下设计方式二。
2.设计方式二
Child类醒来主动调用Dad的Feed()方法。
package pack2;
class Child implements Runnable{
private Dad d;
public Child(Dad d) {
super();
this.d = d;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(5000);//假设孩子睡5秒钟就醒来
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
d.feed(this);//孩子Child醒来主动调用Dad的feed()方法
}
}
class Dad {
public void feed(Child c) {
// TODO Auto-generated method stub
System.out.println("feed child ...");
}
}
public class Design2 {
public static void main(String[] args) {
Dad d=new Dad();
Child c=new Child(d);
Thread threadChild=new Thread(c);
threadChild.start();
}
}
设计方式二较好的完成了情形的模拟,节省了资源。我们考虑更复杂的一下情况,若孩子不同时间醒来,爸爸的响应要不同怎么办?
3.设计方式三
为了实现对不同时间孩子醒来的不同响应,我们把孩子醒来封装成一个事件类WakeUpEvent。
package pack3;
class WakeUpEvent{
private long time;
private Object obj;
public WakeUpEvent(long time, Object obj) {
super();
this.time = time;
this.obj = obj;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
}
class Child implements Runnable{
private Dad d;
public Child(Dad d) {
super();
this.d = d;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(5000);//假设孩子睡5秒钟就醒来
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
d.ActionToWakeUp(new WakeUpEvent(System.currentTimeMillis(),this));//响应醒来事件
}
}
class Dad {
public void ActionToWakeUp(WakeUpEvent wakeUpEvent) {
// TODO Auto-generated method stub
if(wakeUpEvent.getTime()<12){//不同时间对孩子的响应也不同
System.out.println("feed child ...");
}else{
System.out.println("hug child ...");
}
}
}
public class Design3 {
public static void main(String[] args) {
Dad d=new Dad();
Child c=new Child(d);
Thread threadChild=new Thread(c);
threadChild.start();
}
}
设计方式三已经较全面的模拟了孩子醒来响应的基本情形,但此种方法的扩展性不好,例如,孩子醒来爷爷奶奶也有响应又怎么办?总不能来一个响应者就修改一次源代码吧?
4.设计方式三
使用接口,让所有响应者都实现WakeUpListener接口,并在Child类中使用响应者列表,使其可以动态添加响应者。
package pack4;
import java.util.ArrayList;
import java.util.List;
class WakeUpEvent{
private long time;
private Object obj;
public WakeUpEvent(long time, Object obj) {
super();
this.time = time;
this.obj = obj;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
}
interface WakeUpListener{
public void ActionToWakeUp(WakeUpEvent wakeUpEvent);
}
class Child implements Runnable{
private List<WakeUpListener> wakeUpListeners=new ArrayList<WakeUpListener>();
public void addWakeUpListener(WakeUpListener l){
wakeUpListeners.add(l);
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(5000);//假设孩子睡5秒钟就醒来
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//所有的响应者都对孩子醒来做出响应
for(int i=0;i<wakeUpListeners.size();i++){
WakeUpListener l=wakeUpListeners.get(i);
l.ActionToWakeUp(new WakeUpEvent(System.currentTimeMillis(),this));
}
}
}
class Dad implements WakeUpListener{
public void ActionToWakeUp(WakeUpEvent wakeUpEvent) {
// TODO Auto-generated method stub
if(wakeUpEvent.getTime()<12){//不同时间对孩子的响应也不同
System.out.println("Dad feed child ...");
}else{
System.out.println("Dad hug child ...");
}
}
}
class GrandFather implements WakeUpListener{
public void ActionToWakeUp(WakeUpEvent wakeUpEvent) {
// TODO Auto-generated method stub
if(wakeUpEvent.getTime()<12){//不同时间对孩子的响应也不同
System.out.println("GrandFather feed child ...");
}else{
System.out.println("GrandFather hug child ...");
}
}
}
public class Design4 {
public static void main(String[] args) {
Dad d=new Dad();
GrandFather gf=new GrandFather();
Child c=new Child();
c.addWakeUpListener(d);//添加父亲响应者
c.addWakeUpListener(gf);//添加爷爷响应者
Thread threadChild=new Thread(c);
threadChild.start();
}
}
此方法实现了动态添加响应者,它虽然不用修改Child类程序,但是还是要修改main()方法程序来添加响应者。
5.设计方式五
为了实现不修改任何程序而添加响应者,我们使用反射机制,通过读配置文件的方法来动态添加响应者。
下面为配置文件wakeUpListeners.properties。
若后期还需添加其他的响应者,则只需实现WakeUpListener接口,并在配置文件中加入响应类的名称条目即可,实现了无需修改源代码而动态添加响应者。
package pack5;
import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
class WakeUpEvent{
private long time;
private Object obj;
public WakeUpEvent(long time, Object obj) {
super();
this.time = time;
this.obj = obj;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
}
interface WakeUpListener{
public void ActionToWakeUp(WakeUpEvent wakeUpEvent);
}
class Child implements Runnable{
private List<WakeUpListener> wakeUpListeners=new ArrayList<WakeUpListener>();
public void addWakeUpListener(WakeUpListener l){
wakeUpListeners.add(l);
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(5000);//假设孩子睡5秒钟就醒来
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//所有的响应者都对孩子醒来做出响应
for(int i=0;i<wakeUpListeners.size();i++){
WakeUpListener l=wakeUpListeners.get(i);
l.ActionToWakeUp(new WakeUpEvent(System.currentTimeMillis(),this));
}
}
}
public class Design5 {
public static void main(String[] args) throws Exception {
Child c=new Child();
//创建配置文件
File configFile=new File("wakeUpListeners.properties");
Properties prop=new Properties();
FileInputStream fis=new FileInputStream(configFile);
prop.load(fis);
for(int i=0;i<prop.size();i++){
//获取响应者类名称
String wakeUpListenerName=(String)prop.get("wakeUpListener"+(i+1));
//根据类名称获取class文件
Class clazz=Class.forName(wakeUpListenerName);
WakeUpListener wakeUpListener=(WakeUpListener)clazz.newInstance();
c.addWakeUpListener(wakeUpListener);
}
fis.close();
Thread threadChild=new Thread(c);
threadChild.start();
}
}
以下分别为响应者类Dad、GrandFather、GrandMother,它们实现了统一的接口WakeUpListener,为了使用java的反射机制,都应为public类型的类。
package pack5;
public class Dad implements WakeUpListener{
public void ActionToWakeUp(WakeUpEvent wakeUpEvent) {
// TODO Auto-generated method stub
if(wakeUpEvent.getTime()<12){//不同时间对孩子的响应也不同
System.out.println("Dad feed child ...");
}else{
System.out.println("Dad hug child ...");
}
}
}
package pack5;
public class GrandFather implements WakeUpListener{
public void ActionToWakeUp(WakeUpEvent wakeUpEvent) {
// TODO Auto-generated method stub
if(wakeUpEvent.getTime()<12){//不同时间对孩子的响应也不同
System.out.println("GrandFather feed child ...");
}else{
System.out.println("GrandFather hug child ...");
}
}
}
package pack5;
public class GrandMother implements WakeUpListener{
public void ActionToWakeUp(WakeUpEvent wakeUpEvent) {
// TODO Auto-generated method stub
if(wakeUpEvent.getTime()<12){//不同时间对孩子的响应也不同
System.out.println("GrandMother feed child ...");
}else{
System.out.println("GrandMother hug child ...");
}
}
}
若后期还需添加其他的响应者,则只需实现WakeUpListener接口,并在配置文件中加入响应类的名称条目即可,实现了无需修改源代码而动态添加响应者。