如何写出优雅易维护的代码?——浅谈策略模式

前言

最近在工作中偶遇一个看似平平无奇的需求,简单概括就是改造项目里之前做的PDF导出模板和数据,这里需要根据不同状态导出不同的模板数据。接到需求那一刻,脑海里瞬间就有了解决方案,只需要简单修改一下模板,然后修改查询回填数据就可以了嘛   so easy ~  。当我找到这一块的代码的时候整个人都懵了,因为需要判断两个状态,之前的代码是这样实现的:switch…case… 接着里边再用上祖传的 if…else… 判断,注释寥寥几行展示了前人强大的脑回路和过硬的代码功底。尽管有提取方法,代码也没有显得很臃肿,不过俄罗斯套娃式写法也是换汤不换药,代码的可读性可维护性真是极差。人家是前人栽树,后人乘凉,到我这变成前人挖坑,后人填坑。思来想去记得之前有看过一篇技术文章介绍了如何避免大量if判断,接着就抓紧去研究了一番,想到我的博客已经杂草丛生了,就在这里作个记录吧。

一、什么是策略模式

专业回答: 策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。(该模式定义了一系列算法,并将每个算法封装隔离起来,使它们可以相互替换,且算法的变化不会影响算法的使用

业余回答:假设策略模式的实现就好比一场线上模拟篮球比赛,需要进攻、需要防守,身为全局掌控者的你要靠制定各种各样的战术来赢得比赛,这些战术就是你的策略,在球场攻防转换的过程中,进攻和防守这些策略之间是相互隔离开的,进攻的时候不需要防守,防守的时候不需要进攻。所以在代码层面的实现就显而易见了,通过定义一个执行入口(接口),不同的策略类分别实现这个接口,要用哪个就调那个就可以

二、策略模式的优缺点

优点:

  • 避免使用多重条件判断,提高系统可维护性;
  • 算法可以自由切换;
  • 扩展性好,可以在不修改原算法的基础上,灵活增加新的算法;
  • 恰当使用继承可以把公共的代码转移到父类里面,提高代码复用性;

缺点:

  • 如果需要增加策略,只能新增策略类;
  • 使用者必须理解所有策略算法的区别,以便适时选择恰当的算法类;

三、代码示例

1、简单实现便于理解:
  • 定义策略入口
/**
 * @author: Marlon
 * @date: 2022/8/28
 **/
public interface Strategy {
	
    String strategyHandler();

}
  • 定义具体策略实现
/**
 * @author: Marlon
 * @date: 2022/8/28
 **/
public class OffenseStrategy implements Strategy{

    @Override
    public String strategyHandler() {
        return "This is offense ...";
    }

}

/**
 * @author: Marlon
 * @date: 2022/8/28
 **/
public class DefendStrategy implements Strategy {

    @Override
    public String strategyHandler() {
        return "This is defend ...";
    }

}
  • 使用 Context 来查看当它改变策略 Strategy 时的行为变化。
/**
 * @author: Marlon
 * @date: 2022/8/28
 **/
public class Context {

    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public String executeStrategy() {
        return strategy.strategyHandler();
    }
}
  • 测试
/**
 * @author: Marlon
 * @date: 2022/8/28
 **/
public class Test {

    public static void main(String[] args) {
        DefendStrategy defendStrategy = new DefendStrategy();
        OffenseStrategy offenseStrategy = new OffenseStrategy();
        //执行策略
        Context context = new Context(defendStrategy);
        System.out.println(context.executeStrategy());
        //执行策略
        context = new Context(offenseStrategy);
        System.out.println(context.executeStrategy());
    }
		
	/**
	* 输出结果:
	* 
	* 	This is defend ...
	* 	This is offense ...
	*
	**/
   
}
2、使用工厂模式+策略模式
  • 定义策略入口
/**
 * @author: Marlon
 * @date: 2022/8/28
 **/
public interface Strategy {

    String strategyHandler();

}
  • 定义具体策略实现
/**
 * @author: Marlon
 * @date: 2022/8/28
 **/
public class OffenseStrategy implements Strategy{

    @Override
    public String strategyHandler() {
        return "This is offense ...";
    }

}

/**
 * @author: Marlon
 * @date: 2022/8/28
 **/
public class DefendStrategy implements Strategy {

    @Override
    public String strategyHandler() {
        return "This is defend ...";
    }

}
  • 枚举定义映射地址
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;

/**
 * @author: Marlon
 * @date: 2022/8/28
 **/
@Getter
@AllArgsConstructor
@NoArgsConstructor
public enum StrategyEnum {

    /**
     * offense
     */
    OFFENSE("offense", "com.marlon.OffenseStrategy"),

    /**
     * defend
     */
    DEFEND("defend", "com.marlon.DefendStrategy");

    private String code;

    private String className;

    public static String getClassNameByCode(final String code) {
        String className = "";
        if (StringUtils.isEmpty(code)) {
            return className;
        }
        for (StrategyEnum se : StrategyEnum.values()) {
            if (se.code.equalsIgnoreCase(code)) {
                className = se.getClassName();
                break;
            }
        }
        return className;
    }
}
  • 工厂类反射执行
/**
 * @author: Marlon
 * @date: 2022/8/28
 **/
public class StrategyFactory {

    /**
     * 使用策略工厂获取具体策略实现
     *
     * @param code 策略编码
     * @return 策略接口
     */
    public static Strategy getStrategy(final String code) {
        try {
            return (Strategy) Class.forName(StrategyEnum.getClassNameByCode(code)).newInstance();
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

}
  • 上下文获取具体策略
import java.util.Objects;

/**
 * @author: Marlon
 * @date: 2022/8/28
 **/
public class ContextStrategy {

    public static String strategyHandler(final String code){
        if(StringUtils.isEmpty(code)){
            return "code is not null...";
        }
        Strategy strategy = StrategyFactory.getStrategy(code);
        if(Objects.isNull(strategy))
        {
            return "strategy is null...";
        }
        return strategy.strategyHandler();
    }

}
  • 测试
 public static void main(String[] args) {
        //执行DEFEND策略
        System.out.println(ContextStrategy.strategyHandler(StrategyEnum.DEFEND.getCode()));
        //执行OFFENSE策略
        System.out.println(ContextStrategy.strategyHandler(StrategyEnum.OFFENSE.getCode()));

    }

	/**
	* 输出结果:
	* 
	* 	This is defend ...
	* 	This is offense ...
	*
	**/
2、使用策略模式+模板模式
  • 定义策略入口
/**
 * @author: Marlon
 * @date: 2022/8/28
 **/
public interface Strategy {

    String getStrategy();

    String strategyHandler();

    String abstractFetchHandler();

}
  • 定义模板抽象类
/**
 * @author: Marlon
 * @date: 2022/8/28
 **/
public abstract class AbstractFetchStrategy implements Strategy {

    public abstract String strategyHandler();

    @Override
    public final String abstractFetchHandler() {
        String publicMethod = "This is public method";
        String strategy = this.strategyHandler();
        return publicMethod + strategy;
    }
}
  • 定义具体策略实现
import org.springframework.stereotype.Component;

/**
 * @author: Marlon
 * @date: 2022/8/28
 **/
@Component
public class DefendStrategy extends AbstractFetchStrategy implements Strategy {

    @Override
    public String getStrategy() {
        return StrategyEnum.DEFEND.getCode();
    }

    @Override
    public String strategyHandler() {
        return "This is defend ...";
    }

}

import org.springframework.stereotype.Component;

/**
 * @author: Marlon
 * @date: 2022/8/28
 **/
@Component
public class OffenseStrategy extends AbstractFetchStrategy implements Strategy {

    @Override
    public String getStrategy() {
        return StrategyEnum.OFFENSE.getCode();
    }

    @Override
    public String strategyHandler() {
        return "This is offense ...";
    }

}
  • 枚举定义
/**
 * @author: Marlon
 * @date: 2022/8/28
 **/
@Getter
@AllArgsConstructor
@NoArgsConstructor
public enum StrategyEnum {

    /**
     * offense
     */
    OFFENSE("offense"),

    /**
     * defend
     */
    DEFEND("defend");

    private String code;

}
  • 上下文获取具体策略
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * @author: Marlon
 * @date: 2022/8/28
 *
 *  实现ApplicationContextAware 这个类就可以方便获得ApplicationContext中的所有bean。
 *  换句话说,就是这个类可以直接获取spring配置文件中,所有有引用到的bean对象。
 **/
@Component
public class ContextStrategy implements ApplicationContextAware {

    private static final Map<String, Strategy> context = new ConcurrentHashMap<>();

    /**
     * 通过枚举获取对应的bean
     * @param strategyEnum 策略编码
     * @return bean
     */
    public static Strategy getByStrategy(final StrategyEnum strategyEnum) {
        return context.get(strategyEnum.getCode());
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        //获取Strategy接口所有实现类 并放入ConcurrentHashMap
        Map<String, Strategy> strategyMap = applicationContext.getBeansOfType(Strategy.class);
        strategyMap.forEach((code,strategy)->{
            context.put(strategy.getStrategy(),strategy);
        });
    }
}
  • 调用     注意:此调用需要在spring环境使用

	Strategy defendStrategy = ContextStrategy.getByStrategy(StrategyEnum.DEFEND);
    System.out.println(defendStrategy.abstractFetchHandler());
    Strategy offenseStrategy = ContextStrategy.getByStrategy(StrategyEnum.OFFENSE);
    System.out.println(offenseStrategy.abstractFetchHandler());

	/**
	* 输出结果:
	* 
	* 	This is defend ...
	* 	This is offense ...
	*
	**/

文章仅用作记录分享,若有不当,还望指正。

最近开通个人了微信公众号,以后将会定期分享工作学习过程中遇到的问题,欢迎关注与我一道成长交流~:

微信搜索: MYY668999程序猿爱篮球 即可上车。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿爱篮球

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值