一. 问题与解决思路
假设有这样一个需求,希望写一个小程序,实现对一个文件进行排序的功能。如果文件涉及到的文件有不同规模的,如下
很明显不同大小的文件需要使用不同的算法逻辑去实现,而不同的算法逻辑就可以使用策略模式将算法逻辑解耦,具体算法实现不是本文的重点,我们来看下策略模式是如何实现这个代码架构的。
二. 代码实现与分析
1. 业务代码逻辑的架子
如下逻辑架子,简单实现了算法的一些流程步骤,具体算法暂时不实现。
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]);
}
}
2. 代码重构:使用策略模式来解耦代码逻辑
分析下需求和代码结构:
接下来我们分别实现这几个方面
策略定义:
public interface ISortAlg {
void sort(String filePath);
}
public class QuickSort implements ISortAlg {
@Override
public void sort(String filePath) {
//...
}
}
...
ExternalSort implements ISortAlg
ConcurrentExternalSort implements ISortAlg
MapReduceSort implements ISortAlg
...
策略实现:
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;
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;
//AlgRange封装策略类与策略类型,这样就能去除if else
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;
}
}
}
三. 进一步:满足开闭原则:使用注解或配置文件
即便这样,当我们添加新的排序算法的时候,还是需要修改策略实现与选择
的代码,并不完全符合开闭原则。
有什么办法让我们完全满足开闭原则呢?
对于 Java 语言来说,我们可以通过反射来避免对策略工厂类的修改。具体是这么做的:
当添加新的排序算法时,我们只需要改动配置文件即可,不需要改动代码。
参考:《设计模式之美》–王争