【设计模式学习】第二种: Strategy - 策略模式

第二种: Strategy - 策略模式

设计模式真的很不错!


策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。

下面我们看一个简单的例子,来引出策略模式。

1. 引例

package strategy

public class Sorter { //这里有一个排序类,采用的是选择排序算法

	public void sort(int[] 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] < arr[minPos]? j:minPos;
			}
			swap(arr,i,minPos);
		}	
	}

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

}


public class Main {

	public static void main(String[] args) {
		int [] a ={9,2,3,5,7,1,4};
		Sorter sorter = new Sorter();
		sorter.sort(a);
		System.out.println(Arrays.toString(a));
		//输出排序结果
	}
}

在这里插入图片描述
上面是针对的是 int 数组的一个排序,且输出正确。

那么问题来了,如果我想对一个 double 数组排序,或者 float 数组排序,我们是不是要再写一遍这个排序算法呢?

2. 思考一下,如果我们要对下面这个 Cat 排序呢?

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

对猫排序也很简单,需要两步:
第一步,定义猫之间怎么比较大小。第二步,把对外的 int 数组改成 Cat 。
因此我们修改上面的代码:

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

	public int compareTo(Cat c){//我们通过体重来比较大小,体重小的在前面。
		if(this.weight < c.weight) return -1;
		else if(this.weight > c.weight) return 1;
		else return 0;
	}
	@Override
	public String toString() {
		return "Cat [weight=" + weight + ", height=" + height + "]";
	}
	
}

public class Sorter {

	public void sort(Cat[] arr) {//把的 int 数组改成 Cat 类型 。
		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);
		}	
	}

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

public class Main {

	public static void main(String[] args) {
		//int [] a ={9,2,3,5,7,1,4};
		Cat[] a ={new Cat(3,3), new Cat(8,8),new Cat(6,6),new Cat(1,1)};
		Sorter sorter = new Sorter();
		sorter.sort(a);
		System.out.println(Arrays.toString(a));
	}
}

来测试一下,生成4个Cat来比较大小。
在这里插入图片描述
结果正确!

3. 继续思考,如果我们要对其他动物,对车,对人进行排序呢?

因此我们不能把这个类型写死,那我们这里把它写成 Comparable ,Comparable方法里写上这个compareTo的方法。我们就可以写一个包含compareTo方法的Comparable的接口,不管对什么排序,只要实现这个接口,我们就可以轻易做到对其进行比较。如下:

public interface Comparable<T> {//这里使用泛型更简单
	public int compareTo(T o);
}


public class Dog implements Comparable<Dog>{//比较狗的大小
	int food;
	public Dog(int food){
		this.food = food;
	}
	public int compareTo(Dog d){//吃得少的在前面
		if(this.food < d.food) return -1;
		else if(this.food > d.food) return 0;
		else return 0;
	}
	@Override
	public String toString() {
		return "Dog [food=" + food + "]";
	}
	
}

测试也是ok的:
在这里插入图片描述

4. 这才刚刚开始有点策略的意思!

我们的策略仅仅是实现了对任何东西进行排序,但是还是不够灵活,拿下面这个Cat类为例:如果我们想改便对猫的比较策略,想对猫的身高排序怎么办?如果我们直接更改compareTo方法,显然违反了OCP(开闭原则:对扩展开放,对修改关闭)。

public class Cat implements Comparable<Cat>{
	int weight,height;
	
	public Cat(int weight, int height){
		this.weight = weight;
		this.height = height;
	}
	public int compareTo(Cat c){
		if(this.weight < c.weight) return -1;
		else if(this.weight > c.weight) return 1;
		else return 0;
	}

看来这个comparable不能满足我们的需求了,我们这时需要一个comparator比较器。更改sort方法,不仅传递进来一个数组,还要加上这个比较器来表明我们的比较策略。现在我们可以有CatWeightComparator和CatHeightComparator等等多个策略,需要哪个传哪个,会非常方便!
如下:

public interface Comparator<T> {
	int compara(T o1,T o2);
}

public class CatWeightComparator implements Comparator<Cat>{

	@Override
	public int compara(Cat o1, Cat o2) {//体重轻的在前面
		if(o1.weight < o2.weight) return -1;
		else if(o1.weight > o2.weight) return 1;
		else return 0;
	}
}

public class CatHeightComparator implements Comparator<Cat>{

	@Override
	public int compara(Cat o1, Cat o2) {//个高的在前面
		if(o1.height > o2.height) return -1;
		else if(o1.height < o2.height) return 1;
		else return 0;
	}
}

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.compara(arr[j],arr[minPos])==-1 ? j:minPos;
			}
			swap(arr,i,minPos);
		}	
	}

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

}

public class Main {

	public static void main(String[] args) {
		//int [] a ={9,2,3,5,7,1,4};
		Cat[] a ={new Cat(3,3), new Cat(8,8),new Cat(6,6),new Cat(1,1)};
		//Dog[] a ={new Dog(3),new Dog(5),new Dog(1),new Dog(2)};
		Sorter<Cat> sorter = new Sorter<Cat>();
		sorter.sort(a, new CatHeightComparator());//选择高度的比较器
		System.out.println(Arrays.toString(a));
	}
}

测试一下ok的:
在这里插入图片描述

5. 类图:

在这里插入图片描述

6. 策略模式的应用场景及优缺点

应用场景: (例子是:鸭子具有飞和叫的行为,不同的鸭子飞和叫的行为不同,来源于《head first 设计模式》)

  1. 多个类只区别在 表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。(例如FlyBehavior和QuackBehavior)
  2. 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。(例如FlyBehavior和QuackBehavior的具体实现可任意变化或扩充)
  3. 对客户(Duck)隐藏具体策略(算法)的实现细节,彼此完全独立。

优点:

  1. 提供了一种替代继承的方法,而且既保持了继承的优点(代码重用)还比继承更灵活(算法独立,可以任意扩展)。
  2. 避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展。
  3. 遵守大部分GRASP原则和常用设计原则, 高内聚、低偶合。

缺点:

因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值