设计模式备忘 - 结构型
适配器模式(Adapter)
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。在《设计模式》一书中将适配器模式分为类适配器模式和对象适配器模式。区别仅在于适配器角色对于被适配角色的适配是通过继承完成的还是通过组合来完成的。由于在Java中不支持多重继承,而且继承有破坏封装之嫌,故在此使用组合来代替继承。该模式包含以下组成部分:
目标(Target)角色:定义Client使用的接口。被适配(Adaptee)角色:这个角色有一个已存在并使用了的接口,而这个接口是需要我们适配的。适配器(Adapter)角色:这是适配器模式的核心。它将被适配角色已有的接口转换为目标角色希望的接口。以下我们对TextCircle类做适配,使之能满足客户端的Circle接口。
class Circle extends Shape { //这里引用了TextCircle private TextCircle tc; public Circle() { tc = new TextCircle(); //初始化 } void public display() { tc.displayIt(); //在规定的方法里面调用 TextCircle 原来的方法 }}
?
该模式与代理模式的主要区别在于代理模式应用的情况是不改变接口命名的,而且是对已有接口功能的一种控制。而适配器模式则强调接口转换。
?
题外话:在Java中有一种叫做“缺省适配模式”的应用,它和我们所讲的适配器模式是完全的两种东西。缺省适配模式是为一个接口提供缺省的实现,这样子类型就可以从缺省适配模式中进行扩展 ,避免了从原有接口中扩展时要实现一些自己不关心的接口。
?
桥梁模式(Bridge)GOF 在《设计模式》中给桥梁模式的定义为:将抽象部分与它的实现部分分离,使它们都可以独立地变化。这里的抽象部分和实现部分不是我们通常认为的父类与子类、接口与实现类的关系,而是组合关系。也就是说,实现部分是被抽象部分调用,以用来完成抽象部分的功能。
?
简单的说,你可以分析变化的种类,将不变的框架使用抽象类定义出来,然后再将变化的内容使用具体的子类来分别实现。
同时,如果在具体实现中出现了几乎重复的功能代码,则将这些行为提取出来,也采用接口的方式提供出来,然后以组合的方式将服务提供给原来的子类。这样就达到了抽象部分和实现部分独立的变化,而且还达到了实现部分的重用。该模式由以下角色组成:
以下代码来自《Thinking in Patterns with Java》:
//抽象部分的抽象角色class Abstraction { //维护着一个指向实现(Implementor)角色的引用 private Implementation implementation; public Abstraction(Implementation imp) { implementation = imp; } //下面定义了抽象部分应该有的接口 public void service1() { //使用了实现部分已有的接口 //组合实现功能 implementation.facility1(); implementation.facility2(); } public void service2() { implementation.facility2(); implementation.facility3(); }}//抽象部分的精确抽象角色class ClientService1 extends Abstraction { public ClientService1(Implementation imp) { super(imp); } //使用抽象角色提供的方法组合起来完成某项功能 //这就是为什么叫精确抽象角色(修正抽象角色) public void serviceA() { service1(); service2(); } public void serviceB() { service3(); }}//实现部分的实现角色interface Implementation { //这个接口只是定义了一定的接口 void facility1(); void facility2(); void facility3(); void facility4();}//具体实现角色就是要将实现角色提供的接口实现并完成一定的功能class Implementation1 implements Implementation {? ? //这里省略了 ......
?
使用场景:
将对象以树形结构组织起来,以达成“部分 - 整体”的层次结构,使得客户端对单个对象和组合对象的使用具有一致性。从定义中可以得到使用组合模式的环境为:在设计中想表示对象的“部分 - 整体”层次结构,希望用户忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象。其组成包括:
抽象构件角色(Component):它为组合中的对象声明接口,也可以为共有接口实现缺省行为。树叶构件角色(Leaf):在组合中表示叶节点对象 —— 没有子节点,实现抽象构件角色声明的接口。树枝构件角色(Composite):在组合中表示分支节点对象 —— 有子节点,实现抽象构件角色声明的接口,存储子部件。具体实例可参照JUnit的TestCase和TestSuite实现。
?
装饰模式(Decorator)装饰模式也叫包装器模式(Wrapper),GOF 在《设计模式》一书中给出的定义为:动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。其组成包括:
抽象构件角色(Component):定义一个抽象接口,以规范准备接收附加责任的对象。具体构件角色(Concrete Component):这是被装饰者,定义一个将要被装饰增加功能的类。装饰角色(Decorator):持有一个构件对象的实例,并定义了抽象构件定义的接口。具体装饰角色(Concrete Decorator):负责给构件添加增加的功能。以下以JUnit为例,采用装饰模式对TestCase进行扩展:
//这个就是抽象构件角色public interface Test { public abstract int countTestCases(); public abstract void run(TestResult result);}//具体构件对象,但是这里是个抽象类public abstract class TestCase extends Assert implements Test { ...... public int countTestCases() { return 1; } ...... public TestResult run() { TestResult result = createResult(); run(result); return result; } public void run(TestResult result) { result.run(this); } ......}//装饰角色public class TestDecorator extends Assert implements Test { //这里按照上面的要求,保留了一个对构件对象的实例 protected Test fTest; public TestDecorator(Test test) { fTest= test; } public void basicRun(TestResult result) { fTest.run(result); } public int countTestCases() { return fTest.countTestCases(); } public void run(TestResult result) { basicRun(result); }}//具体装饰角色,这个类的增强作用就是可以设置测试类的执行次数public class RepeatedTest extends TestDecorator { private int fTimesRepeat; public RepeatedTest(Test test, int repeat) { super(test); if (repeat < 0) throw new IllegalArgumentException("Repetition count must be > 0"); fTimesRepeat= repeat; } //看看怎么装饰的吧 public int countTestCases() { return super.countTestCases() * fTimesRepeat; } public void run(TestResult result) { for (int i= 0; i < fTimesRepeat; i++) { if (result.shouldStop()) break; super.run(result); } }}//使用的时候,就可以采用下面的方式TestDecorator test = new RepeatedTest(new TestXXX() , 3);
?
题外话:在 java.io 中,并不是纯装饰模式的范例,它是装饰模式、适配器模式的混合使用。
?
门面模式(Facade)门面模式又称外观模式。GOF在《设计模式》一书中给出如下定义:为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。该模式由三个角色组成:
门面角色(facade):这是门面模式的核心。它被客户角色调用,因此它熟悉子系统的功能。它内部根据客户角色已有的需求预定了几种功能组合。子系统角色:实现了子系统的功能。对它而言,facade角色就和客户角色一样是未知的,它没有任何facade角色的信息和链接。客户角色:调用facade角色来完成要得到的功能。使用环境:采用一个共享类来避免大量拥有相同内容的“小类”的开销。这种开销中最常见、直观的影响就是增加了内存的损耗。享元模式以共享的方式高效的支持大量的细粒度对象,减少其带来的开销。
//首先我们先实现一个抽象主题角色MyForumpublic interface MyForum { public void AddFile();}//真实的主题角色public class RealMyForum implements MyForum { public void AddFile() { //实现具体方法 ...... }}//代理主题角色public class MyForumProxy implements MyForum { private RealMyForum forum = new RealMyForum(); private int permission; //权限值 public MyForumProxy(int permission) { this.permission = permission; } //实现的接口 public void AddFile() { //满足权限设置的时候才能够执行操作 //Constants 是一个常量类 if (Constants.ASSOCIATOR == permission) { forum.AddFile(); } else System.out.println("You are not a associator of MyForum!"); } }}?
?