本文来自李明子csdn博客(http://blog.csdn.net/free1985),商业转载请联系博主获得授权,非商业转载请注明出处!
1 目的
装饰(Decorator)提供了一种动态为对象添加额外能力的方法。
2 基本形态
装饰的基本形态如类图2-1所示。
图2-1 装饰类图
3 参与者
结合图2-1,下面介绍各类在装饰设计模式中扮演的角色。
3.1 Component
Component是业务类接口,声明了业务类需要实现的各方法。
3.2 ConcreteComponent
ConcreteComponent是具体的业务类,实现了Component接口。在装饰模式中,ConcreteComponent是实际被动态装饰的对象的类。
3.3 Decorator
Decorator是装饰者。Decorator实现了Component接口,以使它可以像被装饰对象一样使用。Decorator包含一个Component类型的成员变量,该对象在构造方法中初始化,是被装饰的对象。Decorator在实现Component接口声明的各方法时调用被装饰对象的对应方法。
通常来讲,Decorator被声明为抽象类。但如果只有一个具体装饰者,则无需添加这个抽象层。
3.4 ConcreteDecorator
ConcreteDecoratorA和ConcreteDecoratorB是具体装饰者。它们从Decorator抽象类派生,实现Component接口。
它们通过重写业务方法的形式实现对被装饰类的“扩展”。装饰模式的“扩展”方式主要是指对原有业务方法的执行前后的附加操作,因此一般来说,重写的方法将首先调用装饰类自身的前置方法,再调用从Decorator继承的对应方法(实际是调用了被装饰者的方法),最后调用自身的后置方法。
3.5 Client
Client是装饰设计模式的使用者,在图2-1中并未绘制。
4 代码实践
下面我们用一个业务场景实例来进一步讲解装饰的使用。
4.1 场景介绍
某用户控制器可以输出指定id的用户的信息。可以在业务系统中配置用户信息的缓存,有H2和Redis两种缓存可供选择。缓存不是必须的。
以下各节将介绍该场景各类的具体实现及其在装饰设计模式中所对应的参与者角色。
4.2 IUserMgmt
IUserMgmt是用户管理接口,声明了“获取用户名”接口getUserName。对应于装饰模式的参与者,IUserMgmt是业务类接口Component。下面的代码给出了IUserMgmt的声明。
package demo.designpattern.decorator;
/**
* 用户管理接口
* Created by LiMingzi on 2017/7/20.
*/
public interface IUserMgmt {
/**
* 获取用户名
* @param id 用户id
* @return 用户名
*/
String getUserName(String id);
}
4.3 UserMgmt
UserMgmt是用户管理类,实现了IUserMgmt接口。对应于装饰模式的参与者,UserMgmt是具体业务类ConcreteComponent。下面的代码给出了UserMgmt的声明。
package demo.designpattern.decorator;
/**
* 用户管理类
* Created by LiMingzi on 2017/7/20.
*/
public class UserMgmt implements IUserMgmt{
/**
* 获取用户名(demo)
*
* @param id 用户id
* @return 用户名
*/
@Override
public String getUserName(String id) {
System.out.println("debug--从数据库获取id为'"+id+"'的用户名");
if(id.equals("001")){
return "张三";
}else{
return "李四";
}
}
}
上述代码中,15行实现了接口getUserName。该方法是演示方法,实现时只是根据id枚举获取用户名,模拟从关系数据库中查询。另外,16行的调试输出可以让我们了解用户名的实际获取渠道。
4.4 UserMgmtDecorator
UserMgmtDecorator是用户管理类装饰者,实现了IUserMgmt接口。对应于装饰模式的参与者,UserMgmtDecorator是装饰者Decorator。下面的代码给出了UserMgmtDecorator的声明。
package demo.designpattern.decorator;
/**
* 用户管理类装饰者
* Created by LiMingzi on 2017/7/20.
*/
public abstract class UserMgmtDecorator implements IUserMgmt{
/**
* 用户管理接口对象
*/
private IUserMgmt userMgmt;
/**
* 构造方法
* @param userMgmt 用户管理对象
*/
public UserMgmtDecorator(IUserMgmt userMgmt) {
this.userMgmt = userMgmt;
}
/**
* 获取用户名
*
* @param id 用户id
* @return 用户名
*/
@Override
public String getUserName(String id) {
return userMgmt.getUserName(id);
}
}
上述代码中,7行,UserMgmtDecorator被声明为抽象类,因为我们实际上有两个具体装饰者UserMgmtBaseH2Cache和UserMgmtBaseRedisCache。11行,声明了用户管理接口类型的成员变量userMgmt,该变量在17行的构造方法中接受被装饰者进而初始化。28行,实现getUserName接口时,直接调用了被装饰对象的同名方法。
4.5 UserMgmtBaseH2Cache
UserMgmtBaseH2Cache是基于H2缓存的用户管理类,从UserMgmtDecorator派生,实现了IUserMgmt接口。对应于装饰模式的参与者,UserMgmtBaseH2Cache是具体装饰者ConcreteDecorator。下面的代码给出了UserMgmtBaseH2Cache的声明。
package demo.designpattern.decorator;
import java.util.HashMap;
import java.util.Map;
/**
* 基于H2缓存的用户管理类
* Created by LiMingzi on 2017/7/20.
*/
public class UserMgmtBaseH2Cache extends UserMgmtDecorator implements IUserMgmt {
/**
* 用户字典,key为用户id,value为用户名
*/
private static Map<String,String> userMap=new HashMap<>();
/**
* 构造方法
*
* @param userMgmt 用户管理对象
*/
public UserMgmtBaseH2Cache(IUserMgmt userMgmt) {
super(userMgmt);
}
/**
* 获取用户名
*
* @param id 用户id
* @return 用户名
*/
@Override
public String getUserName(String id) {
// 用户名
String name = getUserNameFromH2(id);
if(name==null){
name = super.getUserName(id);
setUserH2(id,name);
}
return name;
}
/**
* 从H2缓存获取用户名(demo)
* @param id 用户id
* @return 用户名
*/
private String getUserNameFromH2(String id){
if(userMap.containsKey(id)){
System.out.println("debug--从H2缓存获取id为'"+id+"'的用户名");
return userMap.get(id);
}
return null;
}
/**
* 将用户信息保存至H2缓存(demo)
* @param id 用户id
* @param name 用户名
*/
private void setUserH2(String id,String name){
userMap.put(id,name);
}
}
上述代码中,46行新增了“从H2缓存获取用户名”方法getUserNameFromH2,该方法从H2中查询指定用户的用户名。59行,新增了“将用户信息保存至H2缓存”方法setUserH2,将用户信息缓存至H2。31行,重写了“获取用户名”方法getUserName。33行,调用getUserNameFromH2方法从H2查询用户名。如果没有查询到,则调用被装饰者方法(35行)从关系数据库查询用户名,并将结果缓存至H2(36行)。
4.6 UserMgmtBaseRedisCache
UserMgmtBaseRedisCache是基于Redis缓存的用户管理类,从UserMgmtDecorator派生,实现了IUserMgmt接口。对应于装饰模式的参与者,UserMgmtBaseRedisCache是具体装饰者ConcreteDecorator。下面的代码给出了UserMgmtBaseRedisCache的声明。
package demo.designpattern.decorator;
import java.util.HashMap;
import java.util.Map;
/**
* 基于Redis缓存的用户管理类
* Created by LiMingzi on 2017/7/20.
*/
public class UserMgmtBaseRedisCache extends UserMgmtDecorator implements IUserMgmt {
/**
* 用户字典,key为用户id,value为用户名
*/
private static Map<String,String> userMap=new HashMap<>();
/**
* 构造方法
*
* @param userMgmt 用户管理对象
*/
public UserMgmtBaseRedisCache(IUserMgmt userMgmt) {
super(userMgmt);
}
/**
* 获取用户名
*
* @param id 用户id
* @return 用户名
*/
@Override
public String getUserName(String id) {
// 用户名
String name = getUserNameFromRedis(id);
if(name==null){
name = super.getUserName(id);
setUserRedis(id,name);
}
return name;
}
/**
* 从Redis缓存获取用户名(demo)
* @param id 用户id
* @return 用户名
*/
private String getUserNameFromRedis(String id){
if(userMap.containsKey(id)){
System.out.println("debug--从Redis缓存获取id为'"+id+"'的用户名");
return userMap.get(id);
}
return null;
}
/**
* 将用户信息保存至Redis缓存(demo)
* @param id 用户id
* @param name 用户名
*/
private void setUserRedis(String id,String name){
userMap.put(id,name);
}
}
上述代码中,46行新增了“从Redis缓存获取用户名”方法getUserNameFromRedis,该方法从Redis中查询指定用户的用户名。59行,新增了“将用户信息保存至Redis缓存”方法setUserRedis,将用户信息缓存至Redis。31行,重写了“获取用户名”方法getUserName。33行,调用getUserNameFromRedis方法从Redis查询用户名。如果没有查询到,则调用被装饰者方法(35行)从关系数据库查询用户名,并将结果缓存至Redis(36行)。
4.7 UserController
UserController类是用户控制器类,对应于装饰模式的参与者,UserController是客户Client。下面的代码给出了UserController的声明。
package demo.designpattern.decorator;
/**
* 用户控制器
* Created by LiMingzi on 2017/7/20.
*/
public class UserController {
/**
* 获取缓存类型,demo
* @return 缓存类型
*/
public String getCacheType() {
return cacheType;
}
/**
* 设置缓存类型,demo
* @param cacheType 缓存类型
*/
public void setCacheType(String cacheType) {
this.cacheType = cacheType;
}
/**
* 缓存类型
*/
private String cacheType="";
/**
* 输出用户信息
* @param id 用户id
*/
public void outputUserInfo(String id){
// 用户管理接口对象
IUserMgmt userMgmt = getUserMgmt();
if(getCacheType().equals("H2")){
userMgmt = new UserMgmtBaseH2Cache(userMgmt);
}else if(getCacheType().equals("Redis")){
userMgmt = new UserMgmtBaseRedisCache(userMgmt);
}
System.out.println(userMgmt.getUserName(id));
}
/**
* 获取用户管理对象(demo)
* @return
*/
private IUserMgmt getUserMgmt(){
return new UserMgmt();
}
}
上述代码中,32行,输出用户信息方法outputUserInfo输出指定id的用户名。34行,调用getUserMgmt方法实例化用户管理接口对象。在实际场景中,该对象的构建过程可能很复杂,也可能不在当前类中。35-39行,通过判断当前使用的缓存类型实例化具体装饰者。为了便于演示,在此提供了设置缓存类型的方法setCacheType(21行),在实际场景中该配置应该持久化于配置文件或关系数据库中。
4.8 测试代码
为了测试本文中的代码,我们可以编写如下测试代码。其中,第一组使用H2缓存,第二组使用Redis缓存,最后一组不使用缓存。
/**
* 装饰测试
*/
public static void decoratorTest(){
// 用户控制器
demo.designpattern.decorator.UserController userController = new demo.designpattern.decorator.UserController();
// 使用H2缓存,demo
userController.setCacheType("H2");
userController.outputUserInfo("001");
userController.outputUserInfo("001");
System.out.println("----------------------------------------------");
// 使用Redis缓存,demo
userController.setCacheType("Redis");
userController.outputUserInfo("001");
userController.outputUserInfo("001");
System.out.println("----------------------------------------------");
// 使用缓存,demo
userController.setCacheType("");
userController.outputUserInfo("001");
userController.outputUserInfo("001");
}
编译运行后,得到如下测试结果:
debug–从数据库获取id为’001’的用户名
张三
debug–从H2缓存获取id为’001’的用户名
张三
—————————————————————
debug–从数据库获取id为’001’的用户名
张三
debug–从Redis缓存获取id为’001’的用户名
张三
—————————————————————
debug–从数据库获取id为’001’的用户名
张三
debug–从数据库获取id为’001’的用户名
张三