策略模式

策略模式把对象本身和运算规则区分开来,其功能非常强大,因为这个设计模式本身的核心思想就是面向对象编程的多形性的思想

Posted by 高明 on 2021-09-13

策略模式

背景

实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能。如查找,排序等,一种常用的方法是硬编码(Hard Coding)在一个类中,如需要提供多种查找算法,可以将算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法;当然也可以将这些查找算法封装在一个统一的方法中,通过if...else...或者switch...case...等条件判断语句来进行选择。这两种实现方式我们都可以称之为硬编码,如果需要一种新的查找算法,需要修改封装算法类的源代码;更换查找算法,也需要修改客户端调用代码。在这个算法类中封装了大量查找算法,该类代码将较复杂,维护较为困难,如果我们将这些策略包含在客户端,这种做法更不可取,这将导致客户端程序庞大而且难以维护,如果存在大量可供选择的算法时,问题将变得更加严重。

问题

【问题】如何让算法和对象分开来,使得算法可以独立于使用它的客户而变化?

【解决方案】策略模式:定义一系列的算法,把每一个算法封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化,也称为政策模式(Ploicy)策略模式把对象本身和运算规则区分开来,其功能非常强大,因为这个设计模式本身的核心思想就是面向对象编程的多形性的思想

适用性

当存在以下情况时使用Strategy模式

  1. 许多相关的类仅仅是行为有异。 策略提供了一种用多个行为中的一个行为来配置一个类的方法。即一个系统需要动态地在几种算法中选择一种。
  2. 需要使用一个算法的不同变体。例如,你可能会定义一些反映不同的空间/时间权衡的算法。当这些变体实现为一个算法的类层次时 ,可以使用策略模式。
  3. 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
  4. 一个类定义了多种行为 , 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。

1336732187_4598

模式组成

  1. 环境类(Context):用一个ConcreteStrategy对象来配置,维护一个对Strategy对象的引用。可定义一个接口来Strategy访问它的数据
  2. 抽象策略类(Strategy):定义所有支持的算法的公共接口,Context使用这个接口来调用某ConcrateStrategy定义的算法
  3. 具体策略类(ConcreteStrategy):以Strategy接口来实现某具体算法

Comparator

Comparable, Comparator

Comparable

1
2
3
4
5
6
7
8
9
public interface Comparable<T> {
/**
* 比较器
*
* @param o
* @return
*/
int compareTo(T o);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Dog implements Comparable<Dog> {

private int age;

public Dog(int age) {
this.age = age;
}

@Override
public int compareTo(Dog o) {
return this.age - o.age;
}

@Override
public String toString() {
return "Dog{" +
"age=" + age +
'}';
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Sorted {
public static void sort(Comparable[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 1; j < arr.length; j++) {
if (arr[i].compareTo(arr[j]) > 0) {
swap(arr, i, j);
}
}
}
}

private static void swap(Comparable[] arr, int i, int j) {
Comparable temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}

}
1
2
3
4
5
6
7
public class TestStrategy {
public static void main(String[] args) {
Dog[] dogs = {new Dog(5), new Dog(2), new Dog(4)};
Sorted.sort(dogs);
System.out.println(Arrays.toString(dogs));
}
}

每一个类都可以实现Comparable接口,Sorted方法根据接口中的compareTo进行比较,但是DogcompareTo方法只能实现一次,无法根据不同的策略进行比较

Comparator

1
2
3
public interface Comparator<T> {
int compare(T o1, T o2);
}
1
2
3
4
5
6
7
public class DogComparator implements Comparator<Dog> {

@Override
public int compare(Dog o1, Dog o2) {
return o1.getAge() - o2.getAge();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Dog {

private int age;

public Dog(int age) {
this.age = age;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "Dog{" +
"age=" + age +
'}';
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Sorted<T> {
public void sort(T[] arr, Comparator<T> comparator) {
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 1; j < arr.length; j++) {
if (comparator.compare(arr[i], arr[j]) > 0) {
swap(arr, i, j);
}
}
}
}

private void swap(T[] arr, int i, int j) {
T temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}

}
1
2
3
4
5
6
7
8
public class TestStrategy {
public static void main(String[] args) {
Dog[] dogs = {new Dog(5), new Dog(2), new Dog(4)};
Sorted<Dog> dogSorted = new Sorted<>();
dogSorted.sort(dogs, new DogComparator());
System.out.println(Arrays.toString(dogs));
}
}

因为comparator只有一个方法,因而可以用函数式编程方法

1
2
3
4
5
6
7
8
9
10
public class TestStrategy {
public static void main(String[] args) {
Dog[] dogs = {new Dog(5), new Dog(2), new Dog(4)};
Sorted<Dog> sorted = new Sorted<>();
sorted.sort(dogs, (o1, o2) -> {
return o1.getAge() - o2.getAge();
});
System.out.println(Arrays.toString(dogs));
}
}

参考文章

设计模式 ( 十八 ) 策略模式Strategy(对象行为型)