策略模式

什么是策略模式?

策略模式是一种用来封装变化, 让算法的变化不影响使用的客户,并且可以灵活的替换各种算法(概念这种东西太死板了,要理解它,灵活使用它, 而不是背过它 !!)

使用场景

  • 平时我们登录网站的时候可以看到有多种选择, 普通的用户密码登录、微信登录、qq登录等, 这里我们就用策略模式(实际上采用简单工厂也可以)来模拟这种情景。
  • 注: 这里采用接口实现,有些书籍可能是采用抽象类, 模式要活用、活用、活用

策略模式实现

public interface LoginWay {
    Boolean login();
}

public class WeChat implements LoginWay {
    
    @Override
    public Boolean login() {
        System.out.println("欢迎使用微信登录, 登录成功");
        return true;
    }
}

public class QQ implements LoginWay {

    @Override
    public Boolean login() {
        System.out.println("欢迎使用qq登录, 登录成功");
        return true;
    }
}
public class LoginStategy {
    LoginWay loginWay;

    public LoginStategy(LoginWay loginWay) {
        this.loginWay = loginWay;
    }

    public Boolean login(){
        return loginWay.login();
    }
}
public class LoginStategyTest {

    /**
     * 基础的策略模式需要在客户端中选择策略, 可以结合简单工厂模式将策略封装在策略类中
     * 如果将具体类创建过程封装到LoginStategy, 实际上在客户端只会了解LoginStategy, 相对于单独的简单工厂方法降低了耦合度
     *
     * 扩展一种新的方式:
     *  1. 扩展一个新的登录方式(loginway)
     *  2. UI端增加一个选项
     *  3. 客户端需要增加一个分支(如果采用简单工厂也只是把客户端的逻辑封装在了工厂中, 一样需要增加分支(如果再结合反射, 完美))
     */
    @Test
    public void testLogin() {

        //界面选择登录方式
        String loginWay = "wechat";
        LoginStategy loginStategy ;
        if(loginWay.equalsIgnoreCase("wechat")){
            loginStategy = new LoginStategy(new WeChat());
        }else if(loginWay.equalsIgnoreCase("qq")){
            loginStategy = new LoginStategy(new QQ());
        }else if(loginWay.equalsIgnoreCase("github")){
            loginStategy = new LoginStategy(new GitHub());
        }else {
            loginStategy = new LoginStategy(new Normal());
        }
        assertTrue(loginStategy.login());
        //新加一个登录方式, 需要修改客户端的分支条件

    }
}
public class LoginWayFactory {
    
      //如果采用简单工厂实现 登录方式选择, 也只是把客户端的判断放到了后台而已
      // 所以我们可以约定好传入参数的格式, 这样就可以利用反射去掉if switch的判断逻辑
    public LoginWay createLoginWay(String loginWay) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        if(loginWay.equalsIgnoreCase("wechat")){
            return new WeChat();
        }else if(loginWay.equalsIgnoreCase("qq")){
            return new QQ();
        }else if(loginWay.equalsIgnoreCase("github")){
            return new GitHub();
        }else {
            return new Normal();
        }

        //利用反射 根据传入类型生成实体类
        //  loginWay = "Normal";
        //  Class<?> clazz = Class.forName("com.lx.designpattern.loginway." + loginWay);
        //  return (LoginWay) clazz.newInstance();

    }
}
public class LoginWayFactoryTest {
    /**
     * 使用简单工厂方式 我们在客户端我们需要了解到俩个类 LoginWayFactory LoginWay 参与到编译中
     *
     */
    @Test
    public void testLogin() throws IllegalAccessException, InstantiationException, ClassNotFoundException {

        LoginWayFactory factory = new LoginWayFactory();
        //界面选择不同的登录方式
        LoginWay loginWay = factory.createLoginWay("wechat");
        assertTrue(loginWay.login());
        loginWay = factory.createLoginWay("qq");
        assertTrue(loginWay.login());

        //新加一个登录方式, 需要修改factory总的分支条件, 需要loginwayfactory参与编译
        loginWay = factory.createLoginWay("githubxx");
        assertTrue(loginWay.login());
    }
}
public LoginStategy(String loginWay) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//        if(loginWay.equalsIgnoreCase("wechat")){
//            this.loginWay = new WeChat();
//        }else if(loginWay.equalsIgnoreCase("qq")){
//            this.loginWay = new QQ();
//        }else if(loginWay.equalsIgnoreCase("github")){
//            this.loginWay = new GitHub();
//        }else {
//            this.loginWay = new Normal();
//        }
        Class<?> clazz = Class.forName("com.lx.designpattern.loginway." + loginWay);
        this.loginWay = (LoginWay) clazz.newInstance();
}
  /**
     * 测试采用策略模式与简单工厂反射结合
     */
    @Test
    public void testLogin2() throws IllegalAccessException, InstantiationException, ClassNotFoundException {

        //约定好登录方式标识,传入的标识直接就是类名
        String loginWay = "WeChat";
        LoginStategy loginStategy = new LoginStategy(loginWay);
        assertTrue(loginStategy.login());

    }

使用策略模式与简单工厂结合的好处:

  • 如果使用最后一种方式, 如果需要扩展一种新的方式,只需要扩展具体的实现类, 并且在ui上约定好返回字符串即可
  • 再次强调, 设计模式是要灵活,混合的使用, 设计模式是思想, 是为了更方便优雅的扩展、封装

代码路径

https://github.com/offline7LY/designpattern/tree/master/strategy

posted @ 2018-03-13 21:56  p205  阅读(170)  评论(0编辑  收藏  举报