策略模式的思考

介绍

策略模式首先是一种行为型模式,把定义的一系列的算法,封装起来,然后根据用户的需求来决定采用哪个算法,并且算法的修改对用户也没影响。


策略模式UML图.png

Context:策略上下文。
Strategy:抽象策略
ConcreteStrategy:具体策略

举例

我们定义了一个整型数组,数组里面的整数都是没有顺序的,然后用户的需求是打算,从小到大排列。然后根据用户的需求来决定采用哪种算法来排序。每个算法之间也可以相互替换,并且这种替换对于用户来说是没有影响的。

策略模式代码

  1. 抽象策略接口:排序接口
public interface ISort {
    
    public int[] sort(int[] strArray);
}
  1. 具体策略1:直接插入排序
/**
 * @description 直接插入排序
 * 
 * @author sunpy
 * @date 2018年10月21日  下午8:08:15
 */
public class DirectInsertSort implements ISort{

    @Override
    public int[] sort(int[] strArray) {
        for (int i = 1 ; i < strArray.length ; i++) {
            int nowValue = strArray[i];
            int j = i - 1;
            
            while (j >= 0 && nowValue < strArray[j]) {
                strArray[j + 1] = strArray[j];
                j--;
            }
            
            strArray[j + 1] = nowValue;
        }
        
        return strArray;
    }
}
  1. 具体策略2:冒泡排序
/**
 * @description 冒泡排序
 * 
 * @author sunpy
 * @date 2018年10月21日  下午8:08:05
 */
public class BubbleSort implements ISort{

    @Override
    public int[] sort(int[] strArray) {
        for (int i = 0 ; i < strArray.length ; i++) {
            for (int j = i + 1 ; j < strArray.length ; j++) {
                if (strArray[i] > strArray[j]) {
                    int temp = strArray[i];
                    strArray[i] = strArray[j];
                    strArray[j] = temp;
                }
            }
        }
        
        return strArray;
    }
}
  1. 引用策略类
/**
 * @description 引用策略类
 * 
 * @author sunpy
 * @date 2018年10月21日  下午8:07:40
 */
public class SortQuote {

    private ISort iSort;
    
    public SortQuote(ISort iSort) {
        this.iSort = iSort;
    }
    
    public int[] useSort(int[] strArray) {
        return iSort.sort(strArray);
    }
}

测试:

public class MyTest {
    
    public static void printArr(int[] arr) {
        for (int i : arr) {
            System.out.print(i);
        }
    }
    
    public static void main(String[] args) throws Exception {
        int[] arr = {2,6,7,1,3,8,4,5};
        SortQuote sq1 = new SortQuote(new DirectInsertSort());
        SortQuote sq2 = new SortQuote(new BubbleSort());
        int[] arr1 = sq1.useSort(arr);
        printArr(arr1);
        System.out.printf("\n");
        int[] arr2 = sq2.useSort(arr);
        printArr(arr2);
    }
}
结果

策略模式优化if / else

我们在写代码中:

        if ("BubbleSort".equals(type)) {
            ....
        } else if ("DirectInsertSort".equals(type)) {
            ....
        } else {
            ....
        }

不同策略:

public interface ISort {
    public Integer[] sort(Integer[] strArray);
}

@Service("bubbleSort")
public class BubbleSortImpl implements ISort{

    @Override
    public Integer[] sort(Integer[] strArray) {
        for (int i = 0 ; i < strArray.length ; i++) {
            for (int j = i + 1 ; j < strArray.length ; j++) {
                if (strArray[i] > strArray[j]) {
                    int temp = strArray[i];
                    strArray[i] = strArray[j];
                    strArray[j] = temp;
                }
            }
        }

        return strArray;
    }
}

@Service("directInsertSort")
public class DirectInsertSortImpl implements ISort{

    @Override
    public Integer[] sort(Integer[] strArray) {
        for (int i = 1 ; i < strArray.length ; i++) {
            int nowValue = strArray[i];
            int j = i - 1;

            while (j >= 0 && nowValue < strArray[j]) {
                strArray[j + 1] = strArray[j];
                j--;
            }

            strArray[j + 1] = nowValue;
        }

        return strArray;
    }
}

优化第一步:使用策略模式 + 简单工厂模式实现将不同条件的业务委派给不同的类去实现

抽象策略、具体策略与之前一样,不发生变化,现在修改策略上下文,然后根据输入不同的类型去调用不同类的不同方法。

@Service
public class SortStrategy {

    @Autowired
    private ISort bubbleSort;

    @Autowired
    private ISort directInsertSort;

    public Integer[] doSort(Integer[] strArray, String type) {
        if (Objects.equals(type, "BubbleSort")) {
            return bubbleSort.sort(strArray);
        } else if (Objects.equals(type, "DirectInsertSort")) {
            return directInsertSort.sort(strArray);
        }

        return null;
    }
}

理解:引入工厂模式,本身就是将代码耦合在了一起,违背了开闭原则。实际上if / else根本没有消除只是转移到上下文,还是存在ifelse问题。

优化第二步:使用策略模式+享元模式,将不同的策略存储起来,使用List来存储获取,将List交由spring管理。

@Service
public class SortStrategy {

    @Autowired
    private List<ISort> storeSortList;

    public Integer[] doSort(Integer[] strArray, String type) {
        ISort selectedSort = storeSortList.stream().filter(iSort -> iSort.currentSortStrategy(type)).findFirst().orElse(null);

        if (Objects.isNull(selectedSort)) {
            throw new CommonException("当前不存在映射的策略");
        }

        return selectedSort.sort(strArray);
    }
}
public interface ISort {
    public Integer[] sort(Integer[] strArray);
    // 查找当前使用哪种策略
    public boolean currentSortStrategy(String sortName);
}

@Service("bubbleSort")
public class BubbleSortImpl implements ISort{

    @Override
    public Integer[] sort(Integer[] strArray) {
        for (int i = 0 ; i < strArray.length ; i++) {
            for (int j = i + 1 ; j < strArray.length ; j++) {
                if (strArray[i] > strArray[j]) {
                    int temp = strArray[i];
                    strArray[i] = strArray[j];
                    strArray[j] = temp;
                }
            }
        }

        return strArray;
    }

    @Override
    public boolean currentSortStrategy(String sortName) {
        return Objects.equals(SortedStrategyEnum.BUBBLE_SORT.getName(), sortName);
    }
}

优化第三步:既然我们根据名称去找集合List中对应的策略类,那么我们可以通过Map集合来建立这种映射结构。

@Service
public class SortStrategy {

    @Autowired
    private Map<String, ISort> storeSortMap;

    public Integer[] doSort(Integer[] strArray, String type) {
        ISort selectedSort = storeSortMap.get(type);

        if (Objects.isNull(selectedSort)) {
            throw new CommonException("当前不存在映射的策略");
        }

        return selectedSort.sort(strArray);
    }
}

理解:既然维护映射关系,我们可以使用SPI方式维护,通过SPI技术来动态发现java服务。

优化第四步:使用SPI来代替Map,映射存储类名与接口,实现动态发现服务,调用不同策略。

配置

配置文件内容

策略上下文实现:

public class SortStrategy {

    public void doSort(Integer[] strArray, Class<?> clazz) {
        ServiceLoader<ISort> serviceLoader = ServiceLoader.load(ISort.class);

        serviceLoader.forEach(iSort -> {
            if (clazz.getCanonicalName().equals(iSort.getClass().getCanonicalName())) {
                iSort.sort(strArray);
            }
        });
    }
}

理解:上面的代码只是将原本的对象和类名的映射关系从Map转移到了文件而已。
SPI技术可参考:https://www.jianshu.com/p/06f8082dadd3

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容