zoukankan      html  css  js  c++  java
  • java设计模式-Observe

    一、背景                    

    请模拟下面情形:
    小孩在睡觉,醒来后要求吃东西
     
    代码:
    class Child{
        private boolean wakenUp = false;
        
        void wakeUp(){
            wakenUp = true;
        }
    
        public boolean isWakenUp() {
            return wakenUp;
        }
        public void setWakenUp(boolean wakenUp) {
            this.wakenUp = wakenUp;
        }
    }
    
    class Dad implements Runnable{
        Child c;
        
        public Dad(Child c){
            this.c = c;
        }
        
        @Override
        public void run() {
            while(!c.isWakenUp()){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            
            feed(c);
        }
    
        private void feed(Child c) {
            System.out.println("feed child");
        }
        
    }
    
    
    public class Test {
    
        public static void main(String[] args) {
            Child d = new Child();
            new Thread(new Dad(d)).start();
        }
    
    }

    上面代码运行起来是有问题的,永远等在那里,因为child永远不会醒过来;

    那么,想让小孩5s之后醒过来怎么做?

    二、让小孩5s之后醒          

    每一个小孩就是一个单独的线程;
     
    代码:
    class Child implements Runnable{
        private boolean wakenUp = false;
        
        void wakeUp(){
            wakenUp = true;
        }
    
        public boolean isWakenUp() {
            return wakenUp;
        }
        public void setWakenUp(boolean wakenUp) {
            this.wakenUp = wakenUp;
        }
    
        @Override
        public void run() {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.wakeUp();
        }
    }
    
    class Dad implements Runnable{
        Child c;
        
        public Dad(Child c){
            this.c = c;
        }
        
        @Override
        public void run() {
            while(!c.isWakenUp()){
                System.out.println("child is't wake up");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            
            feed(c);
        }
    
        private void feed(Child c) {
            System.out.println("feed child");
        }
        
    }
    
    
    public class Test {
    
        public static void main(String[] args) {
            Child d = new Child();
            new Thread(d).start();
            new Thread(new Dad(d)).start();
        }
    
    }

    console:

    child is't wake up
    child is't wake up
    child is't wake up
    child is't wake up
    child is't wake up
    feed child
    这个小程序就模拟完了;也就是说要做完这个功能就已经做完了;
    但是有什么不合理的地方?
    小孩一直在睡着,如果采取现在这种编程模式,Dad就一直监视,无法干其他事情了,就只能在这里死死的盯着他;1s钟盯一眼,太累了;
    这个累的意思就是,CPU的资源无端的被消耗了,CPU没事老是在这循环着;如果小孩3小时不醒就得循环3小时;这种编程的方式起码在效率上有问题;
     
    那么,怎么修正这个问题呢?
     
     
     
    三、第二版的设计              
    第二版的设计,不要让Dad在这浪费时间了;
    Dad是主动的检测这个小孩有没有醒;主动的监听是非常消耗CPU资源的;
    让小孩醒过来的时候自己调我这方法就完了;反过来让小孩监控他Dad;
     
    代码:
    class Child implements Runnable{
        private Dad d;
        
        public Child(Dad d){
            this.d = d;
        }
        
        void wakeUp(){
            d.feed(this);
        }
    
        @Override
        public void run() {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.wakeUp();
        }
    }
    
    class Dad{
        public void feed(Child c) {
            System.out.println("feed child");
        }
    }
    
    
    public class Test {
    
        public static void main(String[] args) {
            Dad d = new Dad();
            Child c = new Child(d);
            new Thread(c).start();
        }
    
    }

    过5s后,console打印:feed child

    同样的功能,这种方式比上面的要效率高一些,消耗的CPU要少一些;
    但是作为设计来讲,在一个软件项目之中的设计,如果只考虑到当前这种设计不具有弹性,也就是不具有可扩展性
    什么叫做没有弹性?
    比方说设计一个新闻系统,发表一篇文章需要经过三级审查,编辑审了,副主编审,再主编审;如果不符合要求或者含有反动等信息就不能发表出去;
    这时候你在程序里就写了三级,假如说将来级别增加了,需要四级审查、五级...怎么办?改源代码就很累;
    考虑到可扩展性就可以用一个参数做配置,在配置文件里写3就是三级审查,写4就是四级审查....这样就有弹性一些;
     
    上面代码,考虑一个问题:小孩可能会在不同的时间段醒来、不同的地点醒来,针对于不同醒来的事件,Dad的处理方式应该是不同的;这件事情的发生是包含着一些具体情况的,应该把不同的情况告诉他Dad,怎么样把事情的各种各样的信息告诉他Dad呢?
    该用什么样的设计方法?
     
     
     
    四、对于事件的处理            
    对于Dad来说,要根据事件的具体情况,才能做出具体的选择,也就是说作为Child来说,有责任把这件事情的具体情况通知你的监测人;
    可以这么写,在Child里面增加什么时候醒来的时间time、醒来的地点loc,然后传给feed()里面的child c;
     
     可以这么写:
    class Child implements Runnable{
        private Dad d;
        String time;
        String loc;
        
        public Child(Dad d){
            this.d = d;
        }
        
        void wakeUp(){
            time = "晚上";
            loc = "床上";
            d.feed(this);
        }
    
        @Override
        public void run() {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.wakeUp();
        }
    }
    
    class Dad{
        public void feed(Child c) {
            System.out.println("child wake up time :" + c.time);
            System.out.println("child wake up location :" + c.loc);
            System.out.println("feed child");
        }
    }
    
    
    public class Test {
    
        public static void main(String[] args) {
            Dad d = new Dad();
            Child c = new Child(d);
            new Thread(c).start();
        }
    }
    View Code

    console:5s后打印:

    child wake up time :晚上
    child wake up location :床上
    feed child
    但是:仔细分析,作为time它应该是小孩的属性吗?loc应该是小孩的属性吗?
    不是,它们应该是事件本身的属性;是事件发生时候的时间、地点。
    所以,面向对象设计的一条最基础的原则:最合适的属性应该放在最合适的类里面;
     
    所以:
    1)再增加一个事件类:
    2)并且,child的wakeUp()方法里面调用Dad的feed()方法,已经写死了,假如child醒过来不想让Dad喂他,而是让Dad带他出去玩呢?
    那么feed方法就不合适了。更灵活的方法是:child一醒过来发出一件事,就让Dad对这件事做出反应,只要Dad做出正确的反应就ok了。
    所以feed方法换为ActionToWakenUp;

    代码:

    /**
     * 醒过来的事件
     */
    class WakenUpEvent{
        private long time;
        private String loc;
        private Child source;        //发生事件的源对象
        
        public WakenUpEvent(long time, String loc, Child source) {
            super();
            this.time = time;
            this.loc = loc;
            this.source = source;
        }
        public long getTime() {
            return time;
        }
        public void setTime(long time) {
            this.time = time;
        }
        public String getLoc() {
            return loc;
        }
        public void setLoc(String loc) {
            this.loc = loc;
        }
        public Child getSource() {
            return source;
        }
        public void setSource(Child source) {
            this.source = source;
        }
    }
    
    class Child implements Runnable{
        private Dad d;
    
        public Child(Dad d){
            this.d = d;
        }
        
        void wakeUp(){
            d.ActionToWakenUp(new WakenUpEvent(System.currentTimeMillis(), "bed", this));
        }
    
        @Override
        public void run() {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.wakeUp();
        }
    }
    
    class Dad{
        public void ActionToWakenUp(WakenUpEvent e) {
            System.out.println("child wake up time " + e.getTime());
            System.out.println("child wake up location " + e.getLoc());
            System.out.println("feed child");
        }
    }
    
    public class Test {
    
        public static void main(String[] args) {
            Dad d = new Dad();
            Child c = new Child(d);
            new Thread(c).start();
        }
    }

    console,5s后打印:

    child wake up time 1529505152384
    child wake up location bed
    feed child

    上面的设计其实还有问题:

    假如说不止Dad,小孩醒过来爷爷也要做出一点反应,该怎么办呢?怎么样让更多的人对这件事做出反应?

     五、第三种设计方法          

    现在问题是怎么让监听事件的这些个人Dad、GrandFather、奶奶,以及对事件做出响应,怎么能做到比较好的扩展,不需要在Child类里面改来改去;
    因为在Child里面改的话:
    不但要增加private GrandFather gf;
    还需要在wakeUp方法里面增加GrandFather的ActionToWakenUp方法;
    还需要修改Child的构造方法,Child(Dad d, GrandFather gf)
    ...
     
    修改为如下:
    import java.util.ArrayList;
    import java.util.List;
    
    
    
    public class Test {
        public static void main(String[] args) {
            Child c = new Child();
            c.addWakenUpListener(new Dad());
            c.addWakenUpListener(new GrandFather());
            new Thread(c).start();
        }
    }
    
    /**
     * 发出事件的主体
     */
    class Child implements Runnable{
        private List<WakenUpListener> WakenUplisteners = new ArrayList<WakenUpListener>();
    
        public void addWakenUpListener(WakenUpListener l){
            this.WakenUplisteners.add(l);
        }
    
        void wakeUp(){
            for(WakenUpListener listener : WakenUplisteners){
                listener.ActionToWakenUp(new WakenUpEvent(System.currentTimeMillis(), "bed", this));
            }
        }
        
        @Override
        public void run() {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.wakeUp();
        }
    }
    
    //对应醒事件的监听器接口
    interface WakenUpListener {
        public void ActionToWakenUp(WakenUpEvent e);
    }
    
    //Dad监听器
    class Dad implements WakenUpListener{
        public void ActionToWakenUp(WakenUpEvent e) {
            System.out.println("event time: "+e.getTime() +",event location: "+e.getLoc());
            System.out.println("feed child");
        }
    }
    //GrandFather监听器
    class GrandFather implements WakenUpListener{
        public void ActionToWakenUp(WakenUpEvent e) {
            System.out.println("event time: "+e.getTime() +",event location: "+e.getLoc());
            System.out.println("hug child");
        }
    }
    
    /**
     * 醒事件
     */
    class WakenUpEvent{
        private long time;
        private String loc;
        private Object source;        //发生事件的源对象
        
        public WakenUpEvent(long time, String loc, Object source) {
            super();
            this.time = time;
            this.loc = loc;
            this.source = source;
        }
        public long getTime() {
            return time;
        }
        public void setTime(long time) {
            this.time = time;
        }
        public String getLoc() {
            return loc;
        }
        public void setLoc(String loc) {
            this.loc = loc;
        }
        public Object getSource() {
            return source;
        }
        public void setSource(Object source) {
            this.source = source;
        }
    }

    console:

    event time: 1529508249412,event location: bed
    feed child
    event time: 1529508249413,event location: bed
    hug child
  • 相关阅读:
    第二十一章流 1流的操作 简单
    第二十章友元类与嵌套类 1友元类 简单
    第十九章 19 利用私有继承来实现代码重用 简单
    第二十章友元类与嵌套类 2嵌套类 简单
    第十九章 8链表类Node 简单
    第二十一章流 3用cin输入 简单
    第十九章 10 图书 药品管理系统 简单
    第十九章 11图书 药品管理系统 简单
    第二十一章流 4文件的输入和输出 简单
    第十九章 12 什么时候使用私有继承,什么时候使用包含 简单
  • 原文地址:https://www.cnblogs.com/tenWood/p/9206107.html
Copyright © 2011-2022 走看看