文章目录
- 一. 问题与解决思路
- 二. 代码实现与分析
- 1. 业务代码逻辑的架子
- 2. 代码重构:使用策略模式来解耦代码逻辑
- 三. 进一步:满足开闭原则:使用注解或配置文件
设计原则和思想其实比设计模式更加普适和重要,掌握了代码的设计原则和思想,我们甚至可以自己创造出来新的设计模式。—《设计模式之美》王争
一. 问题与解决思路
假设有这样一个需求,希望写一个小程序,实现对一个文件进行排序的功能。如果文件涉及到的文件有不同规模的,如下
- 10GB 大小,因为内存有限(比如只有 8GB 大小),我们没办法一次性加载文件中的所有数据到内存中,这个时候,我们就要利用外部排序算法(ing)了。
- 100GB 大小,我们为了利用 CPU 多核的优势,可以在外部排序的基础之上进行优化,加入多线程并发排序的功能,这就有点类似“单机版”的 MapReduce。
- 1TB 大小,即便是单机多线程排序,这也算很慢了。这个时候,我们可以使用真正的 MapReduce 框架,利用多机的处理能力,提高排序的效率。
很明显不同大小的文件需要使用不同的算法逻辑去实现,而不同的算法逻辑就可以使用策略模式将算法逻辑解耦,具体算法实现不是本文的重点,我们来看下策略模式是如何实现这个代码架构的。
二. 代码实现与分析
1. 业务代码逻辑的架子
如下逻辑架子,简单实现了算法的一些流程步骤,具体算法暂时不实现。
publicclassSorter{privatestaticfinallongGB=1000*1000*1000;publicvoidsortFile(StringfilePath){// 省略校验逻辑Filefile=newFile(filePath);longfileSize=file.length();if(fileSize<6*GB){// [0, 6GB)quickSort(filePath);}elseif(fileSize<10*GB){// [6GB, 10GB)externalSort(filePath);}elseif(fileSize<100*GB){// [10GB, 100GB)concurrentExternalSort(filePath);}else{// [100GB, ~)mapreduceSort(filePath);}}privatevoidquickSort(StringfilePath){// 快速排序}privatevoidexternalSort(StringfilePath){// 外部排序}privatevoidconcurrentExternalSort(StringfilePath){// 多线程外部排序}privatevoidmapreduceSort(StringfilePath){// 利用MapReduce多机排序}}publicclassSortingTool{publicstaticvoidmain(String[]args){Sortersorter=newSorter();sorter.sortFile(args[0]);}}2. 代码重构:使用策略模式来解耦代码逻辑
分析下需求和代码结构:
- 策略定义与创建: 每种排序类都是无状态的,我们没必要在每次使用的时候,都重新创建一个新的对象。所以,我们可以使用工厂模式对对象的创建进行封装。
- 策略的使用:这里的 if-else 逻辑分支不多、也不复杂,这样写完全没问题。当然你也可以使用查表法
接下来我们分别实现这几个方面
策略定义:
publicinterfaceISortAlg{voidsort(StringfilePath);}publicclassQuickSortimplementsISortAlg{@Overridepublicvoidsort(StringfilePath){//...}}...ExternalSortimplementsISortAlgConcurrentExternalSortimplementsISortAlgMapReduceSortimplementsISortAlg...策略实现:
publicclassSortAlgFactory{privatestaticfinalMap<String,ISortAlg>algs=newHashMap<>();static{algs.put("QuickSort",newQuickSort());algs.put("ExternalSort",newExternalSort());algs.put("ConcurrentExternalSort",newConcurrentExternalSort());algs.put("MapReduceSort",newMapReduceSort());}publicstaticISortAlggetSortAlg(Stringtype){if(type==null||type.isEmpty()){thrownewIllegalArgumentException("type should not be empty.");}returnalgs.get(type);}}。。。}策略使用:
publicclassSorter{privatestaticfinallongGB=1000*1000*1000;privatestaticfinalList<AlgRange>algs=newArrayList<>();static{algs.add(newAlgRange(0,6*GB,SortAlgFactory.getSortAlg("QuickSort")));algs.add(newAlgRange(6*GB,10*GB,SortAlgFactory.getSortAlg("ExternalSort")));algs.add(newAlgRange(10*GB,100*GB,SortAlgFactory.getSortAlg("ConcurrentExternalSort")));algs.add(newAlgRange(100*GB,Long.MAX_VALUE,SortAlgFactory.getSortAlg("MapReduceSort")));}publicvoidsortFile(StringfilePath){// 省略校验逻辑Filefile=newFile(filePath);longfileSize=file.length();ISortAlgsortAlg=null;for(AlgRangealgRange:algs){//如果处于某一个范围就选定这个,并结束循环if(algRange.inRange(fileSize)){sortAlg=algRange.getAlg();break;}}sortAlg.sort(filePath);}privatestaticclassAlgRange{privatelongstart;privatelongend;//AlgRange封装策略类与策略类型,这样就能去除if elseprivateISortAlgalg;publicAlgRange(longstart,longend,ISortAlgalg){this.start=start;this.end=end;this.alg=alg;}publicISortAlggetAlg(){returnalg;}publicbooleaninRange(longsize){returnsize>=start&&size<end;}}}三. 进一步:满足开闭原则:使用注解或配置文件
即便这样,当我们添加新的排序算法的时候,还是需要修改策略实现与选择的代码,并不完全符合开闭原则。
有什么办法让我们完全满足开闭原则呢?
对于 Java 语言来说,我们可以通过反射来避免对策略工厂类的修改。具体是这么做的:
- 我们通过一个配置文件或者自定义的 annotation 来标注都有哪些策略类;策略工厂类读取配置文件或者搜索被 annotation 标注的策略类,然后通过反射动态地加载这些策略类、创建策略对象;
- 当我们新添加一个策略的时候,只需要将这个新添加的策略类添加到配置文件或者用 annotation 标注即可。
当添加新的排序算法时,我们只需要改动配置文件即可,不需要改动代码。
参考:《设计模式之美》–王争