策略模式解决多重if-else

策略模式解决多重if-else

使用策略模式解决多重if-else

参考学习资料:

  • https://www.cnblogs.com/adamjwh/p/11011095.html
  • https://mp.weixin.qq.com/s/P0G8YHY3kQHJ90NyrOrmOA

最近现在项目开发中遇到公众号发送模板消息。项目经理申请了很多种模板发送消息给关注着。如果不使用设计模式需要使用switch 或者if-else 造成 代码臃肿。看到一篇使用策略模式+工厂+字典map 解决多重if-else 。分享给大家。相互学习。有什么不对的请指正。谢谢.

1 策略模式的理解

**策略模式(Strategy)?*定义了一组算法,将每个算法都封装起来 [可以理解是类的行为(方法)]

 
* - 多个类只有算法或行为上稍有不同的场景
 * - 算法需要自由切换的场景
 * - 需要屏蔽算法规则的场景
* 使用场景:
 * 1.出门的时候会选择不同的出行方式,比如骑自行车、坐公交、坐火车、坐飞机、坐火箭等等,这些出行方式,每一种都是一个策略
 * 2.商场促销方式,打折、满减等
 * 3.Java AWT中的LayoutManager,即布局管理器
*
* 注意:如果一个系统的策略多于四个,就需要考虑使用混合模式来解决策略类膨胀的问题
 

2.演示代码

场景:

物流行业中,通常会涉及到EDI报文(XML格式文件)传输和回执接收,每发送一份EDI报文,

后续都会收到与之关联的回执(标识该数据在第三方系统中的流转状态)。

这里枚举几种回执类型:MT1101、MT2101、MT4101、MT8104、MT8105、MT9999,

系统在收到不同的回执报文后,会执行对应的业务逻辑处理。

当然,实际业务场景并没有那么笼统,这里以回执处理为演示案例

2.1.准备实体类对象

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/** * 收据 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Receipt {
   

    /** * 收据信息 */
    private String message;

    /** * 收据类型 */
    private String type;

}


import java.util.ArrayList;
import java.util.List;


/** * 构造Receipt收据实体类对象. 在实际项目中,这些数据前端传递参数封装对应的对象的。 */

public class ReceiptBuilder {
   

    public static List<Receipt> generateReceiptList(){
   
        //直接模拟一堆回执对象
        List<Receipt> receiptList = new ArrayList<>();
        receiptList.add(new Receipt("我是MT2101回执喔","MT2101"));
        receiptList.add(new Receipt("我是MT1101回执喔","MT1101"));
        receiptList.add(new Receipt("我是MT8104回执喔","MT8104"));
        receiptList.add(new Receipt("我是MT9999回执喔","MT9999"));
        //......
        return receiptList;
    }
}

2.2 创建策略模式和对应的算法

可以通过抽象类或者接口的形式,如果是抽象类。行为适用抽象方法。通过继承让子类去实现抽象方法。如果接口,那就和平时开发一样了呀。

/** * 回执处理策略接口 */
public interface IReceiptHandleStrategy {
   

    void handleReceipt(Receipt receipt);

}
/* * * 策略模式(Strategy),定义了一组算法,将每个算法都封装起来 [可以理解是对类的行为(方法)] * * - 多个类只有算法或行为上稍有不同的场景 * - 算法需要自由切换的场景 * - 需要屏蔽算法规则的场景 * 使用场景: * 1.出门的时候会选择不同的出行方式,比如骑自行车、坐公交、坐火车、坐飞机、坐火箭等等,这些出行方式,每一种都是一个策略 * 2.商场促销方式,打折、满减等 * 3.Java AWT中的LayoutManager,即布局管理器 * *注意:如果一个系统的策略多于四个,就需要考虑使用混合模式来解决策略类膨胀的问题 * */

 

public class Mt1101ReceiptHandleStrategy implements IReceiptHandleStrategy {
   

    @Override
    public void handleReceipt(Receipt receipt) {
   
        System.out.println("解析报文MT1101:" + receipt.getMessage());
    }

}

public class Mt2101ReceiptHandleStrategy implements IReceiptHandleStrategy {
   

    @Override
    public void handleReceipt(Receipt receipt) {
   
        System.out.println("解析报文MT2101:" + receipt.getMessage());
    }

}

public class Mt8104ReceiptHandleStrategy implements IReceiptHandleStrategy {
   

    @Override
    public void handleReceipt(Receipt receipt) {
   
        System.out.println("解析报文MT8104:" + receipt.getMessage());
    }

}

import com.example.strategydemo.pojo.Receipt;
import com.example.strategydemo.strategy.IReceiptHandleStrategy;

public class Mt9999ReceiptHandleStrategy implements IReceiptHandleStrategy {
   

    @Override
    public void handleReceipt(Receipt receipt) {
   
        System.out.println("解析报文MT9999:" + receipt.getMessage());
    }

}

2.3 封装上下文对象


/** * @Description: 上下文类,持有策略接口 */
public class ReceiptStrategyContext {
   
    // set 方式 ---- 类的关系是聚合的关系.如果直接new 对象的话是组合关系
    private IReceiptHandleStrategy receiptHandleStrategy;

    /** * 设置策略接口 * @param receiptHandleStrategy */
    public void setReceiptHandleStrategy(IReceiptHandleStrategy receiptHandleStrategy) {
   
        this.receiptHandleStrategy = receiptHandleStrategy;
    }

    //上下文对象封装了策略 行为,后面通过工厂创建对应策略对象。然后调用这个方法去做处理,也就是运用到多态的知识。真正做事情的是其子类
    public void handleReceipt(Receipt receipt){
   
        if (receiptHandleStrategy != null) {
   
            receiptHandleStrategy.handleReceipt(receipt);
        }
    }
}

2.4 创建策略工厂生产对应的子类对象

/** * @Description: 策略工厂 * @Auther: wuzhazha */
public class ReceiptHandleStrategyFactory {
   

    private static Map<String, IReceiptHandleStrategy> receiptHandleStrategyMap;

    private ReceiptHandleStrategyFactory(){
   
        this.receiptHandleStrategyMap = new HashMap<>();
        this.receiptHandleStrategyMap.put("MT2101",new Mt2101ReceiptHandleStrategy());
        this.receiptHandleStrategyMap.put("MT8104",new Mt8104ReceiptHandleStrategy());
    }

    public static IReceiptHandleStrategy getReceiptHandleStrategy(String receiptType){
   
        return receiptHandleStrategyMap.get(receiptType);
    }
}

2.5 如何使用?

public class Client {
   

    public static void main(String[] args) {
   
        //模拟回执
        List<Receipt> receiptList = ReceiptBuilder.generateReceiptList();


        //策略上下文
        ReceiptStrategyContext receiptStrategyContext = new ReceiptStrategyContext();
        for (Receipt receipt : receiptList) {
   
            //通过收据类型 在工厂中获取对应的 策略对象 对象 [运用了多态]----工厂
            IReceiptHandleStrategy receiptHandleStrategy = ReceiptHandleStrategyFactory.getReceiptHandleStrategy(receipt.getType());
            //拿到策略对象 设置给策略上下文
            receiptStrategyContext.setReceiptHandleStrategy(receiptHandleStrategy);
            //上下文对象封装了 执行策略的公共方法 [ 向上抽取一种思想]
            //Context上下文角色,也叫Context封装角色,起承上启下的作用,
            // 屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。
            //【简单来说运用了封装 、多态思想】【看来还要继续回炉,重新认识面向对象】
            receiptStrategyContext.handleReceipt(receipt);
        }
    }
}
//后期扩展对工厂进行新增即可,不会影响其他的代码。 
//就像作者说的一样使用反射创建对象,那就真正的满足设计原则 :开闭原则了.

2.6 反射工具类

public class ReflectionUtil {

/** * 定义类集合(用于存放所有加载的类) */
private static final Set<Class<?>> CLASS_SET;
static {

//指定加载包路径
CLASS_SET = getClassSet("com.yaolong");
}
/** * 获取类加载器 * @return */
public static ClassLoader getClassLoader(){

return Thread.currentThread().getContextClassLoader();
}
/** * 加载类 * @param className 类全限定名称 * @param isInitialized 是否在加载完成后执行静态代码块 * @return */
public static Class<?> loadClass(String className,boolean isInitialized) {

Class<?> cls;
try {

cls = Class.forName(className,isInitialized,getClassLoader());
} catch (ClassNotFoundException e) {

throw new RuntimeException(e);
}
return cls;
}
public static Class<?> loadClass(String className) {

return loadClass(className,true);
}
/** * 获取指定包下所有类 * @param packageName * @return */
public static Set<Class<?>> getClassSet(String packageName) {

Set<Class<?>> classSet = new HashSet<>();
try {

Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".","/"));
while (urls.hasMoreElements()) {

URL url = urls.nextElement();
if (url != null) {

String protocol = url.getProtocol();
if (protocol.equals("file")) {

String packagePath = url.getPath().replace("%20","");
addClass(classSet,packagePath,packageName);
} else if (protocol.equals("jar")) {

JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
if (jarURLConnection != null) {

JarFile jarFile = jarURLConnection.getJarFile();
if (jarFile != null) {

Enumeration<JarEntry> jarEntries = jarFile.entries();
while (jarEntries.hasMoreElements()) {

JarEntry jarEntry = jarEntries.nextElement();
String jarEntryName = jarEntry.getName();
if (jarEntryName.endsWith(".class")) {

String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", ".");
doAddClass(classSet,className);
}
}
}
}
}
}
}
} catch (IOException e) {

throw new RuntimeException(e);
}
return classSet;
}
private static void doAddClass(Set<Class<?>> classSet, String className) {

Class<?> cls = loadClass(className,false);
classSet.add(cls);
}
private static void addClass(Set<Class<?>> classSet, String packagePath, String packageName) {

final File[] files = new File(packagePath).listFiles(new FileFilter() {

@Override
public boolean accept(File file) {

return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();
}
});
for (File file : files) {

String fileName = file.getName();
if (file.isFile()) {

String className = fileName.substring(0, fileName.lastIndexOf("."));
if (StringUtils.isNotEmpty(packageName)) {

className = packageName + "." + className;
}
doAddClass(classSet,className);
} else {

String subPackagePath = fileName;
if (StringUtils.isNotEmpty(packagePath)) {

subPackagePath = packagePath + "/" + subPackagePath;
}
String subPackageName = fileName;
if (StringUtils.isNotEmpty(packageName)) {

subPackageName = packageName + "." + subPackageName;
}
addClass(classSet,subPackagePath,subPackageName);
}
}
}
public static Set<Class<?>> getClassSet() {

return CLASS_SET;
}
/** * 获取应用包名下某父类(或接口)的所有子类(或实现类) * @param superClass * @return */
public static Set<Class<?>> getClassSetBySuper(Class<?> superClass) {

Set<Class<?>> classSet = new HashSet<>();
for (Class<?> cls : CLASS_SET) {

if (superClass.isAssignableFrom(cls) && !superClass.equals(cls)) {

classSet.add(cls);
}
}
return classSet;
}
/** * 获取应用包名下带有某注解的类 * @param annotationClass * @return */
public static Set<Class<?>> getClassSetByAnnotation(Class<? extends Annotation> annotationClass) {

Set<Class<?>> classSet = new HashSet<>();
for (Class<?> cls : CLASS_SET) {

if (cls.isAnnotationPresent(annotationClass)) {

classSet.add(cls);
}
}
return classSet;
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/100731.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号