一、策略模式:
封装不同的算法,实现客户端调用时可以根据需求,在不修改原算法的情况下,可以很好的切换不同的算法,实现不同的需求;即不同的策略实现不同的功能;比如Comparator比较器接口,由客户端自己去实现不同的compare(Object 1,Object o2)方法,可以由客户端自己去实现对不同对象的排序策略;
策略模式最重要的地方是将策略(不同的算法)的定义、创建、使用,进行解耦;
需求:对不同的物种进行不同的排序
比如对猫和狗进行排序,对男孩和女孩进行排序如何实现?
非策略模式实现:
1、定义比较器接口
public interface MyComparable<T> {
int compareTo(T t1);
}
2、定义需要比较的对象:
/**
* 实体类,需要比较的对象必须实现比较的接口,实现比较的算法
*/
public class Cat implements MyComparable<Cat> {
public Cat(int weight, int height, int food) {
this.weight = weight;
this.height = height;
this.food = food;
}
private int weight;
private int height;
private int food;
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getFood() {
return food;
}
public void setFood(int food) {
this.food = food;
}
@Override
public String toString() {
return "Cat{" +
"weight=" + weight +
", height=" + height +
", food=" + food +
'}';
}
@Override
public int compareTo(Cat t1) {
if (this.weight > t1.weight) {
return 1;
}
if (this.weight < t1.weight) {
return -1;
}
return 0;
}
}
public class Doggy implements MyComparable<Doggy> {
public Doggy(int weight, int height, int food) {
this.weight = weight;
this.height = height;
this.food = food;
}
private int weight;
private int height;
private int food;
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getFood() {
return food;
}
public void setFood(int food) {
this.food = food;
}
@Override
public String toString() {
return "Doggy{" +
"weight=" + weight +
", height=" + height +
", food=" + food +
'}';
}
@Override
public int compareTo(Doggy t1) {
if (this.food > t1.food) {
return 1;
}
if (this.food < t1.food) {
return -1;
}
return 0;
}
}
3、客户端实现比较算法:
public class SortClient {
public static void main(String[] args) {
//比较Cat
Cat[] cats = {new Cat(3, 3, 3), new Cat(1, 1, 1), new Cat(5, 5, 5)};
for (int i = 0; i < cats.length; i++) {
Cat cat1 = cats[i];
for (int j = 0; j < cats.length; j++) {
Cat cat2 = cats[j];
if (cat2.compareTo(cat1) > 0) {
//交换下标位置
Cat temp = cats[i];
cats[i] = cats[j];
cats[j] = temp;
}
}
}
System.out.println("cat sort:" + cats[0] + cats[1] + cats[2]);
}
}
上述案例中,Cat对象是通过比较weight属性来判断大小,Doggy对象是通过比较food属性来比较大小,在实际开发过程中,后续可能会存在这样的需求,比如Cat对象需要通过height属性或者food属性来比较大小,Doggy也需要通过比较weight或者height属性来比较大小,基于设计模式的开闭原则,在不修改原来的代码的情况下,如何实现功能的扩展?
使用策略模式,在原来的代码基础上进行修改:
/**
* @param <T> 自定义一个比较器
*/
public interface MyComparator<T> {
int compare(T t1, T t2);
}
定义一个sort方法,需要传入比较器
public static void sort(Cat[] cats, MyComparator<Cat> myComparator) {
for (int i = 0; i < cats.length; i++) {
Cat cat1 = cats[i];
for (int j = 0; j < cats.length; j++) {
Cat cat2 = cats[j];
if (myComparator.compare(cat1, cat2) > 0) {
//交换下标位置
Cat temp = cats[i];
cats[i] = cats[j];
cats[j] = temp;
}
}
}
System.out.println("cat sort:" + cats[0] + cats[1] + cats[2]);
}
客户端使用:
//通过策略模式根据不同的属性进行排序
sort(cats, new MyComparator<Cat>() {
@Override
public int compare(Cat t1, Cat t2) {
if (t1.getFood() > t2.getFood()) return 1;
if (t1.getFood() < t2.getFood()) return -1;
return 0;
}
});
//lamda表达式写法
sort(cats, (t1, t2) -> {
if (t1.getFood() > t2.getFood()) return 1;
if (t1.getFood() < t2.getFood()) return -1;
return 0;
});
Comparator和Comparable
Comparable相当于一个类的自然比较功能,实现该接口的类,能实现对当前类与接口的compareTo方法传进来的参数进行比较;比较的是当前对象与传参进来的对象;
Comparator是一个比较器,策略模式的一种实现,在客户端使用自定义的比较器去实现不同的比较功能;能比较任意的两个对象;java.lang.utils.Comparator里面的比较器也是这样实现的
二、策略模式的其他实现方式:
计算器的实现,通过输入两个不同的值,分别进行+-*%
定义算法接口:
public interface MathOperator {
double operate(double o1, double o2);
}
不同的算法实现:
public class AdditionOperator implements MathOperator {
@Override
public double operate(double o1, double o2) {
return o1 + o2;
}
}
public class SubtractionOperator implements MathOperator {
@Override
public double operate(double o1, double o2) {
return o1 - o2;
}
}
public class MultiplicationOperator implements MathOperator {
@Override
public double operate(double o1, double o2) {
return o1 * o2;
}
}
public class DivideOperator implements MathOperator {
@Override
public double operate(double o1, double o2) {
if (o2 == 0) {
throw new RuntimeException();
}
return o1 / o2;
}
}
客户端调用不同的实现,不同的算法
public class MathClient {
public static void main(String[] args) {
double a = 1.89;
double b = 3.78;
//实现加减乘除,不同的运算
operate(new AdditionOperator(), a, b);
operate(new SubtractionOperator(), a, b);
operate(new MultiplicationOperator(), a, b);
operate(new DivideOperator(), a, b);
}
public static void operate(MathOperator mathOperator, double o1, double o2) {
mathOperator.operate(o1, o2);
}
}
四、优缺点及适应场景
每个策略都实现了同一个接口,使得在上下文中可以任意切换不同的策略实现不同的功能;
增加策略只需要增加策略的接口实现,不需要修改原来的代码,易于扩展,符合开闭原则;
避免使用过多的if()else语句;