设计模式2-Strategy

本文最后更新于:3 years ago

策略模式

  在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于 行为型模式 。在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

如果想了解策略模式的具体的介绍,菜鸟教程介绍得比较详细↓
菜鸟教程-策略模式

结构图


优缺点

优点:
1、算法可以自由切换。
2、避免使用多重条件判断。
3、扩展性良好。

缺点:
1、策略类会增多。
2、所有策略类都需要对外暴露。

使用场景

1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的 行为 ,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
2、一个系统需要动态地在几种算法中选择一种。
3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

实现代码

试着先实现一个需求,要求建立猫和狗类,猫含有width和height两个属性(都为int),并且实现compareTo接口对height的比较。狗含有food一个属性(为int),实现compareTo接口对food的比较。而我的需求是,实现对猫和狗的排序。

实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
public interface Comparable<T> {
int compareTo(T o);
}//Comparable接口

public class Cat implements Comparable<Cat>{

int Width;
int Height;

public Cat(int width, int height) {
Width = width;
Height = height;
}

@Override
public String toString() {
return "Cat{" +
"Width=" + Width +
", Height=" + Height +
'}';
}

@Override
public int compareTo(Cat o) {
if (this.Height < o.Height) return -1;
else if (this.Height > o.Height) return 1;
else return 0;
}
}//Cat类

public class Dog implements Comparable<Dog> {

int food;

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


@Override
public int compareTo(Dog o) {
if (this.food < o.food) return -1;
else if (this.food > o.food) return 1;
else return 0;
}

@Override
public String toString() {
return "Dog{" +
"food=" + food +
'}';
}
}//Dog类

public class Sorter<T> {

public static void sort(Comparable[] arr) {
for (int i = 0; i < arr.length - 1; 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);
}
}

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

}//Sorter排序函数

public class Main {

public static void main(String[] args) {
// Dog[] o = new Dog[]{new Dog(5), new Dog(1), new Dog(3)};
Cat[] o = new Cat[]{new Cat(5,3), new Cat(1,5), new Cat(3,1)};
Sorter sorter = new Sorter();
sorter.sort(o);
System.out.println(Arrays.toString(o));
}
}

结果如下

1
[Cat{Width=3, Height=1}, Cat{Width=5, Height=3}, Cat{Width=1, Height=5}]

以上代码不是很难实现。接着加大难度

我需要你在使用Sorter函数的情况下,实现既可以根据猫的Width属性对猫进行排序,也可以根据猫的Height进行排序,还可以根据狗的food进行排序。且不能往Comparable接口中添加新的实体类。(添加了代码就很笨重了,不灵活)

乍一看是不是感觉也还行,不是很难。但仔细分析一下,就上面那种方法而言,Sorter()函数中进行比较的方法,是Comparable接口的,Cat、Dog类在实现时,就在类中固定实现了这个方法。

1
2
3
4
5
6
@Override
public int compareTo(Dog o) {
if (this.food < o.food) return -1;
else if (this.food > o.food) return 1;
else return 0;
}

这么一看是不是很笨重,而且你调用了compareTo,只能实现一个属性的判断,而我们的要求是不能往Comparable接口中添加新的实体类。

现在再回看提出的需求,是不是就感觉有点吃力了。

来,分析一下解题思路,这里我们改进了Sorter中的sort方法,多加了一个Comparator类型的参数。

1
2
3
public void sort(T[] arr, Comparator<T> comparator) {
// 内容
}

这里的Comparator是接口,里面有抽象类compare,用来实现类中属性的比较。

1
2
3
public interface Comparator<T> {
int compare(T o1, T o2);
}

而此时,我们创建一个CatWidthComparator类,实现Comparator接口,用来比较Cat类的Width属性

1
2
3
4
5
6
7
8
9
//根据猫的Width属性来进行比较
public class CatWidthComparator implements Comparator<Cat> {
@Override
public int compare(Cat o1, Cat o2) {
if (o1.Width < o2.Width) return -1;
else if (o1.Width > o2.Width) return 1;
else return 0;
}
}

当我们想要对猫的Width属性进行排序的时候,只需在调用sort函数时,传入Cat实例和CatWidthComparator实例即可。

此时的sort函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Sorter<T> {

public void sort(T[] arr, Comparator<T> comparator) {
for (int i = 0; i < arr.length - 1; 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);
}
}

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

}

此时我们用Mian函数调用即可

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Main {

public static void main(String[] args) {
// Dog[] o = new Dog[]{new Dog(5), new Dog(1), new Dog(3)};
// Sorter<Dog> sorter = new Sorter<Dog>();
Cat[] o = new Cat[]{new Cat(5,3), new Cat(1,5), new Cat(3,1)};
Sorter<Cat> sorter = new Sorter<Cat>();
sorter.sort(o, new CatWidthComparator());
System.out.println(Arrays.toString(o));

}

}

结果如下

1
[Cat{Width=1, Height=5}, Cat{Width=3, Height=1}, Cat{Width=5, Height=3}]

如果你想要对Cat的Height属性进行排序时,只要实现CatHeigthComparator类即可,Dog亦是如此。

总结

总体来看,策略模式让算法的变化独立于使用算法的客户,把具体的算法实现从业务逻辑中剥离出来,成为一系列独立算法类,使得它们可以相互替换,这样大大减少了代码的之间的耦合。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!