观察者模式学习笔记(详细)

观察者模式学习笔记(详细)

一、什么是观察者模式

观察者模式,是定义对象之间的一对多的关系,主要作用是减少对象之间的耦合度,分为两个角色

  1. 被观察者:其实就是发布者,发布消息通知所有的观察者
  2. 观察者:接到被观察者发布的消息做出相应的动作

上图中,左边一组是被观察者,右边一组是观察者

  • Subjecct:被观察者抽象类,拥有类成员ObserverList,和三个抽象方法
    ObserverList:存放所有的观察者对象
    addObserver():向ObserverList中添加观察者对象
    delObject():从ObserverList中删除观察者对象
    notifyObservers():通知所有的观察者对象

  • SubjectItem:具体的被观察者类,继承Subject接口,有具体的动作方法,调用父类的notifyObject()方法通知所有的观察者

  • Observer:观察者接口,有一个抽象方法update(),供被观察者调用

  • ObserverItem:具体的观察者类,实现Observer类,实现update()方法

二、使用场景

游戏中,被观察者主角有所动作就通知观察者们
支付中,一个商品购买成功,需要执行许多操作(更新订单,发送邮件,更新账户...),这时就可以使用观察者模式来减少耦合
再比如,关注动态,动态更新,需要发送通知给所有的关注者

三、具体实现

简单的一个冒险游戏,勇者做为被观察者,观察者有怪物,食物,NPC
当勇者移动碰到怪物时血量减1,碰到食物时血量加1,碰到NPC时展开剧情

  1. 编写Observer接口
    public interface Observer {
        public void update();
    }
  1. 编写Subject抽象类
    public abstract class Subject {
        //观察者集合
        private List<Observer> observerList = new ArrayList<>();
                
        //添加观察者
        public void addObserver(Observer observer){
           observerList.add(observer);
        }
        
        //删除观察者
        public void delObserver(Observer observer){
            observerList.remove(observer);
        }
            
        //通知所有观察者
        public void notifyObservers(){
            for(Observer observer : observerList){
                observer.update();
            }
        }
        
    }

observerList集合存放了观察者接口,不直接存放观察者类
notifyObservers()方法遍历observerList集合,调用update()方法
通过抽象层减少耦合

  1. 编写勇者类(被观察者)
    public class Hero extends Subject {
    
        static int X = 0;
        static int Y = 0;
    
        //勇者移动
        public void move(int x, int y){
            X = x;
            Y = y;
            super.notifyObservers();
        }
    
    }

Hero类有move()方法,让勇者移动并调用父类的notifyObservers()方法通知所有的观察者

  1. 编写怪物类(观察者)
    public class Monster implements Observer {
    
        static int X = 5;
        static int Y = 7;
    
        @Override
        public void update() {
            if(Hero.X == X && Hero.Y == Y){
                System.out.println("怪物攻击勇者!");
            }
        }
        
    }  

Monster类实现Observer接口,实现具体的update()方法

  1. 编写食物类(观察者)
    public class Food implements Observer {
        
        static int X = 8;
        static int Y = 8;
    
        @Override
        public void update() {
            if(Hero.X == X && Hero.Y == Y){
                System.out.println("勇者吃到了食物,血量加1!");
            }
        }
        
    }  

Food类实现Observer接口,实现具体的update()方法

  1. 编写npc类(观察者)
    public class Npc implements Observer {
    
        static int X = 15;
        static int Y = 20;
    
        @Override
        public void update() {
            if(Hero.X == X && Hero.Y == Y){
                System.out.println("勇者遇到NPC,剧情展开!");
            }
        }
    
    }

Npc类实现Observer接口,实现具体的update()方法

  1. 编写执行类
    public class Run {
        
        public static void main(String[] args){
            //初始化对象
            Hero hero = new Hero();
            Monster monster = new Monster();
            Food food = new Food();
            Npc npc = new Npc();
    
            //添加观察者
            hero.addObserver(monster);
            hero.addObserver(food);
            hero.addObserver(npc);
            //勇者移动,遇到怪物
            hero.move(5,7);
            //勇者移动,吃到食物
            hero.move(8,8);
            //勇者移动,遇到npc
            hero.move(15,20);
        }
    
    }

勇者移动时通知了所有已注册的观察者,执行各自的update方法

  1. 运行结果

四、总结

  • 优点:
    使用抽象的方式来减少被观察者和观察者之间的耦合,可以方便的增加观察者

  • 缺点:
    如果被观察者有过多的直接观察者和间接观察者的话,会花费更多的时间,如果观察者之间存在循环依赖的话,会导致他们直接进行循环调用,最终导致系统崩溃
    观察者可以知道被观察者发生的变化,但是无法知道被观察者是如何发生的变化

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。