介绍
标准定义:装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
通俗理解:为了某个实现类,在不修改原始类的基础上进行动态的覆盖或增加方法实现类要保持跟原有类的层级关系。(在spring中通常包含Decorator、Wrapper都属于装饰器模式)
如:
InputStream is=null;
FilterInputStream fis=new DataInputStream(is);
这里DataInputStream相比InputStream内容更丰富,但是DataInputStream它本身还是个 InputStream,相当于DataInputStream对InputStream做下美工装饰之后,得到了一个新的InputStream。
案例
场景:拿之前的适配器模式来举例,老系统比较稳定,代码不能修改。而新的需求是希望能够提供第三方的登录,所以不得不对老系统做一些文章。
代码:
1》老系统的登录注册接口
public interface ISignService {
/**
* 用户注册
* @param username
* @param password
* @return
*/
public ResultMsg register(String username, String password);
/**
* 用户登录
* @param username
* @param password
* @return
*/
public ResultMsg login(String username, String password);
}
2》老系统的登录注册实现类
public class SignService implements ISignService{
/**
* 用户注册
* @param username
* @param password
* @return
*/
public ResultMsg register(String username, String password){
return new ResultMsg("200","注册成功",new com.taofut.sjms.adapter.passport.User());
}
/**
* 用户登录
* @param username
* @param password
* @return
*/
public ResultMsg login(String username, String password){
return new ResultMsg("200","登录成功",new com.taofut.sjms.adapter.passport.User());
}
}
3》新系统不希望改变老系统的代码,所以用一个新的接口来继承老系统的登录注册接口
public interface ISignForThirdService extends ISignService{
public ResultMsg loginForQQ(String openid);
public ResultMsg loginWechat(String openid);
public ResultMsg loginForToken(String token);
public ResultMsg loginForTel(String tel,String code);
public ResultMsg loginForRegist(String username,String password);
}
4》新系统接口实现类
public class SignForThirdService implements ISignForThirdService{
private ISignService signService;
public SignForThirdService(ISignService signService) {
this.signService = signService;
}
@Override
public ResultMsg register(String username, String password) {
return signService.register(username,password);
}
@Override
public ResultMsg login(String username, String password) {
return signService.login(username,password);
}
public ResultMsg loginForQQ(String openid){
//1.openid是全局唯一的,我们可以把它当成是一个用户名
//2.密码默认为taofut_xxx,这个只有我们内部开发人员知道
//3.注册(在原有的系统里注册一个用户)
//4.调用原来的登录方法
return loginForRegist(openid,"taofut_xxx");
}
public ResultMsg loginWechat(String openid){
return null;
}
public ResultMsg loginForToken(String token){
return null;
}
public ResultMsg loginForTel(String tel,String code){
return null;
}
public ResultMsg loginForRegist(String username,String password){
//调用原来的注册方法
this.register(username,password);
//调用原来的登录方法
return this.login(username,password);
}
}
5》测试类
public class SignTest {
public static void main(String[] args) {
//原来的功能依旧对外开放、依旧保留
//新的功能同样可以使用
SignForThirdService signForThirdService=new SignForThirdService(new SignService());
signForThirdService.loginForQQ("dsdsdsdsdsd123sdsd");
}
}
总结:以上案例保证了老系统的稳定,在新系统实现第三方登录时,也延用了老系统的登录注册方法,其实这种方式也是适配器模式的一种特例。
如:以上测试类,老系统的SignService实例传给新实现类SignForThirdService类做包装,然后得到一个SignForThirdService,但是SignForThirdService其实还是一个ISignService。
那么装饰器模式跟适配器模式的区别在哪里?
装饰器模式:
(1)会保留层级关系,如:SignForThirdService还是一个ISignService。
(2)装饰者和被装饰者都要实现同一个接口,如:都实现了ISignService接口,主要是为了很好的扩展。
(3)满足is-a的关系,也就是说不管SignForThirdService怎么去装饰,它始终是一个ISignService。
(4)侧重点在覆盖和扩展。
适配器模式:
(1)可以不保留层级关系,是一种非常特殊的适配器模式。
(2)适配者和被适配者没有必然的层级联系,采用静态代理或继承的方式实现包装。
(3)满足has-a关系,充电器插头可以转换为苹果接口,安卓接口,但是苹果跟安卓没关系。
(4)侧重点在兼容和转换。