策略设计模式的设计理念以及简单用法

4 阅读4分钟

情景举例: 当需要针对某些对象中的特定属性且需要制定特殊比较规则时,定义一个对象数组排序器(另一种方法可将数组转为集合,使用stream流中的AIP实现特定排序规则,这里举例说明使用策略模式),进行排序时需要利用到策略设计模式,利用泛型将规则封装起来,并且使比较类别可以自定义替换,实现广泛复用的目的。

传统设计理念需要实现多种不同的数据类型或者对象实现指定条件排序,需要对单个方法多次重写实现

//定义了一个比较工具,重写的方式可以实现我们需要的目的,但可操作性和代码可读性较差;策略者模式可以将其中的每种比较规则单独划分出来,增强代码的可操作性(也就是扩展性)
public class Sorter {

    /**
     * 简单的int类型数组排序
     * @param arr
     */
    public static void sortArray(int[] arr) {
        //实现从小到大数组排序
        if (arr.length > 1) {
            for (int i = 0; i < arr.length - 1; i++) {
                for (int j = i + 1; j < arr.length; j++) {
                    int temp = arr[i];
                    if (arr[i] > arr[j]) {
                        arr[i] = arr[j];
                        arr[j] = temp;
                    }
                }
            }
        }
        System.out.println(Arrays.toString(arr));
    }
    
     /**
     * 简单的double类型数组排序
     * @param arr
     */
    public static void sortArray(double[] arr) {
            .............................
    }
    ..............................
}

策略设计模式理念可以将比较规则设置成一个抽象接口,通过对象实现接口中的比较规则,赋予自己特有属性的比较规则实现排序,比如:

//自定义比较器<接口>
public interface Comparator<T> {
    //用于对象构建特定的比较规则,返回一个int数据作为判断标识
    Integer compareRegular(T o1, T o2);
}
//定义的排序器
public class Sorter<T> {

    /**
     * 通过泛型策略模式实现可扩展性的排序算法
     * 创建自定义比较器,指定特定对象且制定特殊的比较方法给出大小结果
     *
     * @param arr        比较对象数组
     * @param comparator 比较器(对象中特定的比较规则)
     */
    public void sort(T[] arr, Comparator<T> comparator) {
        //实现从小到大数组排序
        if (arr.length > 1) {
            for (int i = 0; i < arr.length - 1; i++) {
                for (int j = i + 1; j < arr.length; j++) {
                    T temp = arr[i];
                    //compareRegular() 为对象中重写的指定比较策略方法
                    if (comparator.compareRegular(arr[i], arr[j]) == 1) {
                        arr[i] = arr[j];
                        arr[j] = temp;
                    }
                }
            }
        }
        System.out.println(Arrays.toString(arr));
    }
}
//定义一个需要进行比较的对象
//这里使用了lombok工具包中的注解,便捷生成get、set以及全参构造器
@Data
@AllArgsConstructor
class Cat {
    private Double food;
    private Double age;
    private String name;

    //重写对象toString
    @Override
    public String toString() {
        return "Cat{" + "name=" + name +
            ", weight=" + weight +
            ", age=" + age +
            ", food=" + food +
            '}';
    }

}


//定义一个Cat对象的特定单一属性比较器
public class CatFoodComparator implements Comparator<Cat> {

    @Override
    public Integer compareRegular(Cat o1, Cat o2) {
       //基本数据类型中的compareTo方法当 o1指定属性大于o2时返回1 相等时返回 0 ,小于时返回-1 
        return o1.getFood().compareTo(o2.getFood());
    }

}

//定义一个Cat对象的特定多属性比较器
class CatNameComparator implements Comparator<Cat> {
    @Override
    public Integer compareRegular(Cat o1, Cat o2) {
        if (o1.getName().length() > o2.getName().length()) {
            return 1;
        } else if (o1.getName().length() == o2.getName().length()) {
            //如果名字长度相同,比较年龄大小
            return o1.getAge().compareTo(o2.getAge());
        } else {
            return -1;
        }
    }
}

测试

public class StrategyModelTest {
    public static void main(String[] args) {
        Cat[] arr = {new Cat(1.1, 1.0, "英缅猫")
            , new Cat(1.2, 2.0, "英缅猫")
            , new Cat(1.2, 1.5, "金渐层长毛猫")
            , new Cat(1.3, 2.0, "银渐层猫")
            , new Cat(1.2, 1.5, "金渐层猫")
            , new Cat(1.5, 3.0, "布偶猫")};
        Sorter<Cat> sorter = new Sorter<>();
        
        //food属性降序排列
        sorter.sort(arr, new CatFoodComparator());    
        //name长度降序排列,当长度相等时降序按年龄大小规则排列
        sorter.sort(arr, new CatNameComparator());
    }

}

打印结果为:

food属性比较器排列结果:

[Cat{name=英缅猫, age=1.0, food=1.1}, Cat{name=英缅猫, age=2.0, food=1.2}, Cat{name=金渐层长毛猫, age=1.5, food=1.2}, Cat{name=金渐层猫, age=1.5, food=1.2}, Cat{name=银渐层猫, age=2.0, food=1.3}, Cat{name=布偶猫, age=3.0, food=1.5}]

name属性比较器排列结果:

[Cat{name=英缅猫, age=1.0, food=1.1}, Cat{name=英缅猫, age=2.0, food=1.2}, Cat{name=布偶猫, age=3.0, food=1.5}, Cat{name=金渐层猫, age=1.5, food=1.2}, Cat{name=银渐层猫, age=2.0, food=1.3}, Cat{name=金渐层长毛猫, age=1.5, food=1.2}]

总结:策略者模可以细化和制定化复用代码模式,虽然代码量、类和接口的创建增加了很多,但可扩展性高,对与应用整合以及大多数二开来说是非常有益的