策略模式,可以简化的说,就是移除 if-else或者switch-case的一种设计模式,今天,我还会拿一个 给文件排序的例子,来讲一讲策略模式的设计意图及应用场景
假设.我们有一个需求,希望写一个小程序,能够对一个文件进行排序,文件中确定只有整形数,数字之间利用逗号隔离,那么如何编写这样的一个程序并实现呢?
简单的方法就是读出文件的内容,然后进行分割,然后排序后重新写回就行,但是如果文件很大呢?超过了内存,如何办,那么就只能使用外部排序算法来做了
这就类似于MapReduce
如果更大,有着1TB大小,就再利用下多线程来进行加快排序吧.或者使用真正的MapReduce框架,来提高多级的处理能力,提高排序效率
首先是单机的实现方式,不涉及到多线程
我们将简单的代码实现贴下去,具体如下
public class Sorter {
private static final long GB = 1000 * 1000 * 1000; public void sortFile(String filePath) { // 省略校验逻辑 File file = new File(filePath); long fileSize = file.length(); if (fileSize < 6 * GB) { // [0, 6GB) quickSort(filePath); } else if (fileSize < 10 * GB) { // [6GB, 10GB) externalSort(filePath); } else if (fileSize < 100 * GB) { // [10GB, 100GB) concurrentExternalSort(filePath); } else { // [100GB, ~) mapreduceSort(filePath); } } private void quickSort(String filePath) { // 快速排序 } private void externalSort(String filePath) { // 外部排序 } private void concurrentExternalSort(String filePath) { // 多线程外部排序 } private void mapreduceSort(String filePath) { // 利用MapReduce多机排序 } } public class SortingTool { public static void main(String[] args) { Sorter sorter = new Sorter(); sorter.sortFile(args[0]); } } |
上面的实现就是如此,对于小文件进行内部排序,大文件进行外部排序,再大的使用多线程和MapReduce
当然,为了简洁,我们将排序相关的逻辑抽离出来,形成单独的排序函数,再一步进行抽分
当然这样并不是完美,因为所有的排序算法都堆在了Sorter类中,导致可读性,可维护性都被降低了
必须将这些东西进行抽离,利用一个ISort接口类,里面有一个sort接口,然后创建不同的实现类去实现这个接口类
public interface ISortAlg {
void sort(String filePath); } public class QuickSort implements ISortAlg { @Override public void sort(String filePath) { //… } } public class ExternalSort implements ISortAlg { @Override public void sort(String filePath) { //… } } public class ConcurrentExternalSort implements ISortAlg { @Override public void sort(String filePath) { //… } } public class MapReduceSort implements ISortAlg { @Override public void sort(String filePath) { //… } } public class Sorter { private static final long GB = 1000 * 1000 * 1000; public void sortFile(String filePath) { // 省略校验逻辑 File file = new File(filePath); long fileSize = file.length(); ISortAlg sortAlg; if (fileSize < 6 * GB) { // [0, 6GB) sortAlg = new QuickSort(); } else if (fileSize < 10 * GB) { // [6GB, 10GB) sortAlg = new ExternalSort(); } else if (fileSize < 100 * GB) { // [10GB, 100GB) sortAlg = new ConcurrentExternalSort(); } else { // [100GB, ~) sortAlg = new MapReduceSort(); } sortAlg.sort(filePath); } } |
经过拆分后,每个类的代码都很少了,将排序算法设计为了独立的类,跟具体的业务逻辑解耦,让排序算法能够复用,
而且,对于上面的代码,可以进一步的优化,因为每种的排序类都是没有状态的,没有必要每次都去创建新的对象,只需要每次都去工厂类中去获取就可以了
我们再一次进行代码的重构
public class SortAlgFactory {
private static final Map<String, ISortAlg> algs = new HashMap<>(); static { algs.put(“QuickSort”, new QuickSort()); algs.put(“ExternalSort”, new ExternalSort()); algs.put(“ConcurrentExternalSort”, new ConcurrentExternalSort()); algs.put(“MapReduceSort”, new MapReduceSort()); } public static ISortAlg getSortAlg(String type) { if (type == null || type.isEmpty()) { throw new IllegalArgumentException(“type should not be empty.”); } return algs.get(type); } } public class Sorter { private static final long GB = 1000 * 1000 * 1000; public void sortFile(String filePath) { // 省略校验逻辑 File file = new File(filePath); long fileSize = file.length(); ISortAlg sortAlg; if (fileSize < 6 * GB) { // [0, 6GB) sortAlg = SortAlgFactory.getSortAlg(“QuickSort”); } else if (fileSize < 10 * GB) { // [6GB, 10GB) sortAlg = SortAlgFactory.getSortAlg(“ExternalSort”); } else if (fileSize < 100 * GB) { // [10GB, 100GB) sortAlg = SortAlgFactory.getSortAlg(“ConcurrentExternalSort”); } else { // [100GB, ~) sortAlg = SortAlgFactory.getSortAlg(“MapReduceSort”); } sortAlg.sort(filePath); } } |
经过上面的测试,现在的代码已经符合了策略模式的代码结构,经过策略模式将策略的定义和创建进行解耦,但是还是很多的if-else逻辑,虽然并不复杂,但是也需要考虑去移除掉
那么,可以进一步的封装,提供一个类,在类中进行判断
public class Sorter {
private static final long GB = 1000 * 1000 * 1000; private static final List<AlgRange> algs = new ArrayList<>(); static { algs.add(new AlgRange(0, 6*GB, SortAlgFactory.getSortAlg(“QuickSort”))); algs.add(new AlgRange(6*GB, 10*GB, SortAlgFactory.getSortAlg(“ExternalSort”))); algs.add(new AlgRange(10*GB, 100*GB, SortAlgFactory.getSortAlg(“ConcurrentExternalSort”))); algs.add(new AlgRange(100*GB, Long.MAX_VALUE, SortAlgFactory.getSortAlg(“MapReduceSort”))); } public void sortFile(String filePath) { // 省略校验逻辑 File file = new File(filePath); long fileSize = file.length(); ISortAlg sortAlg = null; for (AlgRange algRange : algs) { if (algRange.inRange(fileSize)) { sortAlg = algRange.getAlg(); break; } } sortAlg.sort(filePath); } private static class AlgRange { private long start; private long end; private ISortAlg alg; public AlgRange(long start, long end, ISortAlg alg) { this.start = start; this.end = end; this.alg = alg; } public ISortAlg getAlg() { return alg; } public boolean inRange(long size) { return size >= start && size < end; } } } |
这样的代码足够的优美了,将可变的部分隔离到了策略工厂类和Sorter类中的静态代码块中
如果要添加一个Sort,只需要修改对应的静态代码块和添加一个Sort类实现,那么如果想过要添加修改代码的能力
可以通过反射来避免对策略工厂类的修改,通过一个配置文件或者自定义annotation来标注策略类,并且反射的获取到对应的策略类,和对应的策略,新加一个策略的时候,将这个心的策略加入到配置文件或者加上注解就可以了
那么,本章重点就是如下:
之前,对于If-else分支判断,有一种恐惧感,认为if-else是一个烂代码,如果if-else判断不复杂,代码不多,这并没有任何问题,而且相反,对于简单可行的代码,去使用复杂的模板去实现,是一种过度设计
而且,对于策略模式,其本质上来说,还是解耦策略的定义,进行策略类的区分,减少代码的复杂度