策略模式
- 为什么使用策略模式?
- 策略模式包含角色
- 策略模式的类图
- 排序案例
- 策略模式的优点
- 策略模式的缺点
- 适用场景
- 源码分析策略模式的典型应用
-
- Java Comparator 中的策略模式
- 参考文章
为什么使用策略模式?
实现某一个功能有多条途径,每一条途径对应一种算法,此时我们可以使用一种设计模式来实现灵活地选择解决途径,也能够方便地增加新的解决途径。
策略模式包含角色
-
Context(环境类)
:环境类是使用算法的角色,它在解决某个问题(即实现某个方法)时可以采用多种策略。在环境类中维持一个对抽象策略类的引用实例,用于定义所采用的策略。 -
Strategy(抽象策略类)
:它为所支持的算法声明了抽象方法,是所有策略类的父类,它可以是抽象类或具体类,也可以是接口。环境类通过抽象策略类中声明的方法在运行时调用具体策略类中实现的算法。 -
ConcreteStrategy(具体策略类)
:它实现了在抽象策略类中声明的算法,在运行时,具体策略类将覆盖在环境类中定义的抽象策略类对象,使用一种具体的算法实现某个业务处理。
策略模式的类图
策略模式需要一个策略接口,不同的策略实现不同的实现类,在具体业务环境中仅持有该策略接口,根据不同的场景使用不同的实现类即可。
面向接口编程,而不是面向实现。
排序案例
对数组进行排序的算法有很多,但是不同的算法在不同的场景下可以发挥更大的效率,例如数据量很大的时候,我们可以使用快速排序,数据量小的时候就可以采用插入排序
抽象策略类
//抽象策略类
public interface Strategy
{
public void sort();
}
具体策略类
public class QuickSort implements Strategy
{
@Override
public void sort() {
System.out.println("快速排序");
}
}
public class InsertSort implements Strategy
{
@Override
public void sort() {
System.out.println("插入排序");
}
}
public class BubbleSort implements Strategy
{
@Override
public void sort() {
System.out.println("冒泡排序");
}
}
环境类
public class Context
{
private Strategy strategy;
public void sort(int[] arr,Strategy strategy)
{
this.strategy=strategy;
doSort();
}
private void doSort()
{
strategy.sort();
}
}
测试类
public class Client
{
public static void main(String[] args) {
int[] arr={1,1,1,1,1,1,1,1,1,1};
int[] arr1={1,1,1,1,1,1};
int[] arr2={1,1,1};
Context context=new Context();
context.sort(arr,new QuickSort());
context.sort(arr1,new InsertSort());
context.sort(arr2,new BubbleSort());
}
}
策略模式的优点
- 策略模式提供了对 “开闭原则” 的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
- 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族,恰当使用继承可以把公共的代码移到抽象策略类中,从而避免重复的代码。
- 策略模式提供了一种可以替换继承关系的办法。如果不使用策略模式而是通过继承,这样算法的使用就 和算法本身混在一起,不符合 “单一职责原则”,而且使用继承无法实现算法或行为在程序运行时的动态切 换。
- 使用策略模式可以避免多重条件选择语句。多重条件选择语句是硬编码,不易维护。
- 策略模式提供了一种算法的复用机制,由于将算法单独提取出来封装在策略类中,因此不同的环境类可以方便地复用这些策略类。
策略模式的缺点
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
- 策略模式将造成系统产生很多具体策略类,任何细小的变化都将导致系统要增加一个新的具体策略类。
- 无法同时在客户端使用多个策略类,也就是说,在使用策略模式时,客户端每次只能使用一个策略类,不支持使用一个策略类完成部分功能后再使用另一个策略类来完成剩余功能的情况。
适用场景
- 一个系统需要动态地在几种算法中选择一种,那么可以将这些算法封装到一个个的具体算法类中,而这些具体算法类都是一个抽象算法类的子类。换言之,这些具体算法类均有统一的接口,根据 “里氏代换原则” 和面向对象的多态性,客户端可以选择使用任何一个具体算法类,并只需要维持一个数据类型是抽象算法类的对象。
- 一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重条件选择语句来实现。此时,使用策略模式,把这些行为转移到相应的具体策略类里面,就可以避免使用难以维护的多重条件选择语句。
- 不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法与相关的数据结构,可以提高算法的保密性与安全性。
源码分析策略模式的典型应用
Java Comparator 中的策略模式
java.util.Comparator
接口是比较器接口,可以通过 Collections.sort(List,Comparator)
和 Arrays.sort(Object[],Comparator)
对集合和数据进行排序,下面为示例程序
一个学生类,有两个属性 id 和 name
@Data
@AllArgsConstructor
public class Student {
private Integer id;
private String name;
@Override
public String toString() {
return "{id=" + id + ", name='" + name + "'}";
}
}
实现两个比较器,比较器实现了 Comparator
接口,一个升序,一个降序
// 降序
public class DescSortor implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o2.getId() - o1.getId();
}
}
// 升序
public class AscSortor implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.getId() - o2.getId();
}
}
通过 Arrays.sort()
对数组进行排序
public class Test1 {
public static void main(String[] args) {
Student[] students = {
new Student(3, "张三"),
new Student(1, "李四"),
new Student(4, "王五"),
new Student(2, "赵六")
};
toString(students, "排序前");
Arrays.sort(students, new AscSortor());
toString(students, "升序后");
Arrays.sort(students, new DescSortor());
toString(students, "降序后");
}
public static void toString(Student[] students, String desc){
for (int i = 0; i < students.length; i++) {
System.out.print(desc + ": " +students[i].toString() + ", ");
}
System.out.println();
}
}
通过 Collections.sort()
对集合List进行排序
public class Client
{
public static void main(String[] args) {
List<Stu> students = Arrays.asList(
new Stu(3, "张三"),
new Stu(1, "李四"),
new Stu(4, "王五"),
new Stu(2, "赵六")
);
toString(students, "排序前");
Collections.sort(students, new AscSortor());
toString(students, "升序后");
Collections.sort(students, new DescSortor());
toString(students, "降序后");
}
public static void toString(List<Stu> students, String desc){
for (Stu student : students) {
System.out.print(desc + ": " + student.toString() + ", ");
}
System.out.println();
}
}
我们向 Collections.sort()
和 Arrays.sort()
分别传入不同的比较器即可实现不同的排序效果(升序或降序)
这里 Comparator
接口充当了抽象策略角色,两个比较器 DescSortor
和 AscSortor
则充当了具体策略角色,Collections
和 Arrays
则是环境角色
参考文章
策略模式