首页 > 设计模式 > 设计模式之策略模式

设计模式之策略模式

2014年11月30日 发表评论 阅读评论

今天我们来讨论策略模式,策略模式是一个比较难理解的模式,尤其是和工厂模式相结合时,当时看程杰的《大化设计模式》时,第二个讲的就是这个,那是一个迷迷糊糊啊,其实仔细分析策略模式并没有那么难,在我们学策略模式之前,先搞明白什么是策略呢?策略是:在某一件事上,在一定的情况下,所采取的行动。当然这个“在一定的情况下”一定是某一种情况,也即是可数的。当情况很多的时候,也许我们要用很多烦人if-else去处理,所以策略模式就是解决这类问题的,其一个典型应用就是在电商平台中:不同的用户有不同的等级享有不同的折扣,我想有些人第一想到的处理方式就是:

package cn.bridgeli.demo;

public class Strategy {
    public double discount(String grade, double total) {
        if ("A".equals(grade)) {
            return total * 0.6;
        } else if ("B".equals(grade)) {
            return total * 0.7;
        } else if ("C".equals(grade)) {
            return total * 0.8;
        } else if ("D".equals(grade)) {
            return total * 0.9;
        } else {
            return total;
        }
    }
}

我相信这么处理只要看过上篇文章的人都会想到,不满足:开闭原则!当我们新增一个策略时,肯定要该这段代码,增加一个if,当我们的策略发生变化时,例如不仅打折而且在过节时发送促销邮件之类的,那么里面的策略也要改,显然这是一个大问题,那么出现这个问题的根源就是不满足:迪米特原则。
ps:就算不用if-else,用switch-case也有同样的问题。那么用策略模式怎么处理呢?
需要我们首先定义一个策略模式的接口:

package cn.bridgeli.demo.service;

public interface UserGradeStrategyService {
    public double discount();
}

下面是具体的策略的实现


package cn.bridgeli.demo.service;

public class UserGradeAStrategyService implements UserGradeStrategyService {

    @Override
    public double discount() {
        return 0.6;
    }

}

package cn.bridgeli.demo.service;

public class UserGradeBStrategyService implements UserGradeStrategyService {

    @Override
    public double discount() {
        return 0.7;
    }

}

package cn.bridgeli.demo.service;

public class UserGradeCStrategyService implements UserGradeStrategyService {

    @Override
    public double discount() {
        return 0.8;
    }

}

package cn.bridgeli.demo.service;

public class UserGradeDStrategyService implements UserGradeStrategyService {

    @Override
    public double discount() {
        return 0.9;
    }

}

package cn.bridgeli.demo.service;

public class UserGradeEStrategyService implements UserGradeStrategyService {

    @Override
    public double discount() {
        return 1.0;
    }

}


有一个策略的容器,这个容器知道都有哪些策略,它里面包含所有的策略:


package cn.bridgeli.demo.service;

import java.util.HashMap;
import java.util.Map;

public class Container {
    private static Map<String, UserGradeStrategyService> containers = new HashMap<String, UserGradeStrategyService>();

    static {
        containers.put("A", new UserGradeAStrategyService());
        containers.put("B", new UserGradeBStrategyService());
        containers.put("C", new UserGradeCStrategyService());
        containers.put("D", new UserGradeDStrategyService());
        containers.put("E", new UserGradeEStrategyService());
    }

    public UserGradeStrategyService getDiscount(String grade) {
        if (containers.containsKey(grade)) {
            return containers.get(grade);
        }
        return null;
    }
}

那么我们的打折的具体调用方法就简单了:


package cn.bridgeli.demo.service;

import cn.bridgeli.demo.dao.UserDao;
import cn.bridgeli.entity.User;

public class UserService {
    public double discount(int userId) {
        User user = UserDao.getUserById(userId);
        String grade = user.getGrade();
        Container container = new Container();
        UserGradeStrategyService userGradeStrategyService = container.getDiscount(grade);
        return userGradeStrategyService.discount();
    }
}

这样的话,当我们有不同的策略,只需要让他继承:UserGradeStrategyService,然后在Container中注册一下就行了,不会对其他的策略有一点影响,从而实现了不同策略的变换,如果每一种策略有不同的实现,例如除了打折发送促销邮件那么只需要定义新的策略接口就行,不需要改变原有策略;同样我们在对某一等级改变打折策略的时候我们只需去改对应的打折方法就行了,这样的话是不是就满足了:单一职责原则。当然这么做肯定会有网友说,改变Container策略容器也是改变源码啊,也不满足开闭原则,为了满足这一点,我相信有些人一定想到了工厂模式,不错我们可以定义一个工厂,这个工厂可以产出不同的策略,具体这个工厂的实现我们可以去读取XML文件,这样的话,我们就可以根据用户的不同等级,拿到不同的策略,从而得到不同的打折方法,相信这么做的话,就可以满足开闭原则了吧,我想看了:单例模式、侦听者模式以及今天的策略模式之后,大家一定发现了原来“工厂模式”这么重要,当我们为了满足开闭原则时,很多时候都是依赖于工厂模式,所以在设计模式中“工厂模式”可以说无所不在,而在我们的开发中也是同样无所不在的,但是“工厂模式”的实现却又是如此的简单,所以我们不仅要学好这个模式而且还要牢牢掌握!

全文完,如果本文对您有所帮助,请花 1 秒钟帮忙点击一下广告,谢谢。

作 者: BridgeLi,https://www.bridgeli.cn
原文链接:http://www.bridgeli.cn/archives/119
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。
  1. 2015年4月11日19:36 | #1

    下午好,seo http://www.02942.cn

  1. 本文目前尚无任何 trackbacks 和 pingbacks.

请输入正确的验证码