当前位置: 首页 > article >正文

设计模式之策略模式-工作实战总结与实现

文章目录

  • 应用场景
  • 存在问题
  • 解决方案
  • 继续延伸

应用场景

假设有这样的业务场景,大数据系统把文件推送过来,根据不同类型采取不同的解析方式。多数的小伙伴就会写出以下的代码:

public class Question {
    public static void main(String[] args) {
        String type = "";
        if (type == "A") {
            //按照A格式解析
            
        } else if (type == "B") {
            //按B格式解析
            
        } else {
            //按照默认格式解析
        }
    }
}

存在问题

  • 1、如果分支变多,这里的代码就会变得臃肿,难以维护,可读性低。
  • 2、如果你需要接入一种新的解析类型,那只能在原有代码上修改。

说得专业一点的话,就是以上代码,违背了面向对象编程的开闭原则以及单一原则。

  • 开闭原则(对于扩展是开放的,但是对于修改是封闭的):增加或者删除某个逻辑,都需要修改到原来代码
  • 单一原则(规定一个类应该只有一个发生变化的原因):修改任何类型的分支逻辑代码,都需要改动当前类的代码。

解决方案

如果你的代码就是酱紫:有多个if…else等条件分支,并且每个条件分支,可以封装起来替换的,我们就可以使用策略模式来优化。

/**
 * 策略模式:
 * 一个接口或者抽象类,里面两个方法(一个方法匹配类型,一个可替换的逻辑实现方法)
 * 不同策略的差异化实现(就是说,不同策略的实现类)
 */
public interface IFileStrategy {
    /**
     * 解析属于哪种文件类型,也可以继续优化
     *
     * @return 文件类型
     */
    String getFileType();
    
    /**
     * 封装的公用算法(具体的解析方法)
     *
     * @param param
     */
    void process(Object param);
}

@Component
public class AFileResolve implements IFileStrategy {
    @Override
    public String getFileType() {
        return DbDriverClassNameTypeEnum.MYSQL.getType();
    }
    
    @Override
    public void process(Object param) {
        System.out.println("A类型文件解析具体逻辑");
    }
}

@Component
public class BFileResolve implements IFileStrategy {
    @Override
    public String getFileType() {
        return DbDriverClassNameTypeEnum.POSTGRES.getType();
    }
    
    @Override
    public void process(Object param) {
        System.out.println("B类型文件解析具体逻辑");
    }
}

@Component
public class DefaultResolve implements IFileStrategy {
    @Override
    public String getFileType() {
        return DbDriverClassNameTypeEnum.ORACLE.getType();
    }
    
    @Override
    public void process(Object param) {
        System.out.println("默认类型文件解析具体逻辑");
    }
}
/**
 * 如何使用呢?
 * 我们借助spring的生命周期,使用ApplicationContextAware接口,把对用的策略,初始化到map里面。然后对外提供resolveFile方法即可。
 * ApplicationContextAware接口能够轻松感知并在Spring中获取应用上下文,进而访问容器中的其他Bean和资源,
 * 这增强了组件间的解耦,了代码的灵活性和可扩展性,是Spring框架中实现高级功能的关键接口之一。
 */
@Component
public class StrategyService implements ApplicationContextAware {
    private Map<String, IFileStrategy> iFileStrategyMap = new ConcurrentHashMap<>();
    
    /**
     * 初始化策略.
     *
     * @param applicationContext Spring应用上下文
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, IFileStrategy> tmepMap = applicationContext.getBeansOfType(IFileStrategy.class);
        tmepMap.values().forEach(strategyService -> iFileStrategyMap.put(strategyService.getFileType(), strategyService));
    }
    
    /**
     * 统一对外提供的接口,根据类型处理文件
     *
     * @param fileType
     * @param objectParam
     */
    public void resolveFile(String fileType, Object objectParam) {
        IFileStrategy iFileStrategy = iFileStrategyMap.get(fileType);
        if (iFileStrategy != null) {
            iFileStrategy.process(objectParam);
        }
    }
}

继续延伸

在参数校验的场景中,经常会遇到根据某一个枚举字段的不同值来校验不同的入参字段,校验逻辑也是不同的,因此很容易通过IF ELSE来进行判断处理,但是这样的代码扩展性不强,CRUD痕迹很明显,不优雅!因此可以选择测罗模式进行优化。

public interface LinkageTriggerValidator {
    void check(LinkageRuleTriggerVo triggerVo);
}

public class BizPeriodTypeValidator implements LinkageTriggerValidator {
    
    @Override
    public void check(LinkageRuleTriggerVo triggerVo) {
        BizPeriodType eventType = triggerVo.getBizEventType();
        if (null == eventType) {
            throw new ParamCheckRuntimeException("业务周期事件配置缺少业务周期类型");
        }
        // todo
    }
}


public class ReservationEventValidator implements LinkageTriggerValidator {
    
    @Override
    public void check(LinkageRuleTriggerVo triggerVo) {
        ReservationEventType eventType = triggerVo.getReservationEventType();
        if (null == eventType) {
            throw new ParamCheckRuntimeException("预定事件配置缺少预定时间类型");
        }
        // todo
    }
}


public abstract class LinkageTriggerValidatorContext {
    
    /**
     * 触发源校验器.
     */
    private static Map<LinkageTriggerSource, LinkageTriggerValidator> triggerValidatorMap =
            new EnumMap<>(LinkageTriggerSource.class);
    
    static {
        triggerValidatorMap.put(LinkageTriggerSource.RESERVE_TIME, new ReservationEventValidator());
        triggerValidatorMap.put(LinkageTriggerSource.MANUAL_TRIGGER, new TriggerEventValidator());
        triggerValidatorMap.put(LinkageTriggerSource.BIZ_PERIOD, new BizPeriodTypeValidator());
    }
    
  
    public static void check(LinkageTriggerSource type, LinkageRuleTriggerVo triggerVo) {
        LinkageTriggerValidator validator = triggerValidatorMap.get(type);
        if (null == validator) {
            throw new UnsupportedOperationException("不支持该触发源类型:" + type);
        }
        validator.check(triggerVo);
    }
}
使用LinkageTriggerValidatorContext提供的check方法,统一进行参数校验即可
LinkageTriggerValidatorContext.check(trigger.getType(), trigger);

http://www.kler.cn/a/410754.html

相关文章:

  • 基于FPGA的2FSK调制-串口收发-带tb仿真文件-实际上板验证成功
  • 【1.3 Getting Started--->Release Notes】
  • 小米C++ 面试题及参考答案下(120道面试题覆盖各种类型八股文)
  • Python酷库之旅-第三方库Pandas(237)
  • qt+opengl 三维物体加入摄像机
  • Windows 软件之 FFmpeg
  • CPU性能优化--skylake 微架构事件编码示例
  • Android蓝牙架构,源文件目录/编译方式学习
  • idea怎么打开两个窗口,运行两个项目
  • 【LeetCode: 743. 网络延迟时间 + Dijkstra】
  • Vercel 设置自动部署 GitHub 项目
  • .net 支持跨平台(桌面)系列技术汇总
  • transformer.js(三):底层架构及性能优化指南
  • 算法 差分修改 极简
  • 常用Rust日志处理工具教程
  • JavaWeb——SpringBoot原理
  • [工具分享] 根据Excel数据根据Word文档模板,批量创建生成Word文档并重命名,方便快速查找打印
  • Springboot下导入导出excel
  • GitLab指定用户分配合并权限
  • Qt入门1——认识Qt的几个常用头文件和常用函数
  • 对比三种UI交互界面的方案
  • 移动端相关 BFC CSS原子化 ✅
  • WEB攻防-通用漏洞文件上传js验证mimeuser.ini语言特性
  • vue2中的this.$el,this.$parent,this.$children 在vue3中如何表示
  • 《基于FPGA的便携式PWM方波信号发生器》论文分析(三)——数码管稳定显示与系统调试
  • Redis 实现高性能计数器