设计模式-策略模式

策略模式

定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。
将共同的行为封装成策略接口,不同策略实现类只需要实现策略接口编写自己的业务代码,通过上下文获取策略类(多态机制)

角色

上下文(Context)

上下文角色主要作用于操作策略方法,屏蔽了高层模块对策略的直接访问。

抽象策略(Strategy)

对共有的行为进行封装。

具体策略(ConcreteStrategy)

实现抽象策略的具体操作。

优点

避免多重条件判断语句,多重判断不易于维护管理,而且判断和行为逻辑混用。

缺点

客户端(调用者)必须要知道所有的策略类,并且自定决定用哪一个策略类,策略模式只适用于客户端知情的情况。

应用场景

1、多个类只在行为上有稍微不同的场景。
2、策略自由切换。
3、聚合登录、聚合支付。

实例代码

是否看过这样的代码?(伪代码)
public Map login(String loginType){
    if(loginType.equals("QQ")){
        System.out.print("QQ登录");
    }else if(loginType.equals("WECHAT")){
        System.out.print("微信登录");
    }else if(loginType.equals("ali")){
         System.out.print("支付宝登录");
    }
}
实际上我们采用策略模式可以简化很多。
public Map login(String loginType){
    StrategyContext.login(loginType);
}

今天项目可能要接入QQ第三方登录,我们需要编写QQ登录的业务逻辑,老板哪天开心了又要接入微信登录这时候你又写了个微信的类,可有一天QQ不想支持登录了那你又得修改代码重新编译。
为了应付老板这种多变的需求这时候设计模式就出现啦,策略模式你值得拥有,上代码!

通过策略模式的三个角色我们可以发现,首先我们需要找出共同行为封装成一个接口策略类(Strategy)。

/**
 * 这里采用伪代码的形式封装了一个登陆的共同行为。
 */
public interface LoginStrategy {

    /**
     * 登录
     */
    void login();    
    
}

策略的具体实现类伪代码两个类(QQ、WECHAT)。

public class QQLoginStrategy implements LoginStrategy {
    @Override
    public void login() {
        System.out.println("QQ登录");
    }
}
public class WechatLoginStrategy implements LoginStrategy {

    @Override
    public void login() {
        System.out.println("微信登录");
    }
}

这里采用工厂类来初始化对象。

public class LoginStrategyFactory {

    /**
     * 采用反射机制获取Class,所以需要有地方维护classpath(枚举)
     *
     * @param strategyType
     * @return
     */
    public static LoginStrategy getLoginStrategy(String strategyType){
        try {
            String classPath = LoginEnum.valueOf(strategyType).getClassPath();
            return (LoginStrategy) Class.forName(classPath).newInstance();
        } catch (Exception e) {
            return null;
        }

    }
}

维护策略类classpath。

public enum  LoginEnum {
    QQ("com.animo.strategy.impl.QQLoginStrategy"),
    WECHAT("com.animo.strategy.impl.WechatLoginStrategy");

    private String classPath;

    LoginEnum(String classPath) {
        this.classPath = classPath;
    }

    public String getClassPath() {
        return classPath;
    }
    public void setClassPath(String classPath) {
        this.classPath = classPath;
    }
}

上下文具体实现策略。

public class LoginContextStrategy {
    /**
     * 这里按具体业务返回 这边就直接void了
     * @param strategyType
     */
    public void login(String strategyType){
        if(strategyType==null || ("").equals(strategyType)){
            System.out.println("strategyType不能为空");
            return;
        }
        LoginStrategy loginStrategy = LoginStrategyFactory.getLoginStrategy(strategyType);
        if(loginStrategy==null){
            System.out.println("没有找到具体实现");
            return;
        }
        loginStrategy.login();
    }
}

以上代码包含包含四个角色比策略模式所说的三个角色多了一个工厂类,其工厂类的作用就是多态的调用实现类。
不过这代码不适用于Web开发,可以看得出只要每一次访问工厂类中的策略类都会重新创建,只需要稍微改造一下即可。

我们首先需要在策略类上面加上@Component注解使其让Spring管理(单例)性能提升。

随后工厂类肯定就不需要啦,通过Spring管理肯定就是通过ApplicationContext上下文进行获取。

通过Spring获取的话也不需要classpath了,采用的是类首字母小写的形式获取。

在之前通过枚举类去维护classpath,转变成了数据维护。

当然转成数据库维护就要用到mybatis啦具体查询代码就不展示。

数据库表设计

自增ID
策略描述(QQ登录、微信登录)
策略Type(QQ、WECHAT)
BeanId
status(开关,可以用于控制通道是否开启)

策略实现类

头部加入@Component让Spring管理

上下文。

public class LoginContextStrategy {

   private Mapper mapper;
  
   /**
    * 这里按具体业务返回 这边就直接void了
    * @param strategyType
    */
   public void login(String strategyType){
       if(strategyType==null || ("").equals(strategyType)){
           System.out.println("strategyType不能为空");
           return;
       }
       /**
        * 1、 通过数据查询具体的策略实现Bean(判断是否有该渠道)
        *
        * 2、获取BeanId(数据库存在渠道但是没有配置该字段)
        *
        * 通过SpringUtils获取Bean
        *
        */
         Entity entity = mapper.getChannel(strategyType);
          if(entity == null ){
               // 没有找到具体的策略实现类
          }
       String beanId = entity.getBeanId();
          if(//判断是否有BeanId){
              // 没有配置 BeanId
           }
       //还可以在判断是否关闭了某个登录策略
       // SpringUtils这个工具类需自己编写
       LoginStrategy loginStrategy = SpringUtils.getBean(beanId,LoginStrategy.class);
       // loginStrategy 会采用多态的机制去调用对应的实现类方法
       loginStrategy.login();
   }
}

在项中我们只需要传入策略类型即可,例如我们采用聚合登录的例子来展示了策略模式,则Controller中就有这个一个方法。

public Map login(String loginType){
    LoginContextStrategy.login(loginType);
}

博客链接:http://www.ljyanimo.com/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容