设计模式之装饰器模式(java实现)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/harryptter/article/details/84839703

装饰器模式(Decorator):结构型设计模式,为了实现类在不修改原始类的基础上进行动态的覆盖或者增加方法,该实现保持了跟原有类的层级关系。这种设计模式允许向一个现有的对象添加新的功能,同时又不改变其结构。算是一种非常特殊的适配器模式。

在实际业务中,有时候我们会创建了多层子类,但如果当子类层数超过三层,一般来说不太建议,这个时候可以考虑使用装饰器模式。

Spring中的应用场景:在我们的项目中遇到这样一个问题:我们的项目需要连接多个数据库,而且不同的客户在每 次访问中根据需要会去访问不同的数据库。我们以往在 Spring 和 Hibernate 框架中总是配置一个数据 源,因而 SessionFactory 的 DataSource 属性总是指向这个数据源并且恒定不变,所有 DAO 在使用SessionFactory 的时候都是通过这个数据源访问数据库。但是现在,由于项目的需要,我们的 DAO 在 访问 SessionFactory 的时候都不得不在多个数据源中不断切换,问题就出现了:如何让SessionFactory 在执行数据持久化的时候,根据客户的需求能够动态切换不同的数据源?我们能不能 在 Spring 的框架下通过少量修改得到解决?是否有什么设计模式可以利用呢?
首先想到在 Spring 的 ApplicationContext 中配置所有的 DataSource。这些 DataSource 可能是各 种不同类型的,比如不同的数据库:Oracle、SQL Server、MySQL 等,也可能是不同的数据源:比如Apache 提 供 的 org.apache.commons.dbcp.BasicDataSource 、 Spring 提 供 的org.springframework.jndi.JndiObjectFactoryBean 等。然后 SessionFactory 根据客户的每次 请求,将 DataSource 属性设置成不同的数据源,以到达切换数据源的目的。


由于装饰器模式算是一种非常特殊的适配器模式,所以,我这边使用之前适配器模式中的那个例子进行改造,使用装饰器模式来实现。之前适配器模式的java例子

依旧是新增登录类别,不过此时不同的是新增两个接口ISignInService,ISignInForThirdService.

ISignInForThirdService是继承于ISignInService的,SignInForThirdService不再是对SignInService的继承,而是对ISignInForThirdService的实现

类图

具体实现:

User类:

package Decorator;

public class User {
    private String username;
    private String password;
    private String mid;
    private String info;

    public User() {
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getMid() {
        return mid;
    }

    public void setMid(String mid) {
        this.mid = mid;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }
}

 ResultMsg:

package Decorator;

public class ResultMsg {
    private int code;
    private String msg;
    private Object data;

    public ResultMsg( int code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}

ISignInService接口和SignInService类:

package Decorator;

public interface ISignInService {
    /**
     * 注册接口
     * @param username
     * @param password
     * @return
     */
    public ResultMsg register(String username, String password);


    /**
     * 登录的接口
     * @param username
     * @param password
     * @return
     */
    public ResultMsg login(String username, String password);
}
package Decorator;

public class SignInService implements ISignInService {
    /**
     * 注册方法
     * @param username
     * @param password
     * @return
     */
    public ResultMsg register(String username, String password){
        return  new ResultMsg(200,"注册成功",new User());
    }


    /**
     * 登录的方法
     * @param username
     * @param password
     * @return
     */
    public ResultMsg login(String username, String password){
        System.out.println("登陆成功");
        return null;
    }
}

 ISignInForThirdService接口和SignInForThirdService类:

package Decorator;

public interface ISignInForThirdService extends ISignInService {
    public ResultMsg loginForQQ(String openId);

    public ResultMsg loginForWechat(String openId);

    public ResultMsg loginForToken(String token);

    public ResultMsg loginForTelephone(String telephone, String code);

    public ResultMsg loginForRegister(String username, String password);

    public ResultMsg login(String username, String password);
}
package Decorator;

public class SignInForThirdService implements ISignInForThirdService {

    private ISignInService service;
    public SignInForThirdService(ISignInService service){
        this.service = service;
    }

    @Override
    public ResultMsg register(String username, String password) {
        return service.register(username,password);
    }

    @Override
    public ResultMsg login(String username, String password) {
        return service.login(username,password);
    }


    public ResultMsg loginForQQ(String openId){
        //1、openId是全局唯一,我们可以把它当做是一个用户名(加长)
        //2、密码默认为QQ_EMPTY
        //3、注册(在原有系统里面创建一个用户)

        //4、调用原来的登录方法
        String QQDefaultPasswords = "QQ_EMPTY";
        //这里省略查重验证,默认为新用户,实际项目执行会有

        System.out.println("QQ登录");

        return loginForRegister(openId,QQDefaultPasswords);
    }

    public ResultMsg loginForWechat(String openId){
        String WechatDefaultPasswords = "WECHAT_EMPTY";
        System.out.println("wechat登录");
        return loginForRegister(openId,WechatDefaultPasswords);

    }

    public ResultMsg loginForToken(String token){
        //通过token拿到用户信息,然后再重新登陆了一次
        User user = new User();
        System.out.println("token自动登录");
        return login(user.getUsername(),user.getPassword());
    }

    public ResultMsg loginForTelephone(String telephone, String code){

        String telephoneDefaultPasswords = "TELEPHONE_EMPTY";
        System.out.println("手机号登录");
        return loginForRegister(telephone,telephoneDefaultPasswords);
    }

    public ResultMsg loginForRegister(String username, String password){
        this.register(username,password);
        return this.login(username,password);
    }

}

比较特殊的是实际的调用方式的实现SignInForThirdServiceTest:

package Decorator;

public class SignInForThirdServiceTest {
    public static void main(String[] args) {

        ISignInForThirdService iSignInForThirdService = new SignInForThirdService(new SignInService());

        //原来的功能依旧对外开放,依旧保留
        //新的功能同样的也可以使用
        iSignInForThirdService.loginForQQ("sdfgdgfwresdf9123sdf");
        iSignInForThirdService.loginForTelephone("1560017471","sdha");
        iSignInForThirdService.loginForToken("dsajdsakldjksafjhfkasljkla");
        iSignInForThirdService.loginForWechat("dhafkahkjdsada");

    }
}

使用SignInForThirdService来装饰SignInService;

执行结果:

以上就是一个装饰器模式的实现了。

可以看到装饰器模式可以替代继承,来实现功能。因此,但我们要实现一个类的功能扩展的时候,可以考虑装饰器模式。

那么装饰器模式和适配器模式的差别是哪些呢?

装饰器模式 适配器模式
是一种非常特别的适配器模式  可以不保留层级关系

装饰者和被装饰者都要实现同一个接口,

主要目的是为了扩展,依旧保留OOP关系 

适配者和被适配者没有必然的层级联系

通常采用代理或者继承形式进行包装

满足is-a的关系   满足has-a关系
注重的是覆盖、扩展   注重兼容、转换

在Spring 源码中,但类名里面有 Wrapper,Decorator,基本上可以认为是装饰器模式。

猜你喜欢

转载自blog.csdn.net/harryptter/article/details/84839703