什么是策略模式?策略模式就是:定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户,简单点来说就是“封装变化”.仍然以手机为列,举一个反列:有两个手机 HuaWeiMobile.java,和XiaoMiMobile.java,这两个手机都有一个1200W的后置摄像头来拍照( photo()方法),都能打电话( call() 方法),为了代码复用,不在每一个子类中都实现这两个方法,可以在这两个子类的父类(OppositeMobile.java)中实现这两个方法,就像下面这样
package strategy;
/**
* 反列
* @author Administrator
*
*/
public class OppositeMobile {
public void photo(){
System.out.println("用1200W后置摄像头拍照");
}
public void call(){
System.out.println("打电话");
}
}
但是这有一个问题,假如有一个魅族手机(MeiZuMoible.java)他的后置摄像头是2100W的,那怎么办?假如又有一个老式的诺基亚手机,他没有拍照功能,那又怎么办,我们可以看出,对于拍照这个功能,对于不同的手机,可能会有不同的实现,他是不断变化的,我们没法为了“复用”而把它放在父类中实现,难道没有办法了吗?有办法,我们可以重写 MeiZuMobile.java等子类中的 photo()方法,从而覆盖父类的实现,一个还好说,假如有100个手机呢,都有自己的后置摄像头像素,每一次都要被迫去检查photo()方法,看是否需要覆盖父类的实现。这显然太糟糕了。。这时候我们的另一种解决办法是把photo()这个方法抽取出来,把这个方法放到一个接口中 Photo.java 中,然后让有拍照功能的手机 如 魅族手机,和华为手机,去实现这个接口,至于用2100 w 还是用1200W像素 让这两个子类自己去实现,对于没有拍照功能的老式诺基亚手机,就不需要实现这个接口。但是,这又造成了新的问题,假如有很多手机都是 用同一个像素比如1200w 像素,那么每个这种像素的手机类中的 photo()方法中的代码都是一样的,这样就没法“复用”代码了,也就是说用实现接口这种方式,没法复用代码,这中情况下又该怎么办?
这时候就是策略模式大显身手的时候了。还记得刚说的策略模式的核心是什么了吗 “封装变化”,我们把 photo()这个方法抽取出来,定义到一个接口里边(AbstractPhotoStrategy.java),如下
但是这里我们不是让手机子类去实现这个接口,而是把他独立成一个接口(或者父类),让 PhotoWith1200Strategy.java ,PhotoWith2100Strategy.java
这两个子类去实现 AbstractPhotoStrategy.java 接口中的 photo()方法,
AbstractPhotoStrategy .java
/**
* 抽象策略角色
*/
public interface AbstractPhotoStrategy {
public void photo();
}
PhotoWith1200Strategy.java
package design.strategy;
/**
* 具体策略角色
*/
public class PhotoWith1200Stragegy implements AbstractPhotoStrategy {
@Override
public void photo() {
System.out.println("用1200W摄像头拍照");
}
}
PhotoWith2100Stragegy .java
package design.strategy;
/**
* 具体策略角色
*/
public class PhotoWith2100Stragegy implements AbstractPhotoStrategy {
@Override
public void photo() {
System.out.println("用2100W摄像头拍照");
}
}
这是不是一个策略?仔细想试是不是有那么点意思?,策略模式也有几个角色,如下:
(1)抽象策略角色(如上,AbstractPhotoStrategy.java,一个接口)
(2)具体策略角色(下面的,PhotoWith1200Stragegy.java, PhotoWith2100Stragegy.java)
(2)环境角色(Context.java,持有对策略角色的引用(引用的是父类或者接口类型,也就是超类型,为的是能够使用多态这个特性))
我们定义一个AbstractPhotoStrategy.java的实现类 PhotoWith1200Stragegy.java给华为,和小米手机用来拍照用的,魅族不是浪吗,给魅族定义一个AbstractPhotoStrategy.java的实现类 PhotoWith2100Stragegy.java,给魅族用来拍照的。(这里面体现了一个设计原则:针对接口编程,这里的接口不是就单单指 interface,指的是针对超类型,父类啊,接口啊,都是超类型).
我们定义一个环境角色Context.java, 让子类型 HuaWeiMobile.java , XiaoMiMobile.java ,MeiZuMobile.java 拥有环境角色的引用(成员变量)。
环境角色 Context.java
package design.strategy;
public class Context {
//环境角色持有对抽象策略角色的引用
AbstractPhotoStrategy photo;
public void photo() {
photo.photo();
}
public void call(){
System.out.println("打电话");
}
public AbstractPhotoStrategy getPhoto() {
return photo;
}
public void setPhoto(AbstractPhotoStrategy photo) {
this.photo = photo;
}
}
}
上面体现了一个设计原则 :“多用组合,少用继承”,Context.java中的成员变量 photo 就是组合,然后委托photo干活,组合使Context.java具备了他自身没有的功能(photo对象的功能)
HuaWeiMobile.java
package design.strategy;
public class HuaWeiMobile{
Context context;//可以通过构造方法参数或者是get,set方法传入
public void photo(){
context.photo();//委托,委托给 context 干活
}
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
}
XiaoMiMobile.java
package design.strategy;
public class XiaoMiMobile {
Context context;//可以通过构造方法参数或者是get,set方法传入
public void photo(){
context.photo();//委托,委托给 context 干活
}
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
}
MeiZuMobile.java
package design.strategy;
public class MeiZuMobile {
Context context;//可以通过构造方法参数或者是get,set方法传入
public void photo(){
context.photo();//委托,委托给 context 干活
}
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
}
客户端测试类ClientTest.java
package design.strategy;
import org.junit.Test;
public class ClientTest {
@Test
public void test() {
HuaWeiMobile huaWeiMobile=new HuaWeiMobile();
Context context1=new Context();
context1.setPhoto(new PhotoWith1200Stragegy());
huaWeiMobile.setContext(context1);
huaWeiMobile.photo();
System.out.println("--------------->");
XiaoMiMobile xiaoMiMobile=new XiaoMiMobile();
Context context2=new Context();
context2.setPhoto(new PhotoWith1200Stragegy());
xiaoMiMobile.setContext(context2);
xiaoMiMobile.photo();
System.out.println("---------->");
MeiZuMobile meiZuMobile=new MeiZuMobile();
Context context3=new Context();
context3.setPhoto(new PhotoWith2100Stragegy());
meiZuMobile.setContext(context3);
meiZuMobile.photo();
}
}
用1200W摄像头拍照
--------------->
用1200W摄像头拍照
---------->
用2100W摄像头拍照
当然,如果你愿意的话,也可以让HuaWeiMobile.java 华为手机用上2100W的后置摄像头。。。o(╯□╰)o