1、官方描述:
为其他对象提供一种代理以控制对这个对象的访问。
2、实例讨论:
在进行应用系统开发的时候,往往会有一些信息在部署时才能够确定,并且可能会根据情况进行调整,如数据库配置信息等等,如果把这些信息直接硬编码到程序中,在需要调整时就不得不对系统进行重新编译和发布,这样不仅很麻烦,而且如果系统已经交付给用户使用,那么更是极其的不方便。
怎样解决这些配置信息问题呢,使其既便于修改,而又不对系统产生影响?一个可行的方案是把这些信息写到配置文件中,如Java项目可以写到XML文件或properties文件中,系统在需要这些信息时可以读取配置文件进行获取,这样当需要对配置信息进行调整时可以直接修改这些文件内容,而系统无需再进行编译和发布,这对于已经交付给用户的系统来说无疑也非常的方便。
那么这些信息如何获取呢?每次需要时都直接读取配置文件吗?因为这些配置信息可能会频繁的使用,如果每次都要读取配置文件,那么性能开销将是很大的。这是我们就可以采用Proxy模式,在数据访问对象上进行代理访问,每次需要这些配置信息时,都通过访问代理进行获取,访问代理判断是否已经访问过配置文件并缓存了配置信息,如果已经缓存了,就直接返回缓存的信息,否则在通过真正的DAO进行读取,这个配置文件数据访问模块类结构图如下:
3、代理类型:
Proxy模式在访问对象时提供了一定程度的间接性,这种间接性可以帮助解决某些问题,不同的Proxy解决不同类型的问题,在GOF设计模式中列举了四种代理类型,分别如下:
1)远程代理(Remote Proxy),为一个对象在不同的地址空间提供本地代表;
2)虚代理(Virtual Proxy),根据需要创建开销很大的对象;
3)保护代理(Protection Proxy),控制对原始对象的访问,用于不同的客户端有不同的访问权限;
4)智能指针(Smart Reference),取代了简单指针,在访问对象时执行一些附加操作;
在上面讨论的系统配置信息代理访问ConfigDAOProxy,属于4)智能指针的类型,在这里进行数据缓存以提高数据访问性能的附加能力。
4、实现特征:
1)Proxy和被代理的实体具有相同的接口Subject,这样在被代理实体可以使用的地方就可以用Proxy进行代替;
2)Proxy具有被代理实体的一个引用,以便在需要时把客户端的请求转发给真正的实体;
5、Java代码演示:
下面代码演示了在上面“实例讨论”部分中配置文件读取模块的Java代码结构:
类DataBaseConfig,数据库配置信息值对象:
package
qinysong.pattern.proxy;
// 值对象,数据库配置信息
public class DataBaseConfig ... {
private String dateBaseUrl;
private String dateBaseName;
private String userName;
private String password;
public String getDateBaseUrl() ...{
return dateBaseUrl;
}
public void setDateBaseUrl(String dateBaseUrl) ...{
this.dateBaseUrl = dateBaseUrl;
}
//.......
}
// 值对象,数据库配置信息
public class DataBaseConfig ... {
private String dateBaseUrl;
private String dateBaseName;
private String userName;
private String password;
public String getDateBaseUrl() ...{
return dateBaseUrl;
}
public void setDateBaseUrl(String dateBaseUrl) ...{
this.dateBaseUrl = dateBaseUrl;
}
//.......
}
接口ConfigDAO,获取配置信息的数据访问接口:
package
qinysong.pattern.proxy;
public interface ConfigDAO ... {
public DataBaseConfig getDataBaseConfig();
}
public interface ConfigDAO ... {
public DataBaseConfig getDataBaseConfig();
}
类ConfigDAOImp,实现ConfigDAO,进行实际的从配置文件中读取配置信息操作:
package
qinysong.pattern.proxy;
public class ConfigDAOImp implements ConfigDAO ... {
//通过配置文件读取数据库配置信息
public DataBaseConfig getDataBaseConfig()...{
System.out.println("ConfigDAOImp.getDataBaseConfig 调用");
DataBaseConfig dataBaseConfig = new DataBaseConfig();
//通过配置文件设置dataBaseConfig数据
//......
return dataBaseConfig;
}
}
public class ConfigDAOImp implements ConfigDAO ... {
//通过配置文件读取数据库配置信息
public DataBaseConfig getDataBaseConfig()...{
System.out.println("ConfigDAOImp.getDataBaseConfig 调用");
DataBaseConfig dataBaseConfig = new DataBaseConfig();
//通过配置文件设置dataBaseConfig数据
//......
return dataBaseConfig;
}
}
类ConfigDAOProxy,数据访问代理,实现ConfigDAO,通过ConfigDAOImp进行首次配置信息读入,并加以缓存,以后访问直接从缓存返回,提高系统性能:
package
qinysong.pattern.proxy;
import java.util.HashMap;
public class ConfigDAOProxy implements ConfigDAO ... {
//缓冲容器,保存已经读取过的配置信息
private static HashMap configs = new HashMap();
//数据访问实体的引用,以便必要时进行调用
private ConfigDAO configDAO;
//获得数据库配置信息的代理方法,若已经获取则直接返回,若首次获取则调用DAO进行获取
public DataBaseConfig getDataBaseConfig()...{
System.out.println("ConfigDAOProxy.getDataBaseConfig 调用");
if (configs.containsKey("dataBaseConfig"))...{
return (DataBaseConfig) configs.get("dataBaseConfig");
} else ...{
configDAO = new ConfigDAOImp();
DataBaseConfig dataBaseConfig = configDAO.getDataBaseConfig();
configs.put("dataBaseConfig", dataBaseConfig);
return dataBaseConfig;
}
}
}
import java.util.HashMap;
public class ConfigDAOProxy implements ConfigDAO ... {
//缓冲容器,保存已经读取过的配置信息
private static HashMap configs = new HashMap();
//数据访问实体的引用,以便必要时进行调用
private ConfigDAO configDAO;
//获得数据库配置信息的代理方法,若已经获取则直接返回,若首次获取则调用DAO进行获取
public DataBaseConfig getDataBaseConfig()...{
System.out.println("ConfigDAOProxy.getDataBaseConfig 调用");
if (configs.containsKey("dataBaseConfig"))...{
return (DataBaseConfig) configs.get("dataBaseConfig");
} else ...{
configDAO = new ConfigDAOImp();
DataBaseConfig dataBaseConfig = configDAO.getDataBaseConfig();
configs.put("dataBaseConfig", dataBaseConfig);
return dataBaseConfig;
}
}
}
类Client,客户端代码,这里对上面的类进行使用演示:
package
qinysong.pattern.proxy;
public class Client ... {
public static void main(String[] args) ...{
System.out.println("Client.main begin ......");
ConfigDAO configDao = new ConfigDAOProxy();
System.out.println("Client.main 首次调用");
DataBaseConfig databaseConfig = configDao.getDataBaseConfig();
System.out.println("Client.main 再次调用");
databaseConfig = configDao.getDataBaseConfig();
System.out.println("Client.main end ......");
}
}
public class Client ... {
public static void main(String[] args) ...{
System.out.println("Client.main begin ......");
ConfigDAO configDao = new ConfigDAOProxy();
System.out.println("Client.main 首次调用");
DataBaseConfig databaseConfig = configDao.getDataBaseConfig();
System.out.println("Client.main 再次调用");
databaseConfig = configDao.getDataBaseConfig();
System.out.println("Client.main end ......");
}
}