工作中我们经常会出现这样一种场景:在工作不忙的时候,老板出去办事了,然后很多员工就在工位上开始忙自己的事,比如看股票,看视频,看小说,吃零食。前台员工充当哨兵,只要老板一来,就马上通知员工们。
第一版,双向耦合代码实现:
- //秘书充当前哨
- public class Secretary {
- private List<StockObserver> observers = new ArrayList<StockObserver>();
- private String action;
- public void attach(StockObserver observer){
- observers.add(observer);
- }
- public void notifyObservers(){
- for(StockObserver so : observers){
- so.update();
- }
- }
- public String getAction() {
- return action;
- }
- public void setAction(String action) {
- this.action = action;
- }
- }
- public class StockObserver {
- private String name;
- private Secretary sub;
- public StockObserver(String name, Secretary sub){
- this.name = name;
- this.sub = sub;
- }
- public void update(){
- System.out.println(sub.getAction() + " " + name + " 关闭股票行情,继续工作!");
- }
- }
- public class Test {
- public static void main(String[] args) {
- //前台人员
- Secretary xxx = new Secretary();
- //看股票的同事
- StockObserver yyy = new StockObserver("xiaoming", xxx);
- StockObserver zzz = new StockObserver("xiaohua", xxx);
-
- //前台记下两位同事
- xxx.attach(zzz);
- xxx.attach(yyy);
- xxx.setAction("老板来了");
- xxx.notifyObservers();
- }
- }
我们发现“前台”类与“看股票者”是双向耦合的,如果观察者中还有想看NBA直播、看电影的,那“前台”类就得改动。这违反了开放-封闭原则,也违背了依赖倒转原则,我们应该让程序都依赖抽象,而不是互相依赖。我们发现,老板也好,前台也好,秘书也好,都是具体的通知者。这里观察者也不应该依赖具体的实现,而是一个抽象的通知者。
第二版,解耦实践:
- //通知者接口
- public interface Subject {
- void attach(Observer observer);
- void detach(Observer observer);
- void notifyObservers();
- String getSubjectState();
- void setSubjectState(String action);
- }
- public class Boss implements Subject{
- private List<Observer> observers = new ArrayList<Observer>();
- private String action;
- @Override
- public void attach(Observer observer) {
- observers.add(observer);
-
- }
- @Override
- public void detach(Observer observer) {
- observers.remove(observer);
-
- }
- @Override
- public void notifyObservers() {
- for(Observer o : observers){
- o.update();
- }
-
- }
- @Override
- public String getSubjectState() {
- return this.getAction();
- }
- public String getAction() {
- return action;
- }
- public void setAction(String action) {
- this.action = action;
- }
- @Override
- public void setSubjectState(String action) {
- this.setAction(action);
-
- }
- }
前台类与老板类类似,略。
- public abstract class Observer {
- protected String name;
- protected Subject sub;
- public Observer(String name, Subject sub){
- this.name = name;
- this.sub = sub;
- }
- public abstract void update();
- }
- public class StockObserver1 extends Observer{
- public StockObserver1(String name, Subject sub){
- super(name, sub);
- }
- public void update(){
- System.out.println(sub.getSubjectState() + " " + name + " 关闭股票行情,继续工作!");
- }
- }
- public class NBAObserver extends Observer{
- public NBAObserver(String name, Subject sub){
- super(name, sub);
- }
- public void update(){
- System.out.println(sub.getSubjectState() + " " + name + " 关闭NBA直播,继续工作!");
- }
- }
- public class Test2 {
- public static void main(String[] args) {
- //前台人员
- Subject boss = new Boss();
- //看股票的同事
- Observer yyy = new StockObserver1("xiaoming", boss);
- Observer zzz = new NBAObserver("xiaohua", boss);
-
- //前台记下两位同事
- boss.attach(zzz);
- boss.attach(yyy);
- boss.detach(yyy);
- boss.setSubjectState("你们的boss我回来了");
- boss.notifyObservers();
- }
- }
这就是改进版的观察者模式。
观察者模式:也叫做发布-订阅模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对象。这个主题对象在状态发生变化时,会通知所有的观察者,使他们能够自动地更新自己。

观察者模式特点:
将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是要维护相关对象间的一致性。这个给维护、扩展、重用都带来了不便。而观察者模式解决了这个不便。那什么时候该用此模式呢?当一个对象的改变需要同时改变其他对象的时候。总的来讲,观察者模式所做的工作就是在解除耦合,让耦合的双方都依赖于抽象,而不是依赖具体,从而使得各自的变化都不会影响到另一边的变化。该实现方法也有不足之处,尽管已经使用了依赖倒转原则,但“抽象通知者”还是依赖“抽象观察者”,也就是说万一没了抽象观察者这样的接口,这通知功能就完不成了。另外就是每个具体的观察者,它不一定是“更新”的方法要调用,而是其他不同名的方法。
第三版,事件委托实现
- public class Event {
- //要执行方法的对象
- private Object object;
- //要执行的方法名称
- private String methodName;
- //要执行方法的参数
- private Object[] params;
- //要执行方法的参数类型
- private Class[] paramTypes;
- public Event(){
-
- }
- public Event(Object object, String methodName, Object...args){
- this.object = object;
- this.methodName = methodName;
- this.params = args;
- contractParamTypes(this.params);
- }
- //根据参数数组生成参数类型数组
- private void contractParamTypes(Object[] params){
- this.paramTypes = new Class[params.length];
- for(int i = 0; i < params.length; i++){
- this.paramTypes[i] = params[i].getClass();
- }
- }
- public Object getObject() {
- return object;
- }
- public void setObject(Object object) {
- this.object = object;
- }
- public String getMethodName() {
- return methodName;
- }
- public void setMethodName(String methodName) {
- this.methodName = methodName;
- }
- public Object[] getParams() {
- return params;
- }
- public void setParams(Object[] params) {
- this.params = params;
- }
- public Class[] getParamTypes() {
- return paramTypes;
- }
- public void setParamTypes(Class[] paramTypes) {
- this.paramTypes = paramTypes;
- }
- //执行该对象的该方法
- public void invoke() throws Exception{
- String name = this.getMethodName();
- Method method = object.getClass().getMethod(this.getMethodName(), this.getParamTypes());
- if(null == method){
- return;
- }
- method.invoke(this.getObject(), this.getParams());
- }
- }
- public class EventHandler {
- private List<Event> objects;
- public EventHandler(){
- objects = new ArrayList<Event>();
- }
- //添加某个对象要执行的事件及需要的参数
- public void addEvent(Object object, String methodName, Object...args){
- objects.add(new Event(object, methodName, args));
- }
- //通知所有的对象执行指定的事件
- public void notifyX() throws Exception{
- for(Event e : objects){
- e.invoke();
- }
- }
- }
- public abstract class Notifier {
- private EventHandler eventHandler = new EventHandler();
- //增加需要帮忙放哨的学生
- public abstract void addListener(Object object, String methodName, Object...args);
- public abstract void notifyX();
- public EventHandler getEventHandler() {
- return eventHandler;
- }
- public void setEventHandler(EventHandler eventHandler) {
- this.eventHandler = eventHandler;
- }
- }
- public class GoodNotifier extends Notifier{
- @Override
- public void addListener(Object object, String methodName, Object... args) {
- System.out.println("有新的同学委托尽职尽责的放哨人");
- this.getEventHandler().addEvent(object, methodName, args);
- }
- @Override
- public void notifyX() {
- System.out.println("尽职尽责的放哨人告诉所有需要帮忙的同学:老师来了");
- try {
- this.getEventHandler().notifyX();
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- public class PlayingGameListener {
- public PlayingGameListener(){
- System.out.println("我正在玩游戏 开始时间:" + new Date());
- }
- public void stopPlayingGame(Date date){
- System.out.println("老师来了,快回到座位上,结束时间:" + date);
- }
- }
- public class WatchingTVListener {
- public WatchingTVListener(){
- System.out.println("我正在看电视开始时间:" + new Date());
- }
- public void stopWatchingTV(Date date){
- System.out.println("老师来了,快关闭电视,结束时间:" + date);
- }
- }
- public class Test {
- public static void main(String[] args){
- Notifier goodNotifier = new GoodNotifier();
- PlayingGameListener playingGameListener = new PlayingGameListener();
- WatchingTVListener watchingTVListener = new WatchingTVListener();
- goodNotifier.addListener(playingGameListener, "stopPlayingGame", new Date());
- goodNotifier.addListener(watchingTVListener, "stopWatchingTV", new Date());
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- goodNotifier.notifyX();
- }
- }
现在可以解释一下委托是什么了。委托就是一种引用方法的类型,一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法可以像其他任何方法一样,具有参数和返回值,委托可以看做是对函数的抽象,是函数的“类”,委托的实例将代表一个具体的函数。new EventHandler()其实就是创建了一个委托实例,一个委托可以搭载多个方法,所有方法被依次唤起,它可以使委托对象所搭载的方法并不需要属于同一个类。但委托也是有前提的,那就是委托对象所搭载的所有方法必须具有相同的原形和形式,也就是拥有相同的参数列表和返回值类型。