什么是设计模式:在我们进行程序设计时,逐渐形成了一些典型问题和问题的解决方案,这就是软件模式;每一个模式描述了一个在我们程序设计中经常发生的问题,以及该问题的解决方案;当我们碰到模式所描述的问题,就可以直接用相应的解决方法去解决这个问题,这就是设计模式。
设计模式就是抽象出来的东西,它不是学出来的,是用出来的;或许你根本不知道任何模式,不考虑任何模式,却写着最优秀的代码,即使以“模式专家”的角度来看,都是最佳的设计,不得不说是“最佳的模式实践”,这是因为你积累了很多的实践经验,知道“在什么场合代码应该怎么写”,这本身就是设计模式。
有人说:“水平没到,学也白学,水平到了,无师自通”。诚然,模式背熟,依然可能写不出好代码,更别说设计出好框架;OOP理解及实践经验到达一定水平,同时也意味着总结了很多好的设计经验,但"无师自通",却也未必尽然,或者可以说,恰恰是在水平和经验的基础上,到了该系统的学习一下“模式”的时候了,学习一下专家总结的结果,印证一下自己的不足,对于提高水平还是很有帮助的。
本系列的设计模式学习笔记,实际是对于《Java与模式》这本书的学习记录。
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
代理模式的英文叫做Proxy或Surrogate,中文都可译成“代理”。所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理模式和适配器模式的区别:适配器模式的用意是要改变所考虑的对象的接口,而代理模式并不能改变所代理对象的接口。
代理模式和装饰模式的区别:装饰模式应当为所装饰的对象提供增强功能,而代理模式对对象的使用施加控制,并不提供对象本身的增强功能。
代码模式和门面模式:有时候,门面模式兼任代理的职责,这时候,门面模式又叫做代理门面模式,或门面代理模式。
(1)抽象主题角色:声明了真实主题和代理主题的共同接口,这样一来在任何可以使用真实主题的地方都可以使用代理主题。
(2)代理主题(Proxy)角色:代理主题角色内部含有对真实主题的引用,从而可以在任何时候操作真实主题对象;代理主题角色提供一个与真实主题角色相同的接口,以便可以在任何时候都可以替代真实主题,控制对真实主题的引用,负责在需要的时候创建真实主题对象(和删除真实主题对象);代理角色通常在将客户端调用传递给真实主题之前或者之后,都要执行某个操作,而不是单纯地将调用传递给真实对象。
(3)真实主题角色:定义了代理角色所代表的真实对象。
abstract class Subject
{
public abstract void request();
}
class RealSubject extends Subject
{
public RealSubject() {}
public void request()
{
System.out.println("From real subject");
}
}
class ProxySubject extends Subject
{
private RealSubject realSubject;
public ProxySubject() {}
public void request()
{
preRequest();
if(realSubject == null)
{
realSubject = new RealSubject();
}
realSubject.request();
postRequest();
}
//请求前的操作
private void preRequest()
{
//something you want to do before requesting
}
//请求后的操作
private void postRequest()
{
//something you want to do after requesting
}
}
自从JDK3之后,提供了反射类库,可以在运行期创建代理对象。下面的例子讲解java.lang.reflect.Proxy的使用方法。首先,这个例子为Vector对象提供一个代理对象,当Vector的任何方法被调用之前和调用之后,分别打印出两条信息,这表明代理对象有能力截获和控制这个Vector对象。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.lang.reflect.Method;
import java.util.*;
class VectorProxy implements InvocationHandler
{
private Object proxyobj;
public VectorProxy(Object obj)
{
this.proxyobj = obj;
}
public static Object factory(Object obj)
{
Class cls = obj.getClass();
return Proxy.newProxyInstance(cls.getClassLoader(),
cls.getInterfaces(),
new VectorProxy(obj));
}
public Object invoke(Object proxy,Method method,Object[] args) throws Exception
{
System.out.println("before calling " + method);
if(args != null)
{
for(int i=0;i
如果按照使用目的来划分,代理有以下几种:远程(Remote)代理、虚拟(Virtual)代理、Copy-on-Write代理、保护(Protect or Access)代理、Cache代理、防火墙(Firewall)代理、同步化(Synchronization)代理、智能引用(Smart Reference)代理。
常用的有如下四种:
(1)远程代理:代理远程访问,优点是隐藏网络细节,可以像访问本地对象一样进行远程访问;缺点是客户可能没有意识到会启动一个耗费时间的远程调用,因此,可能没有必要的思想准备。
(2)保护代理:可以在运行期进行权限检查,核实后将调用传递给被代理的对象。
(3)智能引用代理:在访问一个对象时,执行一些内务处理(Housekeeping)操作,比如计数操作等。
(4)虚拟代理:使用虚拟代理模式的优点是代理对象可以在必要的时候才将被代理的对象加载。延迟加载模式。
class Client
{
private static Searcher searcher;
public static void main(String[] args)
{
searcher = new Proxy();
String userId = "Admin";
String searchType = "SEARCH_BY_ACCOUNT";
String result = searcher.doSearch(userId,searchType);
System.out.println(result);
}
}
interface Searcher
{
String doSearch(String userId,String searchType);
}
class Proxy implements Searcher
{
private RealSearcher searcher;
private UsageLogger usageLogger;
private AccessValidator accessValidator;
public Proxy()
{
searcher = new RealSearcher();
}
//查询操作
public String doSearch(String userId,String keyValue)
{
if(checkAccess(userId))
{
String result = searcher.doSearch(null,keyValue);
logUsage(userId);
return result;
} else {
return null;
}
}
//查询前的授权操作
private boolean checkAccess(String userId)
{
accessValidator = new AccessValidator();
return accessValidator.validateUser(userId);
}
//查询后的日志操作
private void logUsage(String userId)
{
UsageLogger logger = new UsageLogger();
logger.setUserId(userId);
logger.save();
}
}
class RealSearcher implements Searcher
{
public RealSearcher() {}
//真实的查询
public String doSearch(String userId,String keyValue)
{
return "真实的查询结果";
}
}
class AccessValidator
{
//用户的权限查询发生在这里
public boolean validateUser(String userId)
{
if(userId.equals("Admin"))
{
return true;
} else {
return false;
}
}
}
class UsageLogger
{
private String userId;
public void setUserId(String userId)
{
this.userId = userId;
}
//记录日志
public void save()
{
//...
}
public void save(String userId)
{
this.userId = userId;
save();
}
}
关于代理模式,日常开发过程中的使用场景还是很多的。比如调用WebService,我们总会自动或手动生成客户端的代理类,这是远程代理模式;对于登录,也总可以使用保护代理和智能引用代理。
关于保护代理和智能引用代理,我们过去可能经常把代理主题角色和实际主题角色合并,可以试着按照代理模式的职责进行拆分。
结构模式(Structural Pattern)一共有七种,分别是:适配器模式、装饰模式、合成模式、代理模式、享元模式、门面模式、桥梁模式。
大致总结如下:
最大特点 | 典型应用 | |
适配器模式 | 利用对象的功能,并转换其接口 | 日常工作,入目尽是适配器,DAO适配,Cache功能适配,等等 |
装饰模式 | 对象层面的功能增强,接口不变 | Java I/O类库的设计 |
合成模式 | 树枝、叶子同样对待 | 分类树、权限树等 |
代理模式 | 代表人 | WebService 的本地代理,权限访问代理,引用计数代理等 |
享元模式 | 共享对象,减小内存占用 | 编译器系统,Java String |
门面模式 | 统一对外接口,方便调用 | 基于SOA框架编程中,不同系统之间的接口 |
桥梁模式 | 解耦 | 大多数的驱动器,包括JDBC Driver |