声明:迁移自本人CSDN博客
在常见的23种设计模式中,static proxy和decorator在代码结构上是特别相似的。那它们的不同具体体现在什么地方呢,本文就通过静态代理模式代码和装饰器模式代码的比较说明这个问题。
本文给出的例子将会尽可能简单的例子,把最核心的接口及继承关系展现出来,以期读者不被繁复的业务逻辑所束缚。
static proxy模式代码如下
接口类package com.test.designpattern.staticproxyexample;public interface Subject { void printString(String url);}
接口实现类
package com.test.designpattern.staticproxyexample;public class ConcreteSubject implements Subject { @Overridepublic void printString(String url) { String dataFormUrl = mockNetRequest(url); System.out.println("data from internet is :" + dataFormUrl);}private String mockNetRequest(String url) { return new StringBuilder("data from ").append(url).append(" is:").append("200").toString();}}
代理类,实现接口类
package com.test.designpattern.staticproxyexample;public class Proxy implements Subject { private Subject subject; public Proxy(Subject subject) { this.subject = subject;}@Overridepublic void printString(String url) { System.out.println("This is a proxy server in hongkong, you can accesss google through it"); subject.printString(url);}}
测试类
package com.test.designpattern.staticproxyexample;public class TestStaticProxy { public static void main(String[] args) { //如果这里直接访问ConcreteSubject,将因为各种原因受限而返回404,模拟代码实现作为读者的家庭作业 Subject proxySubject = new Proxy(new ConcreteSubject()); proxySubject.printString("https://xxx.xxx.xxx"); }}
运行结果如下
This is a proxy server in hongkong, you can accesss google through itdata from internet is :data from https://xxx.xxx.xxx is:200
decorator模式代码如下
接口类 package com.test.designpattern.docoratorexample;public interface Component { void printString(String s);}
接口实现类
package com.test.designpattern.docoratorexample;public class ConcreteComponent implements Component { @Override public void printString(String s) { System.out.println("Input String is:" + s); }}
抽象装饰类,提供了接口方法的一般实现,也可以不需要这个类
package com.test.designpattern.docoratorexample;public class Decorator implements Component { private Component component; public Decorator(Component c) { component = c; } @Override public void printString(String s) { component.printString(s); }}
具体装饰类,完善功能
package com.test.designpattern.docoratorexample;public class ConcreteDecoratorA extends Decorator { public ConcreteDecoratorA(Component c) { super(c); } @Overridepublic void printString(String s) { printPromptMesage(); //保留原有的功能 super.printString(s); //加强已有的功能 printStringLen(s);}private void printPromptMesage() { System.out.println("begin to output message by decorator A");}private void printStringLen(String s) { System.out.println("The length of string is:" + s.length());}}
测试类
package com.test.designpattern.docoratorexample;public class TestDecorator { public static void main(String[] args) { Component myComponent = new ConcreteComponent(); myComponent.printString("A test String"); Decorator myDecorator = new ConcreteDecoratorA(myComponent); myDecorator.printString("A test String"); }}
运行结果
Input String is:A test Stringbegin to output message by decorator AInput String is:A test StringThe length of string is:13
单从代码结构上来讲,静态代理模式和装饰器模式是一样的,但是它们有本质的不同,本文作者挑选的2个例子,尽可能把函数名都写成一样的,但是它们的内涵是不一样的。
从意图上来说,Proxy中模拟在本地直接调用接口实现类ConcreteSubject 无法实现而必须经过Proxy才能实现的功能。Decorator中模拟了完善接口实现类ConcreteComponent的功能。 作者在公司工作的时候,有一个功能是实现限流,就是控制存取磁盘数据的数据流量,这时采用的就是装饰器类而非代理类,装饰器装饰的是Java原生的InputStream、OutputStream。学习设计模式,最应该认真反复学习的书籍当然是“四人帮”编写的《Design Pattern—Elements of Reusable Object-Oriented Software》.
借用其中的一段话表述Proxy和Decorator的区别。
这2种模式都描述了怎样为对象提供一定程度上的间接引用,proxy和decorator对象的实现部分都保留了指向另一个对象的指针,它们向这个对象发送请求。然而,它们具有不同的设计目的。 像Decorator模式一样,Proxy模式构成一个对象并为用户提供一致的接口,但与Decorator模式不同的是,Proxy模式不能动态地添加或分离性质,它也不是为递归组合而设计的。它的目的是,当直接访问一个实体不方便或者不符合需要时,为这个实体提供一个替代者,例如,实体在远程设备上,访问受到限制或者实体是持久存储的。 在Proxy模式中,实体定义了关键功能,而Proxy提供(或拒绝)对它的访问。Proxy模式访问模型如下:
Client——>Proxy——>ConcreteSubject,目标在ConcreteSubject,如果Client能直接访问到ConcreteSubject,是不会去麻烦Proxy的。Decorator模式访问模型如下:
Client——>ConcreteDecorator——>ConcreteComponent,此时的目标不是ConcreteComponent了,就是ConcreteDecorator,因为ConcreteDecorator才能提供Client需要的完整功能。比如上文说的IO限流器。