• Head First 设计模式 --7 适配器模式 外观模式


    适配器模式:将一个类东街口转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。

    适配器模式有两种,对象适配器和类的适配器。先看一下对象适配器。

    还是看最开始鸭子的例子,如果此时鸭子不够了,需要一个火鸡来充当一个鸭子。

    对象适配器

    interface Duck {
      public void quack();
    
      public void fly();
    }
    
    interface Turkey {
      public void gobble();
    
      public void fly();
    }
    
    class WildTurkey implements Turkey {
      @Override
      public void gobble() {
        System.out.println("Gobble gobble");
      }
    
      @Override
      public void fly() {
        System.out.println("i am flying a short distance");
      }
    }
    
    class TurkeyAdaper implements Duck {
      Turkey turkey;
    
      public TurkeyAdaper(Turkey turkey) {
        this.turkey = turkey;
      }
    
      @Override
      public void quack() {
        turkey.gobble();
      }
    
      @Override
      public void fly() {
        for (int i = 0; i < 5; i++) {
          turkey.fly();
        }
      }
    }
    
    public class Test {
      public static void main(String[] args) {
        Turkey turkey = new WildTurkey();
        Duck turkeyAdaper = new TurkeyAdaper(turkey);
        turkey.gobble();
        turkey.fly();
    
        testDuck(turkeyAdaper);
      }
    
      static void testDuck(Duck duck) {
        duck.quack();
        duck.fly();
      }
    }

    类图:

    适配器的需要进行的工作和目标接口的大小成正比。如果一个目标接口很大,不用适配器的话,就要改写原来的代码来调用这个接口,这也会很耗费力气,相比之下,还是将所有的变化封装进一个适配类中比较好。

    假如上面还有还有一个鹅(Goose)也不够用了,还需要用火鸡来充当鹅,只需要将这个适配器在实现Goose的接口,这样这个适配器技能适配成Duck还能适配成Goose。

    然后看一下类的适配器

    interface Duck {
      public void quack();
    
      public void fly();
    }
    
    interface Turkey {
      public void gobble();
    
      public void fly();
    }
    
    class WildTurkey implements Turkey {
      @Override
      public void gobble() {
        System.out.println("Gobble gobble");
      }
    
      @Override
      public void fly() {
        System.out.println("i am flying a short distance");
      }
    }

    class AnotherTurkeyAdaper extends WildTurkey implements Duck { @Override public void quack() { } }

    类图:

    类的适配器继承原有类(WildTurkey),实现目标类(Duck)。这样适配器中可以具有目标类的方法,如果要扩招原有的类也可以覆盖重写原有类的方法。(自己感觉:这种模式的原有类的基类最好是一个抽象类,这样可以继承基类获取方法,而不是去实现基类的子类)

    就代码而言,两种模式的区别是对象适配器中需要持有原对象,而类的适配器是同时扩展两个类(实际上应该是同时继承两个类,但是java不允许多继承,只好实现目标接口),不需要在适配器持有原对象,如果需要可以重写原对象的方法。无论怎么做一定要扩展目标类(Duck),因为你就是想让不是目标类(Duck)的类型可以伪装成一个目标类(Duck)。

    对象适配器利用组合,可以扩展这个类的子类。类适配器利用继承,不需要重新实现整个被适配者,必要的时候,可以重写。对象适配器是利用组合,需要持有被适配者,这样可以将工作委托给适配者进行,更有弹性,同时也用到了组合优于继承。但是这是有代价的,需要在代码中在创建一个被适配者,代码量增加,而类适配器只需要这个适配器就可以,效率高。

    外观模式:提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。

    利用了新的原则:最少知识原则(迪米特法则,不要和陌生人说话),定义类和类之间的松耦合

    首先先理解一下这个原则。(下面代码copy自 http://www.cnblogs.com/yucongblog/p/4620607.html   )
    下面这段代码严重违反迪米特法则:

    public class Teacher { 
        public void commond(GroupLeader groupLeader) {
            List<Girl> listGirls = new ArrayList<Girl>();
            for (int i = 0; i < 20; i++) {
                listGirls.add(new Girl());
            }
            groupLeader.countGirls(listGirls);
        }
    }

    我们在代码中这么写可能很常见,以后应该在写代码的时候应该想想这方面的问题。
    为什么违反了迪米特法则,在commend方法中,用到了Girl类,而Teacher类的行为commend在运行前Teacher类居然不知道还依赖了这个类。
    正确的做法:

    public class Teacher {
        public void commond(GroupLeader groupLeader) {
            groupLeader.countGirls();
        }
    }
    public class GroupLeader {
        private List<Girl> listGirls;
      
        public GroupLeader(List<Girl> _listGirls) {
            this.listGirls = _listGirls;
        }
        public void countGirls() {
            System.out.println("女生数量是:" + listGirls.size());
        }
    }

    迪米特法则希望,类中public尽量少,尽量多的private,protected,final。一定要减少类之间的耦合

    这个原则提供了一些方针:在对象的方法内,我们只能调用一下范围的方法
    1、该对想本身
    2、被当做方法的参数传进来的对象
    3、此方法所创建或实例化的任何对象  (前三点隐藏着一个注意的地方:如果某对象是调用其他方法的返回结果,不要调用改对象的方法)
    4、对象的组件

    外观模式代码:

    public class Test2Facade {
      public static void main(String[] args) {
        Facade facade = new Facade(new FacedeA(), new FacedeB(), new FacedeC());
        facade.method();
      }
    }
    
    class FacedeA {
      public void facadeA() {
    
      }
    }
    
    class FacedeB {
      public void facadeB() {
    
      }
    }
    
    class FacedeC {
      public void facadeC() {
    
      }
    }
    
    class Facade {
      private FacedeA facedeA;
      private FacedeB facedeB;
      private FacedeC facedeC;
    
      public Facade(FacedeA facedeA, FacedeB facedeB, FacedeC facedeC) {
        this.facedeA = facedeA;
        this.facedeB = facedeB;
        this.facedeC = facedeC;
      }
    
      public void method() {
        facedeA.facadeA();
        facedeB.facadeB();
        facedeC.facadeC();
      }
    }

    就是将一个大方法组成了一个小方法供client去调用。类图也不用了,基本上写代码的时候都是这么写的。要注意的就是尽量使用迪米特法则来设计外观模式。

  • 相关阅读:
    CentOS6 samba配置实例及详解
    itools airplay没出现该图标的解决方法
    修改注册表让您的Windows7系统彻底禁用U盘
    iphone 4S 下拉通知背景切换的方法
    JAVA EE Apache Zookeeper / Google Chubby
    db Oracle OLTP OLAP / apache kylin / druid
    appServer WAS / WebSphere / javacore.txt 、heapdump.phd、core.dmp、Snap.trc
    JAVA EE Code Quality / Sonar / findbugs / checkstyle / cobertura(coverage) / PMD
    OS + Linux CoreOS / Ubuntu 16.04 LTS / docker / openSTF
    App Store Review Guidelines 送審前十項常犯必檢查項目表
  • 原文地址:https://www.cnblogs.com/badboyf/p/6212815.html
Copyright © 2020-2023  润新知