-
需求分析:
假设现在有一款非常火热的游戏, 有很多人都在玩, 有的人为了升级甚至还通宵通宵的玩.public interface GamePlayer { void login(Stirng username, String password); void killBoss(); void upgrade(); } public RealGamePlayer implements GamePlayer { public void login(String username, String password) { System.out.println("登录"); } public void killBoss() { System.out.println("打怪"); } public void upgrade() { System.out.println("升级"); } }但是可能由于某些人技术不好一直都没能升级, 这时候就想通过游戏代练来帮助他们升级. 这时候就可以采用代理模式来完成.
public class GamePlayerProxy implements GamePlayer { private GamePlayer gamePlayer; public GamePlayerProxy(GamePlayer gamePlayer) { this.gamePlayer = gamePlayer; } public login(String username, String password) { this.gamePlayer.login(username, password); } public killBoss() { this.gamePlayer.killBoss(); } public void upgrade() { this.gamePlayer.upgrade(); } } public class Client { public static void main(String[] args) { RealGamePlayer gamePlayer = new RealGamePlayer(); GamePlayerProxy proxy = new GamePlayerProxy(gamePlayer); proxy.login("zhangsan", "123456"); proxy.killBoss(); proxy.upgrade(); } }这里游戏代理负责对游戏玩家的各种动作的应用.
-
代理模式的定义:
Provide a surrogate or placeholder for another object to control access to it(为其他对象提供一种代理以控制对这个对象的访问).
-
代理模式的应用:
在实际应用中, 代理模式一般都以动态代理的形式使用的. 例如 Spring 框架中的 AOP 以及 Mybatis 框架中将 Mapper 接口映射为底层的 JDBC 查询都使用到了动态代理. 下面我们使用 JDK 的动态代理来实现一个简单的查询数据的实例./** * 代表人的类 */ public class Person { private int id; private String name; public int getId() { return this.id; } public void setId(int id) { this.id = id; } public String getName() { return this.name; } public void setName(String name) { this.name = name; } } /** * 业务层接口 */ public interface PersonService { Person getById(int id); void setConnection(Connection conn); } /** * 业务层实现 */ public class PersonServiceImpl implements PersonService { private Connection conn; pulic Person getById(int id) { Statement statement = conn.createStatement(); String querySql = "SELECT * FROM person WHERE id = " + id; ResultSet resultSet = statement.executeQuery(querySql); Person person = new Person(); resultSet.next(); person.setId(resultSet.getInt(1)); person.setName(resultSet.getString(2)); return person; } public void setConnection(Connection conn) { this.conn = conn; } } /** * JDBCHandler 用于为每个查询建立数据库连接 */ public class JDBCInvocationHandler implements InvocationHandler { // 被代理对象, 相当于类图中的 RealSubject private PersonService personService; public JDBCInvocationHandler(PersonService personService) { this.personService = personService; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Class.forName("com.mysql.jdbc.Driver"); Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/proxy", "root", "123456"); personService.setConnection(connection); // 这里就相当于调用被代理对象 Object result = method.invoke(personService, args); connection.close(); return result; } } /** * 客户端代码 */ public class Client { public static void main(String[] args) throws Exception { PersonServiceImpl personServiceImpl = new PersonServiceImpl(); JDBCInvocationHandler invocationHandler = new JDBCInvocationHandler(personServiceImpl); // 这里就是类图中的 Proxy, 这里是 JDK 动态的建立代理和被代理之间的联系 PersonService personServiceProxy = (PersonService) Proxy.newProxyInstance(PersonService.class.getClassLoader(), new Class<?>[] {PersonService.class}, invocationHandler); // 这里一样通过调用代理对象的方法来实现最终调用被代理对象的方法 personServiceProxy.getPersonById(); } }在传统的 JDBC 编码过程中, 每次需要写大量的建立数据库连接的代码, 但实际上核心的代码就是查询数据的那几行代码. 在这里我们通过使用代理模式, 消除了建立重复性的代码, 当然这里仍然不够灵活. 在调用
PersonService的实现类的方法之前, 都可以为PersonService对象获取到一个数据库连接, 这样在方法中只用实现最核心的代码即可. -
参考:
[1] : Java 库中的设计模式
[2] : 设计模式之禅
[3] : Head First 设计模式
[4] : 疯狂 Java 讲义