概念:
策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。(维基百科)
理解概念:
概念中说策略模式是某个行为在不同场景下有不同的实现算法。我们知道有一条设计原则是这样的:分离变化的部分和不变的部分。在这个概念中我们可以知道。不变的地方,是这个行为,变化的,是执行这个行为所需要的算法,或者说是策略。
应用场景:
这个模式的应用场景应该是这样的。我们需要对若干个都可以实现我们所要的功能中选择一个最合适的。举一个简单的例子:java君、Pascal君、Scala君三个人要去餐厅吃饭。可是呢,他们仨个人的口味不一样,java君想吃面向对象大餐,Pascal君想吃面向过程大餐,而Scala君想吃函数式大餐,这时候假设你是餐厅服务员,你给每个人点餐的时候,就要这样写了
if(pascal君) return 面向过程大餐;else
if(java君)return 面向对象大餐;else
if(scala君)return 函数式大餐;
这就太糟了。因为如果又来了一个spring先生想要吃aop大餐,就要去更改源码了。这不符合我们“对修改关闭,对扩展开放”的原则。所以这种设计是不行的。
解决之道:
我们说要分离开变化的和不变的东西。变化的就是对不同的人要上不同的菜。我们把这一点从代码中分离出来。单独封装起来。利用多态,给每一类人一个点某种菜的能力。我们可以让所有人实现一个Order接口,这个接口中定义了点菜这个方法,让每个人都去实现这个方法,然后在点菜的时候直接去调用每个人的order方法。这样如果在来新的人的话,我们不用改动原有的代码,只需要让新人实现自己的order方法就可以。这样就符合设计原则了。下面是实现代码。
/*
* Created by 王镜鑫 on 2016/10/7
*/
interface Order//点餐接口
{
public void order();//点餐方法
}
class JAVA implements Order//java君实现点餐接口
{
public void order()
{
System.out.println("I want a Object Oriented meal");
}
}
class Pascal implements Order//Pascal君实现点餐接口
{
public void order()
{
System.out.println("I want a Procedure Oriented meal");
}
}
class Scala implements Order//Scala君实现点餐接口
{
public void order()
{
System.out.println("I want a functional meal");
}
}
public class Restaurant//餐厅
{
private Order order = null;
public void setOrder(Order order)//设置点餐的人。
{
this.order = order;
}
public void order()//让点餐人点餐
{
order.order();
}
public static void main(String[] args)
{
Restaurant restaurant = new Restaurant();//实例化一个餐厅
restaurant.setOrder(new JAVA());//将java君设置进去
restaurant.order();//让java君点餐
restaurant.setOrder(new Pascal());//将Pascal君设置进去
restaurant.order();//让Pascal君点餐
restaurant.setOrder(new Scala());//将Scala君设置进去
restaurant.order();//让Scala君点餐
}
}
输出如下:
I want a Object Oriented meal I want a Procedure Oriented meal I want a functional meal
可见,java君、Pascal君、Scala君都吃到了自己想吃的大餐。并且及时再来一个汇编先生,我们也不怕,也不用修改原来的代码。只需要在添加一个汇编先生类让其实现点餐接口就可以了。
提炼总结:
我们通过一个例子。来描述了所谓的策略模式的应用场景,和如何应用策略模式解决问题,使代码更易扩展,降低耦合。下面我们总结一下策略模式。
通过上面的例子,我们得知,策略模式包含以下角色:
- Context:上下文类,起到承上启下的封装作用,屏蔽上层模块对策略的直接访问。
- Strategy:抽象策略类。通常是一个抽象(接口或者抽象类)定义每个策略中的必须有的方法和属性。
- ConcreteStrategy:具体策略类,是具体策略的实现。
三者的关系如下图:
上下文中有一个策略属性,还有一个设置策略的方法,一个工作方法。工作方法中实现的就是策略的工作方法。 具体的策略A和具体的策略B去实现了策略接口,实现了work方法。这样在调用上下文的work方法,就是调用了实际设置的具体策略的work方法,从而实现了将执行策略与具体的策略解耦,有新的策略的时候,只需要再重新声明一个策略类就可以。
策略模式是一种非常简单的设计模式,策略模式是对不同的算法的封装,把算法的责任和算法本身分离开来,将算法委派给不同的对象管理,用一句话来说,策略模式就是:实现一族算法,并将每一个算法都封装起来,使其相互交换。
策略模式中,使用哪一种策略是由用户来决定的,这提高了系统的灵活性,但是这在一定程度上,也增加了客户端的使用难度。因为客户端需要了解不同的算法之间的区别,选择合适的算法。策略模式还体现了这样一个设计原则:针对接口编程,而不是针对实现编程。我们在策略模式中,用一个接口代表行为,根据多态,上下文在工作时执行的行为,是这个接口的具体实现。思考我们常用的jdk官方的排序方法。Colloctions.sort(List<T> list)和他的重载方法 sort(List<T> list, Comparator<? super T> c)他们的实现都是直接调用了List的sort方法,区别是一个传入的比较器是null,一个是传入了具体的比较器,比较器是null的那个方法,list中的元素T也是实现了Comparablel接口的。这就是一个典型的策略模式的应用。再排序的时候,由于不同的需要排序的类的排序规则不一样,也就是策略不一样,所以我们把策略封装到客户部分,也就是具体需要排序的类中,也就是让具体需要排序的类实现Comparable接口,然后在传入到排序的算法中的时候,排序的算法在比较两个元素的先后顺序的时候直接调用其compareTo方法,根据多态,他调用的其实是具体传入的compareTo方法。也就是封装到子类中的具体的比较策略,这样就能实现了具体的排序。这是策略模式在排序中的应用。
由于知识水平有限,文章中有疏漏或者错误的地方,欢迎大家留言指正。