策略模式,可以简化的说,就是移除 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判断不复杂,代码不多,这并没有任何问题,而且相反,对于简单可行的代码,去使用复杂的模板去实现,是一种过度设计

而且,对于策略模式,其本质上来说,还是解耦策略的定义,进行策略类的区分,减少代码的复杂度

发表评论

邮箱地址不会被公开。 必填项已用*标注