记录一次在工作中使用的设计模式-策略模式

Posted javartisan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记录一次在工作中使用的设计模式-策略模式相关的知识,希望对你有一定的参考价值。

本文稍微涉及一点quartz与业务的东西,请耐心看下去,下面开始。

 

工作中由于新需求的支持,需要在组内其他工作的模块中添加新需求,这里面简化一下需求场景如下:

线上系统中有一个定时调度执行系统,主要实现是基于quartz-schduler完成的,没有太多值得说的东西,都是一些常见的业务逻辑就不描述这个简单的定时调度系统了。需求是什么呢?需求是线上有一个JobGroup里面含有大量Job,JobGroup是Job的一个逻辑划分,此处假设该JobGroup名字为Index-JobGroup,用于定时生成索引。但是Index-JobGroup需要依赖DataSyncJob数据同步Job,只有DataSyncJob执行完毕才会执行Index-JobGroup组内的Job,此处注意:DataSyncJob作业并不会一次就执行完毕,同步数据Job需要轮询数据准备状态,只有数据准备状态OK才可以进行同步数据之后触发Index-JobGroup执行(执行索引也不一定全执行,还需要判断数据量是否符合一定条件),所以这里面的执行依赖不只是一个时间顺序,还有一个外部状态。但是由于Quartz仅仅是一个依赖时间顺序调度的框架,对于依赖时间和其他状态的调度执行支持的并不是很完美。大概需求是这样,接下来说一下分析解决方案:

 

方案一:

由于DataSyncJob同步多个数据子任务,因此可以将DataSyncJob中的每一个同步数据子任务分别划分到期应属于的Index-JobGroup中的每一个Job中,这样Index-JobGroup中的每一个Job都包含两部分:一部分是检查轮询数据状态知道OK然后开始执行同步数据,最后根据同步数据的量判断时候执行创建索引,这样虽然可以解决问题,但是也存在弊端;因此当大量的数据在轮询状态并长时间数据没有准备OK的话将会占用大量的quartz线程,这样会导致其他的Job得到准时的运行,甚至一直无法运行。所以此方案不可行。(作业的触发都是基于quartz触发的)。

方案一虽然简单,但是性能较差,生产环境一般慎用,因此想到第二种方案。

方案二:

此时想到基于JobGroup进行调度,思路如下:首先执行DataSyncJob,DataSyncJob是一个任务,因此只占用一个线程,该线程轮询数据准备状态,直到数据准备OK并同步完毕数据之后由此Job拉起Index-JobGroup内的所有Job(实现思路是:借助Job的持久化层查询出来该组内的每一个Job然后遍历每一次使用SimpleTrigger执行即可完成按组触发)。然后看了一下现有代码提供的接口如下:


 public JobKey triggerGroupJobsNow(String group, Class<? extends PipelineQuartzJobBean> jobBeanClz, Date simpleStartDate) 

发现该接口只支持执行该组内的所有job,并不支持组内的部分job执行(因为我们需要根据每一个数据量创建索引,因此就需要只执行组内那些数据量满足条件的Job)。所以此时就需要支持接口,但是又不能硬编码在方法中添加这样一个逻辑,这样代码维护性扩展性太差,而且这种执行组内部分Job的需求以后肯定比比皆是,因此就要想办法将接口写的灵活一点。所以脑海中第一个浮现的都是策略模式,因为组内那些Job可以运行哪些Job不可以运行都是策略决定的;策略模式,重点是策略,所以如何选择需要执行的Job就是一个策略;以后其他场景有自己的选择策略时候只需要实现一个对应的策略即可,那么应该如何改造呢?首先定义策略抽象接口:

public interface GroupJobsFilter 

    /**
     * 判断是否允许运行此Job;所谓的策略
     *
     * @param bean
     * @return
     */
    boolean allow(JobDetail bean);

接下来选择创建索引Job的策略实现类:(伪代码)

public class IndexGroupJobsFilter implements GroupJobsFilter 

    @Autowired
    private DataCountService dataCountService;
    @Override

    public boolean allow(JobDetail bean) 
        
        return dataCountService.count(bean)>100000;
    

以后其他业务创建需要自己的策略时候实现GroupJobsFilter即可!!!!

 

重构原有查询接口:(伪代码)

    @Override
    public JobKey triggerGroupJobsNow(String group, Class<? extends PipelineQuartzJobBean> jobBeanClz, Date simpleStartDate) 
        return triggerGroupJobsNow(group, null, jobBeanClz, simpleStartDate);

    


 @Override
    public JobKey triggerGroupJobsNow(String group, GroupJobsFilter condition, Class<? extends PipelineQuartzJobBean> jobBeanClz, Date simpleStartDate) 
        GroupMatcher<JobKey> matcher = GroupMatcher.jobGroupEquals(group);
        List<JobKey> jobKeyList = new ArrayList<>();
        jobKeyList.addAll(quartzService.getJobKeys(matcher));
        List<JobDetail> jobDetailList = new ArrayList<>();
        for (JobKey jobKey : jobKeyList) 
            JobDetail tempJob = quartzService.getJobDetail(jobKey);
            if (condition != null) 
                if (condition.allow(tempJob)) 
                    jobDetailList.add(tempJob);

                
                continue;
            
            jobDetailList.add(tempJob);

        
        //代码略
    

 

到此其实如果你对Spark RDD API熟悉的话,你会发现其实RDD.filter(func)等很多API都是策略模式的表现,其中的func就是策略的实现,只不过这是函数式编程而已,更准确来说函数式编程就是策略的应用。

 

 

备注:

            由于公司业务保密原则,以上业务场景是根据真是场景模拟出来的一种场景,不影响理解策略模式。

以上是关于记录一次在工作中使用的设计模式-策略模式的主要内容,如果未能解决你的问题,请参考以下文章

headFirst设计模式——策略模式

headFirst设计模式——策略模式

策略模式和工厂模式搭配使用

策略模式和工厂模式搭配使用

设计模式之策略模式

设计模式学习---策略模式