在阅读前需要对策略模式、状态模式、枚举有所了解。
例子:
文件分享,分享表需要记录分享期限,简化表结构如下
id | time_limit | create_time |
id为主键,time_limit为分享期限,有1,2,3三个值,其中1代表永久,2代表一周,3代表一天,create_time分享创建时间。
id | share_id | resource_id |
id为主键,share_id为分享表的id,resource_id为文件的id,分享和文件为多对多的关系。
查询到一条share记录后,需要计算当前的分享是否过期。
如果需要测试以下代码将所有的Share share = shareService.getByUserId(userId);替换为Share share = new Share();
根据share表结构新建一个class Share自定义属性即可。
通过if else的解决办法:
@Controller
public Share getShare(String userId) {
Share share = shareService.getByUserId(userId);
if(isValidShare(share)) {
return share;
}
return null;
}
// 判断是否分享是否过期
private boolean isValidShare(Share Share){
Integer timeLimit = share.getTimeLimit();
int day = 0;
// 将数据库数据转化为实际计算的天数
if(timeLimit.equals(1)) {
day = 1;
}else if(timeLimit.equals(2)) {
day = 7;
}
// 计算天数
if(day == 0){
return true;
}
if(System.currentTimeMillis() < createTime.getTime() + day * 24 * 60 * 60 * 1000) {
return true;
}
return false;
}
通过以上代码,如果我想增加一个时间限制类型,如分享期限为一个月,那么我就需要在原来的if else中再添加一个if else。
// 将数据库数据转化为实际计算的天数
if(timeLimit.equals(1)) {
day = 1;
}else if(timeLimit.equals(2)) {
day = 7;
}else if(timeLimit.equals(4)) {
day = 30;
}
设计模式原则之一——开闭原则:对扩展开放,对修改关闭。
显然,在原来的代码上增加一个if else这不符合上面的原则。
策略模式:
策略类
/**
*抽象父类
*/
public abstract class CountTimeStrategy{
protect abstract getDay(); // 获取实现类的天数
public boolean isValid(Share share){
Date createTime = share.getCreateTime();
if(getDay() == 0){
return true;
}
return System.currentTimeMillis() < createTime.getTime() + getDay() * 24 * 60 * 60 * 1000;
}
}
/*
* “一天”的实现类
*/
public class OneDayStrategy extends CountTimeStrategy{
@Override
int getDay(){
return 1;
}
}
/*
* “一周”的实现类
*/
public class WeekStrategy extends CountTimeStrategy{
@Override
int getDay(){
return 7;
}
}
/*
* “永远”的实现类
*/
public class ForeverStrategy extends CountTimeStrategy{
@Override
int getDay(){
return 0;
}
}
controller
@Controller
public Share getShare(String userId) {
Share share = shareService.getByUserId(userId);
if(isValidShare(share)) {
return share;
}
return null;
}
// 判断是否分享是否过期
private boolean isValidShare(Share Share){
Integer timeLimit = share.getTimeLimit();
// 判断使用哪个策略
CountTimeStrategy countTimeStrategy;
if(timeLimit.equals(1)) {
countTimeStrategy = new OneDayStrategy();
}else if(timeLimit.equals(2)) {
countTimeStrategy = new WeekStrategy();
}else if(timeLimit.equals(3)) {
countTimeStrategy = new ForeverStrategy();
}
return countTimeStrategy.isValid(timeLimit)
}
虽然策略模式已经把实现抽离了主方法,但还是免不了使用if else,免不了if else,就无法很好的对修改关闭,究其原因是因为程序无法自动判断使用哪个实现类。
如何自动判断使用哪个策略实现类呢?不妨自己思考一下,有好的办法欢迎分享留言。
我的解决办法是使用map,在CountTimeStrategy中添加一个key为判断条件,value为实现类的map,修改后代码如下
使用map代替if else
/**
*抽象父类
*/
public abstract class CountTimeStrategy{
// key-判断条件 value-实现类的map
Map<Integer, CountTimeStrategy> strategyMap = new HashMap<>;
protect abstract getDay(); // 获取实现类的天数
public boolean isValid(Share share){
Date createTime = share.getCreateTime();
if(getDay() == 0){
return true;
}
return System.currentTimeMillis() < createTime.getTime() + getDay() * 24 * 60 * 60 * 1000;
}
}
/*
* “一天”的实现类
*/
public class OneDayStrategy extends CountTimeStrategy{
static {
OneDayStrategy oneDayStrategy = new OneDayStrategy();
strategyMap.put(1, oneDayStrategy);
}
@Override
int getDay(){
return 1;
}
}
/*
* “一周”的实现类
*/
public class WeekStrategy extends CountTimeStrategy{
static {
WeekStrategy weekStrategy = new WeekStrategy ();
strategyMap.put(2, weekStrategy );
}
@Override
int getDay(){
return 7;
}
}
/*
* “永远”的实现类
*/
public class ForeverStrategy extends CountTimeStrategy{
static {
ForeverStrategy foreverStrategy = new ForeverStrategy ();
strategyMap.put(3, foreverStrategy );
}
@Override
int getDay(){
return 0;
}
}
Controller
@Controller
public Share getShare(String userId) {
Share share = shareService.getByUserId(userId);
if(isValidShare(share)) {
return share;
}
return null;
}
// 判断是否分享是否过期
private boolean isValidShare(Share share){
Integer timeLimit = share.getTimeLimit();
return CountTimeStrategy.strategys.get(timeLimit).isValid(share);
}
使用map后就可以通过key获取对应的实现类了,这时候当我们需要添加一个月的期限时就不需要修改任何代码,只需要新建一个CountTimeStrategy的子类即可,如下
扩展分享期限“一个月”
/*
* “一月”的实现类
*/
public class MonthStrategy extends CountTimeStrategy{
static {
// 将实现类存入父类的map
MonthStrategy monthStrategy = new MonthStrategy();
strategyMap.put(4, monthStrategy);
}
@Override
int getDay(){
return 30;
}
}
看到这里会发现对于不同的timeLimit会有不同的day来对应,使用策略模式有点小题大作了,完全可以使用map<Integer, Integer>{<1, null>, <2, 7>, <3, 1>}来解决,没错,策略模式是适用于方法实现不同的场景,而这里的方法实现相同,唯一的不同就是参数day。
那么只有参数变化的情况使用什么方式比较合适呢?
使用枚举enum
使用枚举和使用策略模式非常相似
public enum CountTimeEnum {
ONEDAY(1), // 相当于OneDayStrategy
WEEK(7), // 相当于WeekStrategy
FOREVER(null); // 相当于ForeverStrategy
public static Map<Integer, TimeLimitEnum> countTimeEnumMap = new HashMap<>();
static{
countTimeEnumMap .put(1,FOREVER);
countTimeEnumMap .put(2,WEEK);
countTimeEnumMap .put(3,ONEDAY);
}
private Integer day;
TimeLimitEnum(Integer day) {
this.day= day;
}
public boolean isValid(Share share) {
Date createTime = share.getCreateTime();
if(day == null){
return true;
}
if(System.currentTimeMillis() < createTime.getTime() + day* 24 * 60 * 60 * 1000){
return true;
}else{
return false;
}
}
}
@Controller
public Share getShare(String userId) {
Share share = shareService.getByUserId(userId);
if(isValidShare(share)) {
return share;
}
return null;
}
// 判断是否分享是否过期
private boolean isValidShare(Share share){
Integer timeLimit = share.getTimeLimit();
return TimeLimitEnum.timeLimitEnumMap .get(timeLimit).isValid(share);
}
枚举和策略模式的区别:
枚举类中的各个实例的方法实现是一样的,通过不同的参数得到不同的结果。
策略模式的各个策略方法除了可以输入不同的参数,实现也可以不同,可以将相同的实现放在抽象父类,不同的实现由子类实现,更加灵活。
题外话:
因为方法的参数为share,应用面向对象的思想应该把isValidShare(Share share)方法放入share类中。
public class Share {
private String id;
private Integer timeLimit;
private Date createTime;
// 判断是否分享是否过期
private boolean isValidShare(){
return TimeLimitEnum.timeLimitEnumMap .get(timeLimit).isValid(createTime);
}
}
public enum CountTimeEnum {
ONEDAY(1), // 相当于OneDayStrategy
WEEK(7), // 相当于WeekStrategy
FOREVER(null); // 相当于ForeverStrategy
public static Map<Integer, TimeLimitEnum> countTimeEnumMap = new HashMap<>();
static{
countTimeEnumMap .put(1,FOREVER);
countTimeEnumMap .put(2,WEEK);
countTimeEnumMap .put(3,ONEDAY);
}
private Integer day;
TimeLimitEnum(Integer day) {
this.day= day;
}
public boolean isValid(Date createTime) {
if(day == null){
return true;
}
if(System.currentTimeMillis() < createTime.getTime() + day* 24 * 60 * 60 * 1000){
return true;
}else{
return false;
}
}
}
@Controller
public Share getShare(String userId) {
Share share = shareService.getByUserId(userId);
// 判断是否分享是否过期
if(share.isValidShare()) {
return share;
}
return null;
}