观察者模式

应用场景

使对象在每次发生状态的改变时,都通知它的观察者。

观察者模式的构造过程

  • 定义观察者接口,包含1个消息处理方法。
  • 定义被观察者接口,包含注册/解除/通知观察者方法。
  • 在被观察者中组合观察者对象,并在动作执行中调用观察者的消息处理方法。

代码示例

需求说明:本文拟定了“烽火台”的运转模式。烽火台即是观察者,也是被观察者。

/**
 * 被观察者接口
 */
public interface Subject {
	
	void registerObserver(Observer observer);
	
	void removeObserver(Observer observer);
	
	void notifyAllObserver();
	
	void notifyObserver(Observer observer);
}

/**
 * 观察者接口 
 */
public interface Observer {

	void doSomething(Subject subject);
}
/**
 * 烽火台(即是观察者,也是被观察者)
 */
public class GreatWall implements Subject, Observer  {
	
	//私有属性
	private String name;
	private boolean fired;
	private List<Observer> obList = new ArrayList<>(); 
	//构造器
	public GreatWall(String name){
		super();
		this.name = name;
	}
	//私有方法
	public void findEnemy(){
		System.out.println(this.name + ":" +"点燃狼烟!");
		fire(null);
	}
	private void fire(Subject subject){
		fired = true;
		notifyAllObserver();
	}
	
	//实现observer方法
	@Override
	public void doSomething(Subject subject) {
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		GreatWall wall = (GreatWall)subject;
		if (!fired) {
			fired = true;
			System.out.println(name + ":" + "发现狼烟,来自" + wall.getName() + "。点燃狼烟!");
			notifyAllObserver();
		} else {
			System.out.println(name + ":" + "发现狼烟,来自" + wall.getName());
		}
	}

	//实现subject方法
	@Override
	public void registerObserver(Observer observer) {
		this.obList.add(observer);
	}
	@Override
	public void removeObserver(Observer observer) {
		this.obList.remove(observer);
	}
	@Override
	public void notifyAllObserver() {
		if (obList != null && obList.size() > 0) {
			for (Observer ob: obList) {
				ob.doSomething(this);
			}
		}	
	}
	@Override
	public void notifyObserver(Observer observer) {
		observer.doSomething(this);
	}

	//getter setter
	public String getName() {
		return name;
	}

	public boolean isFired() {
		return fired;
	}

	public List<Observer> getObList() {
		return obList;
	}
}
/**
 * 测试类
 * 需求:模拟烽火台功能
 *
 */
public class Test {

	public static void main(String[] args) {
		
		GreatWall wall1 = new GreatWall("山海关");
		GreatWall wall2 = new GreatWall("函谷关");
		GreatWall wall3 = new GreatWall("嘉峪关");
		
		wall1.registerObserver(wall2);
		wall1.registerObserver(wall3);
		wall2.registerObserver(wall1);
		wall2.registerObserver(wall3);
		wall3.registerObserver(wall1);
		wall3.registerObserver(wall2);
		
		wall1.findEnemy();
	}
}

执行结果:
在这里插入图片描述这里已经实现了对观察者的通知功能。

发现问题:

狼烟由山海关发出,嘉峪关第一次看到狼烟是在函谷关而不是山海关。与构想的预警过程有误差。
原因在于GreatWall的NotifyAllObserver中对观察者逐一遍历,山海关先通知了函谷关,在函谷关执行了其dosomething方法、并调用嘉峪关dosomething后,山海关才调用了嘉峪关的dosomething方法。

解决方法如下

通过多线程实现对各观察者的通知同步

  1. 设计观察者通知线程
public class NoticeTread implements Runnable {
	
	private Observer ob;
	private Subject sub;
	
	public NoticeTread(Observer ob, Subject sub){
		super();
		this.ob = ob;
		this.sub = sub;
	}

	@Override
	public void run() {
		ob.doSomething(sub);
	}
}
  1. 修改被观察者的notifyAllObserver方法,在该方法中逐个启用线程进行通知。
	@Override
	public void notifyAllObserver() {
		if (obList != null && obList.size() > 0) {
			for (Observer ob: obList) {
				Thread noticeTread = new Thread(new NoticeTread(ob,this));
				noticeTread.start();
			}
		}	
	}

执行结果
在这里插入图片描述
最后,得到了预想的结果。实现了对烽火台观察和传递警报的简单模拟。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值