美文网首页Java · 成长之路技术类程序员
设计模式学习(二)——观察者模式

设计模式学习(二)——观察者模式

作者: 活成理想中的样子 | 来源:发表于2017-06-07 22:46 被阅读389次

    一.需求

    时间过的真快,小王的便利店已经开了大半年了,生意很是红火。

    一天,小王远在乡下的表哥来城里办事,顺便来看小王。表哥在乡下开了个养鸡场,都是散养的柴鸡,每过一段时间,表哥就会来城里送一批柴鸡蛋。

    小王突发奇想:能不能在自己的便利店也卖柴鸡蛋呢?这个想法一说,表哥当即就赞同了。几天后,就送来了第一批柴鸡蛋。

    小区张大妈经常给孙女做糕点,看到便利店卖柴鸡蛋,就买了一些;便利店隔壁是赵阿姨开的一家餐馆,也买了一些回去。吃过小王便利店里柴鸡蛋的人都说不错,渐渐的,柴鸡蛋开始供不应求了。不少顾客来买东西都要问还有没有柴鸡蛋卖,甚至有一些顾客每天都来一次专门问柴鸡蛋到货了没有。

    小王感到很是不安,这么多人来买鸡蛋,但是鸡蛋根本不够卖,害的顾客空跑,有没有什么好办法呢?

    二.初步尝试

    经过一番仔细分析,小王觉得,眼下最需要解决的问题是让大家知道店里还有没有鸡蛋卖,不能让大家空跑一趟。

    小王想到的办法是:在店门口贴一张通知栏,在布告栏上写明当前店里有没有柴鸡蛋卖,并及时更新。每个顾客每天从小区门口路过,如果自己想买柴鸡蛋,并且布告栏上写着有鸡蛋,则可以进店购买。

    在这个过程中,有两个对象:通知栏,顾客。通知栏有一个状态来实时展示当前是否有柴鸡蛋,顾客每天看通知栏,决定是否进店购买。如果用程序实现就是下面这样:

    // 布告栏
    class Notice {
        private boolean hasEgg;
    
        public boolean hasEgg() {
            return hasEgg;
        }
    
        public void setHasEgg(boolean hasEgg) {
            this.hasEgg = hasEgg;
        }
    }
    
    // 顾客
    class Customer {
    
        private String name;
    
        private boolean needEgg;
    
        private Notice notice;
    
        public Customer(String name, boolean needEgg, Notice notice) {
            this.name = name;
            this.needEgg = needEgg;
            this.notice = notice;
        }
    
        public void bugEgg() {
            if (needEgg && notice.hasEgg()) {
                System.out.println(this.name + "进店购买鸡蛋");
            } else {
                System.out.println(this.name + "不进店购买鸡蛋");
            }
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public boolean isNeedEgg() {
            return needEgg;
        }
    
        public void setNeedEgg(boolean needEgg) {
            this.needEgg = needEgg;
        }
    }
    
    // 客户端
    public class Main {
    
        public static void main(String[] args) {
            Notice notice = new Notice();
            notice.setHasEgg(true); // 鸡蛋到货了
    
            Customer motherZhang = new Customer("张大妈", true, notice);
            Customer auntZhao = new Customer("赵阿姨", true, notice);
            Customer uncleLi = new Customer("李叔叔", false, notice);
    
            motherZhang.bugEgg();
            auntZhao.bugEgg();
            uncleLi.bugEgg();
        }
    }
    

    运行一下试试:

    张大妈进店购买鸡蛋
    赵阿姨进店购买鸡蛋
    李叔叔不进店购买鸡蛋
    

    看起来一切正常!所有的顾客不用进店就可以知道有没有柴鸡蛋卖,大大方便了顾客。

    三.更好的方案

    上面的方案可以正常工作,实现通知的目的。但是仍然有几个问题:
    1.通知不够及时,依赖于顾客自己主动去看通知。如果鸡蛋到货时,自己刚好在家里没有看到,就会错过买鸡蛋;
    2.一些顾客不关心柴鸡蛋的通知。尽管有很多顾客喜欢小王家的柴鸡蛋,但是也有不少顾客其实并不关心,门口的布告栏对他们来说毫无价值。

    第二个问题倒是无关紧要,第一个问题的确是存在的,需要进一步改进。

    为了进一步方便大家来买柴鸡蛋,小王想到了一个更好的办法:对于那些经常买鸡蛋的顾客,留下他们的手机号码,一旦有鸡蛋可卖,就给他们群发短信,通知他们可以来买鸡蛋了。

    这样一来,就解决了通知不及时的问题,而且第二个问题同时也解决了:短信只会发给那些想买鸡蛋的人,不想买鸡蛋的人不会收到。之前想买鸡蛋但是后来不想买了,可以到小王这里说一下,下次就不会给他发短息了;之前不想买鸡蛋的人也可以随时到小王这里留下电话,下次就可以接到通知了。

    新的方案如下:

    // 鸡蛋管理
    class EggAdmin {
        private List<Customer> customerList;
    
        private boolean hasEgg;
    
        public EggAdmin() {
            customerList = new ArrayList<Customer>();
        }
    
        public void addCustomer(Customer customer) {
            System.out.println(customer.getName() + "订阅鸡蛋到货消息");
            customerList.add(customer);
        }
    
        public void removeCustomer(Customer customer) {
            System.out.println(customer.getName() + "退订鸡蛋到货消息");
            customerList.remove(customer);
        }
    
        public void notice() {
            if (hasEgg) { // 一旦鸡蛋到货就通知所有想买鸡蛋的人来买鸡蛋
                for (Customer customer : customerList) {
                    customer.bugEgg();
                }
            }
        }
    
        public boolean hasEgg() {
            return hasEgg;
        }
    
        public void setHasEgg(boolean hasEgg) {
            this.hasEgg = hasEgg;
        }
    }
    // 顾客
    class Customer {
    
        private String name;
    
        public Customer(String name) {
            this.name = name;
        }
    
        public void bugEgg() {
            System.out.println(this.name + "进店购买鸡蛋");
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if ( ! (obj instanceof  Customer)) {
                return false;
            }
            Customer newCustomer = (Customer) obj;
            return this.name.equals(newCustomer.getName());
        }
    }
    
    // 客户端
    public class Main {
    
        public static void main(String[] args) {
    
            Customer motherZhang = new Customer("张大妈");
            Customer auntZhao = new Customer("赵阿姨");
            Customer uncleLi = new Customer("李叔叔");
    
            EggAdmin eggAdmin = new EggAdmin();
            eggAdmin.addCustomer(motherZhang);
            eggAdmin.addCustomer(auntZhao);
            eggAdmin.setHasEgg(true);
            eggAdmin.notice();
    
            eggAdmin.addCustomer(uncleLi);
            eggAdmin.removeCustomer(auntZhao);
            eggAdmin.notice();
    
        }
    }
    

    运行一下:

    张大妈订阅鸡蛋到货消息
    赵阿姨订阅鸡蛋到货消息
    张大妈进店购买鸡蛋
    赵阿姨进店购买鸡蛋
    李叔叔订阅鸡蛋到货消息
    赵阿姨退订鸡蛋到货消息
    张大妈进店购买鸡蛋
    李叔叔进店购买鸡蛋
    

    感觉还不错,这下想买鸡蛋的顾客就可以及时收到提醒,来店里买鸡蛋了。

    四.模式总结

    我们在上面的新方案中用到了观察者模式。但是我们只是简单的应用,真实的观察者模式要更为灵活,定义了抽象的主题、具体的主题,抽象的观察者、具体的观察者,采用面向接口编程。

    使用场景

    当你需要维护一个一对多的关系时,其中一个主题的状态发生变化,其他几个依赖者都需要接收到通知并自动更新。

    一个典型的使用场景是:在交互界面,我们可以对每个按钮添加Listener,当按钮被点击时,所有Listener都会得到通知。

    类图
    观察者模式类图

    在观察者模式中,有主题和观察者两种角色,主题可以有多种实现,观察者亦可以有多种实现,每个具体主题中维护了观察者列表,以便通知所有观察者。每个观察者中也维护了需要观察的主题,以便将来取消观察。

    在主题中通常还会有一个subjectState属性用来表示状态,这可以更灵活的控制通知发生的时机,例如在我们上面的例子中,有一个hasEgg字段用来表示是否有鸡蛋,只有当有鸡蛋时才会真正发出通知。

    优点

    1.观察者模式定义了一种稳定的消息传递机制,可以优雅的在主题与观察者之间发布更新;
    2.主题和观察者之间是松耦合的,彼此间不知道对方的实现细节,且基于接口编程,当有新的观察者出现时,主题类无需修改,易于扩展;

    缺点

    如果一个主题的观察者较多,则观察者全部接到通知会花费一定的时间,不同的观察者接收到通知的时间也会有先后,由此可能会导致一些问题。

    参考资料:
    《Head First Java》

    本文已迁移至我的博客:http://ipenge.com/631.html

    相关文章

      网友评论

      • 蚂蚁不爱搬家:正想系统的学习下设计模式,get!感谢作者🐸
      • 小西ios:这是java语言吗?感觉和swfit语言很像
        fanzkday:什么语言无所谓。重要的是模式和方法。
        活成理想中的样子: @小西ios 是的,我用java写的
      • fanzkday:写的不错, 通俗易懂, 很有收获。原来这就是观察者模式,哈哈。
        fanzkday:@自由水鸟 我去了解了解。多学点。等大潮落下的时候,我可不想光着屁股。
        活成理想中的样子: @fanzkday 谢谢哈,我也是想把常用的设计模式好好整理一遍,《head first设计模式》那本书很不错,也是通俗易懂,有兴趣可以看看

      本文标题:设计模式学习(二)——观察者模式

      本文链接:https://www.haomeiwen.com/subject/jfjnfxtx.html