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
  • 相关阅读:
    LeetCode 914. 卡牌分组
    LeetCode 999. 车的可用捕获量
    LeetCode 892. 三维形体的表面积
    21航电5E
    min25筛 学习笔记
    牛客多校6G
    2021航电多校3
    2021牛客多校H
    [模版] 快速傅里叶变换
    2021牛客多校第五场
  • 原文地址:https://www.cnblogs.com/tenWood/p/9206107.html
Copyright © 2011-2022 走看看