设计模式只策略模式

66 阅读7分钟

前言

之前学习了工厂设计模式,简单工厂将所有的产品通过一个工厂生产;工厂方法不同的产品使用不同的工厂生产,每个工厂类实现工厂接口,每个产品类实现产品接口;抽象工厂则为同一个系列不同产品而准备的,一个抽象工厂的实现类可以生产某一个系列下的所有产品。

其实工厂模式我的理解就是就将具体类的实例化(new)交给了具体的工厂类来处理。这样处理的好处就是如果需要改为实例化的类,只要让新的类实现相同的接口,然后修改对应工厂中创建实例方法即可。

每个设计模式都是为了解决某种问题而设计出来的,策略模式也是如此。

图片

演进

以排序需求为例,一步步修改演进出所需的策略模式。

需求1

对一个数组中的数字进行排序。

代码1

//使用冒泡排序
public class Sort {
   public void sort(int[] arr){
       for (int i = 0; i < arr.length; i++) {
           int minPos = i;
           for (int j = i+1; j < arr.length; j++) {
               minPos = arr[j]<arr[minPos]?j:minPos;
          }
           swap(arr,i,minPos);
      }
  }
​
   private void swap(int[] arr,int i,int minPos){
       int tmp = arr[minPos];
       arr[minPos] = arr[i];
       arr[i] = tmp;
  }
//测试结果[1, 2, 3, 4, 5, 6, 7, 8, 9]
   public static void main(String[] args) {
       int[] a = {6,4,2,5,7,8,1,9,3};
       Sort s = new Sort();
       s.sort(a);
       System.out.println(Arrays.toString(a));
  }
}

需求2

现在修改了需求,对拥有体重和身高的猫使用体重进行排序。

public class Cat{
   public int weight;
   public int height;
​
   public Cat(int weight, int height) {
       this.weight = weight;
       this.height = height;
  }
}

代码2

//在cat中增加一个compareTo方法用于比较体重
public class Cat implements Comparable{
   public int weight;
   public int height;
​
   public Cat(int weight, int height) {
       this.weight = weight;
       this.height = height;
  }
​
   /**
    * 需求2增加功能
    * @param c
    * @return
    */
   public int compareTo(Cat c){
       if(weight>c.weight){
           return 1;
      }
       if(weight<c.weight){
           return -1;
      }
       return 0;
  }
   
   @Override
   public String toString() {
       return "Cat{" +
               "weight=" + weight +
               ", height=" + height +
               '}';
  }
​
}
//修改排序方法使用compareTo进行比较
public class Sort1 {
   public void sort(Cat[] arr){
       for (int i = 0; i < arr.length; i++) {
           int minPos = i;
           for (int j = i+1; j < arr.length; j++) {
               minPos = arr[j].compareTo(arr[minPos])==-1?j:minPos;
          }
           swap(arr,i,minPos);
      }
  }
​
   private void swap(Cat[] arr,int i,int minPos){
       Cat tmp = arr[minPos];
       arr[minPos] = arr[i];
       arr[i] = tmp;
  }
//测试结果[Cat{weight=10, height=7}, Cat{weight=20, height=6}, Cat{weight=30, height=3}, Cat{weight=40, height=8}, Cat{weight=50, height=2}]
   public static void main(String[] args) {
       Cat[] a = new Cat[5];
       a[0] = new Cat(20,6);
       a[1] = new Cat(40,8);
       a[2] = new Cat(30,3);
       a[3] = new Cat(10,7);
       a[4] = new Cat(50,2);
​
       Sort1 s = new Sort1();
       s.sort(a);
       System.out.println(Arrays.toString(a));
  }
}

需求3

除了对猫进行排序,还想对蛇、狗等其他动物排序。

这个时候,排序sort方法参数要能同时兼容不同的动物,这个参数要么是接口要么是几个动物的父类。

代码3

#动物对象实现java的Comparable接口和compareTo方法

public class Cat implements Comparable{
   public int weight;
   public int height;
​
   public Cat(int weight, int height) {
       this.weight = weight;
       this.height = height;
  }
​
   /**
    * 需求2增加Comparable接口实现,同时实现方法
    * @param o the object to be compared.
    * @return
    */
   @Override
   public int compareTo(Object o) {
       Cat c = (Cat)o;
       if(weight>c.weight){
           return 1;
      }
       if(weight<c.weight){
           return -1;
      }
       return 0;
  }
​
   @Override
   public String toString() {
       return "Cat{" +
               "weight=" + weight +
               ", height=" + height +
               '}';
  }
​
}
public class Dog implements Comparable{
   public int age;
   public int weight;
​
   public Dog(int age, int weight) {
       this.age = age;
       this.weight = weight;
  }
​
   @Override
   public int compareTo(Object o) {
       Dog d = (Dog)o;
       if(age>d.age){
           return 1;
      }
       if(age<d.age){
           return -1;
      }
       return 0;
  }
​
   @Override
   public String toString() {
       return "Dog{" +
               "age=" + age +
               ", weight=" + weight +
               '}';
  }
}

#修改排序类

public class Sort2 {
   public void sort(Comparable[] arr){
       for (int i = 0; i < arr.length; i++) {
           int minPos = i;
           for (int j = i+1; j < arr.length; j++) {
               minPos = arr[j].compareTo(arr[minPos])==-1?j:minPos;
          }
           swap(arr,i,minPos);
      }
  }
​
   private void swap(Comparable[] arr,int i,int minPos){
       Comparable tmp = arr[minPos];
       arr[minPos] = arr[i];
       arr[i] = tmp;
  }
​
   public static void main(String[] args) {
       Cat[] a = new Cat[5];
       a[0] = new Cat(20,6);
       a[1] = new Cat(40,8);
       a[2] = new Cat(30,3);
       a[3] = new Cat(10,7);
       a[4] = new Cat(50,2);
​
       Sort2 s = new Sort2();
       s.sort(a);
       System.out.println(Arrays.toString(a));
​
       Dog[] b = new Dog[5];
       b[0] = new Dog(5,50);
       b[1] = new Dog(4,60);
       b[2] = new Dog(7,40);
       b[3] = new Dog(3,80);
       b[4] = new Dog(6,20);
​
       s.sort(b);
       System.out.println(Arrays.toString(b));
  }
}

需求4

现在我希望每次实现实体类的compareTo不需要使用强转,如Dog d = (Dog)o。

要做到以上要求,这里需要引入泛型。

代码4

#修改动物实体类在实现Comparable接口时,使用泛型Comparable

public class Pig implements Comparable<Pig>{
   public int weight;
   public int height;
​
   public Pig(int weight, int height) {
       this.weight = weight;
       this.height = height;
  }
​
   /**
    * 需求4使用泛型后再也不用使用强制转化
    * 这里和之前的Cat和Dog不一样
    * @param c the object to be compared.
    * @return
    */
   @Override
   public int compareTo(Pig c) {
       if(weight>c.weight){
           return 1;
      }
       if(weight<c.weight){
           return -1;
      }
       return 0;
  }
​
   @Override
   public String toString() {
       return "Cat{" +
               "weight=" + weight +
               ", height=" + height +
               '}';
  }
​
}

#排序方法不变

   public static void main(String[] args) {
       Pig[] a = new Pig[5];
       a[0] = new Pig(20,6);
       a[1] = new Pig(40,8);
       a[2] = new Pig(30,3);
       a[3] = new Pig(10,7);
       a[4] = new Pig(50,2);
​
       Sort3 s = new Sort3();
       s.sort(a);
       System.out.println(Arrays.toString(a));
  }

需求5

到需求4为止我们已经可以,不同的动物排序使用自己定义的排序方式了,但相同的动物如果想要切换不同的排序比较方式,我们还得改实体类中的compareTo的方法。

为了减少改动,我们可以讲compareTo这部分抽出来,定义一个比较器接口,具体的比较通过实现比较器接口Comparator。而sort方法中增加一个形参就是Comparator接口,这样我们在调用排序的方法的时候将需要的比较器实现类传入即可。

代码5

#简化后的实体类

public class Crow {
   public int weight;
   public int height;
​
   public Crow(int weight, int height) {
       this.weight = weight;
       this.height = height;
  }
​
   @Override
   public String toString() {
       return "Cat{" +
               "weight=" + weight +
               ", height=" + height +
               ", weight/height=" + weight/height +
               '}';
  }
​
}

#比较器接口定义了泛型

public interface Comparator<T> {
  public int compare(T t1,T t2);
}

#比较器实现类指定泛型

public class CrowComparator implements Comparator<Crow>{
   @Override
   public int compare(Crow t1, Crow t2) {
       if(t1.weight>t2.weight){
           return 1;
      }
       if(t1.weight<t2.weight){
           return -1;
      }
       return 0;
  }
}
public class CrowComparator1 implements Comparator<Crow>{
   @Override
   public int compare(Crow t1, Crow t2) {
       if(t1.weight/ t1.height>t2.weight/t2.height){
           return 1;
      }
       if(t1.weight/ t1.height<t2.weight/t2.height){
           return -1;
      }
       return 0;
  }
}

#排序类定了泛型,同时在方法入参上也定义了泛型和比较器接口

public class Sort4<T> {
   public void sort(T[] arr, Comparator<T> comparator){
       for (int i = 0; i < arr.length; i++) {
           int minPos = i;
           for (int j = i+1; j < arr.length; j++) {
               minPos = comparator.compare(arr[j],arr[minPos])==-1?j:minPos;
          }
           swap(arr,i,minPos);
      }
  }
​
   private void swap(T[] arr,int i,int minPos){
       T tmp = arr[minPos];
       arr[minPos] = arr[i];
       arr[i] = tmp;
  }
​
   public static void main(String[] args) {
       Crow[] a = new Crow[5];
       a[0] = new Crow(20,6);
       a[1] = new Crow(40,8);
       a[2] = new Crow(30,3);
       a[3] = new Crow(10,7);
       a[4] = new Crow(50,2);
       Sort4<Crow> s = new Sort4<>();
       s.sort(a,new CrowComparator());
       System.out.println(Arrays.toString(a));
​
       s.sort(a,new CrowComparator1());
       System.out.println(Arrays.toString(a));
​
       //lambada函数式接口,接口只有一个需要实现的方法就是函数式接口
       s.sort(a,(t1,t2)->{
           if(t1.height>t2.height){
           return 1;
          }
           if(t1.height<t2.height){
               return -1;
          }
           return 0;
      });
       System.out.println(Arrays.toString(a));
  }
}

直至需求5才是策略模式,可以自由选择自己想要使用的策略。

类图

图片

通过类图我们可以看到,在需求5中实现的类一个关系图。其中排序方法中只依赖排序接口,在实际排序中我们可以传递任何一个实体类和他对应的排序接口实现类。这样我们完全可以不用因为排序比较策略的改变去改变相关的代码,我们只需要实现新的比较策略然后传入排序方法就行,这其实也是一种解耦的策略。

总结

策略模式给我感觉主要是让策略的调整更灵活,是的具体依赖策略的类,不再强耦合某个具体的策略,而是只依赖其接口,具体实现由使用的时候传参决定。如果做的更灵活一点,我们可以见策略的配置到配置文件,中这样项目启动的时候就可以直接读取相应的策略了。

能力有限水平一般,如有纰漏望指正~