装饰者模式
装饰者设计模式和适配器模式有点类似,算得上是适配器的一种特殊形式。个人浅显的理解,只是认为两者在某一种程度上有着细微的差别,后面会做一个对比。这里采用一个较为通俗的例子,常用的登录模式来介绍装饰者模式。
传统的登录方式就是就是根据用户名和密码进行登录,如果需要提供第三方登录方式,就需要在原有的项目上进行扩展,而这里的扩展,就需要用到装饰者模式
类图结构如下:
这里是在原有的注册登录方式进行扩展,ISigninService是原有的登录方式,SigninServiceImpl是原有登录方式的实现,下面给出代码
public interface ISigninService {
public ResultMsg regist(String username, String password);
public ResultMsg login(String username, String password);
}
public class SigninServiceImpl implements ISigninService{
@Override
public ResultMsg regist(String username, String password) {
return null;
}
@Override
public ResultMsg login(String username, String password) {
return null;
}
}
ISinginForThirdService是提供第三方登录方式的接口,该接口继承了被装饰者的接口,然后在其上面进行了扩展
public interface ISigninForThirdService extends ISigninService{
public ResultMsg loginForQQ(String id);
public ResultMsg loginForWeChat(String id);
public ResultMsg loginForToken(String id);
public ResultMsg loginForTelephone(String telephone,String code);
public ResultMsg loginForRegist(String username, String password);
}
其对应的实现类
public class SigninForThirdService implements ISigninForThirdService{
private ISigninService signinService;
public SigninForThirdService(ISigninService signinService){
this.signinService = signinService;
}
/**
* 不需要修改的,就直接调用原来的
* @param username
* @param password
* @return
*/
@Override
public ResultMsg regist(String username, String password) {
return signinService.regist(username,password);
}
/**
* 如果针对原来的方法,有修改的,这里也可以直接覆盖
* @param username
* @param password
* @return
*/
@Override
public ResultMsg login(String username, String password) {
System.out.println("传统方式登录");
return signinService.login(username,password);
}
public ResultMsg loginForQQ(String id){
System.out.println("利用QQ进行登录");
return loginForRegist(id,"QQ_EMPTY");
}
public ResultMsg loginForWeChat(String id){
System.out.println("利用微信进行登录");
return null;
}
public ResultMsg loginForToken(String id){
System.out.println("利用标示进行登录");
return null;
}
public ResultMsg loginForTelephone(String telephone,String code){
System.out.println("利用手机号进行登录");
return null;
}
public ResultMsg loginForRegist(String username,String password){
this.regist(username,"password");
ResultMsg resultMsg=this.login(username,"QQ_EMPTY");
return resultMsg;
}
}
简单的测试类
public class SigninTest {
public static void main(String[] args) {
//原来的功能依旧会对外开放,新的功能同样也可以用
//适配器中的方法,通常会打上过时的标签
ISigninForThirdService signinForService = new SigninForThirdService(new SigninServiceImpl());
signinForService.loginForQQ("testId");
}
}
装饰者模式中,其实对于被装饰者,无需要知道装饰对象的存在,装饰对象中如果需要使用被装饰对象中的方法,可以维护一个被装饰者的引用,如果单纯的是对被装饰者的方法进行增强操作,可以不用维护引用,直接覆盖掉被装饰对象中的方法即可,这也是其不同于适配器模式的地方,适配器模式中,需要通过某种方式维护被装饰者的引用。总体来说,装饰者模式中,装饰对象与被装饰者是一个is-a的关系,而适配器模式中,似乎是has-a的关系。一定层度上来说,装饰者模式好像就是多继承的一种实现而已
补充:
针对装饰者模式和适配器模式两者的区别,可以参考这篇博客——装饰者模式、适配器模式、代理模式的异同
针对装饰者模式更加深层次的理解,可以参考这篇博客,其中还对装饰者模式在IO中的应用有了介绍(实例有点绕)——深入理解装饰者模式