适配器模式:将一个类东街口转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。
适配器模式有两种,对象适配器和类的适配器。先看一下对象适配器。
还是看最开始鸭子的例子,如果此时鸭子不够了,需要一个火鸡来充当一个鸭子。
对象适配器
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去调用。类图也不用了,基本上写代码的时候都是这么写的。要注意的就是尽量使用迪米特法则来设计外观模式。