策略模式代替if-else的两种方式
前言:
之前写业务逻辑的时候,发现大量的if-else处理业务逻辑,如果业务分支较少倒是还好,但是一旦后续有业务拓展的需要,一直在下面加if-else或switch-case模式,会发现,这个业务方法代码量会非常多,往往随着业务拓展,这个方法代码量都是几百行往上的飙升,最后经手的人多了,就成了大家都不愿看的屎山代码。
办法总比困难多,偶然机会发现采用策略模式来设计代码,在彻底理解里面的精髓后,才感叹设计模式的伟大之处,他的根本目的之一就是解耦合,代码的高度耦合是软件设计最忌讳的地方,因为这样会导致后续的维护性极差,策略模式+反射/Spring容器+工厂模式的组合方式,在这方面极大的优化了上面导致的问题,也很好的符合软件设计的开闭原则(对拓展开放,对修改关闭)。
方法一:策略模式+工厂模式+反射
下面2个demo案例,应该比较好理解
首先创建一个接口,抽象出公共方法放其中
下面用一个支付的案例来测试反射+策略模式的组合
公共方法的接口
package com.rao.stragry;
public interface PayStrategy {
void pay(double total);
}
支付宝支付策略
package com.rao.stragry;
// 支付宝付款
public class Alipay implements PayStrategy {
@Override
public void pay(double total) {
System.out.println("pay with alipay: " + total);
}
}
银行卡支付策略
package com.rao.stragry;
// 银行卡付款
public class EbankPay implements PayStrategy {
@Override
public void pay(double total) {
System.out.println("pay with ebankpay: " + total);
}
}
微信支付策略
package com.rao.stragry;
//微信付款
public class WechatPay implements PayStrategy {
@Override
public void pay(double total) {
System.out.println("pay with wechatpay: " + total);
}
}
一个策略工厂
package com.rao.stragry;
public class StrategyFactory {
public static PayStrategy getStrategy(String strategyType) throws Exception {
String className = PayEnum.valueOf(strategyType).getClassName();
return (PayStrategy) Class.forName(className).newInstance();
}
}
枚举类
package com.rao.stragry;
public enum PayEnum {
ALIPAY("com.rao.stragry.Alipay"),
WECHATPAY("com.rao.stragry.WechatPay"),
EBANKPAY("com.rao.stragry.EbankPay");
PayEnum(String className) {
this.setClassName(className);
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
private String className;
}
测试类
package com.rao.stragry;
public class Client {
public static void main(String[] args) throws Exception {
String alipay = "ALIPAY";//
PayStrategy alipay_strategy = StrategyFactory.getStrategy(alipay);
alipay_strategy.pay(12);
String wechatpay = "WECHATPAY";//
PayStrategy wechatpay_strategy = StrategyFactory.getStrategy(wechatpay);
wechatpay_strategy.pay(12);
String ebankpay = "EBANKPAY";//
PayStrategy ebankpay_strategy = StrategyFactory.getStrategy(ebankpay);
ebankpay_strategy.pay(12);
}
}
第二种方式
通过Spring提供的容器来管理,把我们的实现策略类作为一个bean,用@Component注入spring容器,通过spring容易统一来管理我们bean的生命周期,其实大体上跟上面方法一反射实现差不多,因为spring容器也是通过反射的方式去动态获取bean对象的,返回给我们一个代理对象,只不过通过spring提供的容器,我们就不需要用原始的反射方式了。这样方式也很简单,代码设计思想最重要~
下面用一个小例子演示
提取的公共方法接口
package com.rao.stragry2;
public interface PromotionStrategy {
/**
* 促销接口
* @param promotionType
*/
void doPromotion(String promotionType);
}
策略实现类一
package com.rao.stragry2;
import org.springframework.stereotype.Component;
@Component
public class DefaultStrategy implements PromotionStrategy{
@Override
public void doPromotion(String promotionType) {
System.out.println("result is:"+promotionType+ " 不做促销~");
}
}
策略实现类二
package com.rao.stragry2;
import org.springframework.stereotype.Component;
@Component
public class DisCountStrategy implements PromotionStrategy{
@Override
public void doPromotion(String promotionType) {
System.out.println("result is:"+promotionType+ " 折扣策略~");
}
}
策略实现类三
package com.rao.stragry2;
import org.springframework.stereotype.Component;
@Component
public class FeeStrategy implements PromotionStrategy{
@Override
public void doPromotion(String promotionType) {
System.out.println("result is:"+promotionType+ " 0元购~");
}
}
业务方法
package com.rao.stragry2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PromotionService {
@Autowired
private PromotionStrategyService promotionStrategyService;
public void doPromotion(String promotionType){
//获取策略实例并执行
promotionStrategyService.getStrategyInstance(promotionType).doPromotion(promotionType);
}
}
动态获取bean
package com.rao.stragry2;
import org.springframework.context.support.ApplicationObjectSupport;
import org.springframework.stereotype.Service;
import java.util.Objects;
@Service
public class PromotionStrategyService extends ApplicationObjectSupport {
private final PromotionStrategy DEFAULT_PROMOTION=new DefaultStrategy();
public PromotionStrategy getStrategyInstance(String promotionType){
EnumPromotionType enumPromotionType=EnumPromotionType.find(promotionType);
//枚举为空,则为默认的促销策略
if(Objects.isNull(enumPromotionType)){
return DEFAULT_PROMOTION;
}
//实际的促销策略
return super.getApplicationContext().getBean(enumPromotionType.getDescription(),PromotionStrategy.class);
}
}
枚举类
package com.rao.stragry2;
import lombok.Data;
import lombok.Getter;
import java.util.Arrays;
@Getter
public enum EnumPromotionType {
/**
* 满减
*/
FULL_REDUCTION("1","defaultStrategy"),
FREE("2","feeStrategy"),
DISCOUNT("3","disCountStrategy");
private String code;
private String description;
EnumPromotionType(String code,String description){
this.code=code;
this.description=description;
}
public static EnumPromotionType find(String code){
EnumPromotionType enumPromotionType = Arrays.stream(EnumPromotionType.values())
.filter(input -> input.getCode().equals(code))
.findFirst()
.orElse(null);
return enumPromotionType;
}
}
测试类
package com.rao;
import com.rao.stragry2.PromotionService;
import com.rao.stragry2.PromotionStrategyService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class JavaPracticeApplicationTests {
@Autowired
private PromotionService promotionService;
@Test
void contextLoads() {
promotionService.doPromotion("1");
promotionService.doPromotion("2");
promotionService.doPromotion("3");
}
}
总结:越来越感觉,越学到后面,发现代码的设计思想越来越重要,如果只是每天的重复敲业务代码,还不如每天抽出点时间提升自己,这样即是对自己的提升,然后这种提升效益于工作中,提升公司代码的维护性和可读性,对公司来说也是一种财富,毕竟代码首先是人读,再是机器读。