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去调用。类图也不用了,基本上写代码的时候都是这么写的。要注意的就是尽量使用迪米特法则来设计外观模式。

原文地址:https://www.cnblogs.com/badboyf/p/6212815.html