当你碰到几个对象是同一类对象,都有大致相同种类的行为,但是这些同类行为所造成的效果不同时,你就可以考虑策略模式来构建你的类关系了,善用策略模式会使你的代码耦合程度获得显著的降低。
首先我们先来举一个常见的例子来为讨论策略模式做一个铺垫:
class PhoneBelongToJack{ //杰克有一部手机
String ownerName = "Jack";
void ringtone(){
System.out.println("Old mobile phone ring"); //这部手机是Old手机
}
void systemName(){
System.out.println("I have no system"); //没有操作系统
}
void rang(){
ringtone();
systemName();
System.out.println("\n"+ ownerName +"! You have a phone call.");
}
}
不幸的是这部Old手机很老了,于是,杰克买了一部Nokia N97。那么,现在问题来了,如果一旦有电话打进来,手机铃声仍然是Old手机铃声,操作系统仍然是No system,如何改变呢?
我们不得不删除原来的代码重新书写,这违反了OO设计原则之一——对扩展开放,对修改关闭。
但是,也许有人会说:“我可以继承PhoneBelongToJack类,覆盖掉老的ringtone(),systemName()方法。”这个人的代码是:
class NokiaPhone extends PhoneBelongToJack{
void ringtone(){
System.out.println("Nokia mobile phone ring"); //这部手机是Nokia
}
void systemName(){
System.out.println("My system is Symbian"); // Symbian操作系统
}
}
这算是一种解决方式,而且很传统和初级。但是这种方法仍然可以解决杰克又买了一部了iphone的问题,代码是这样的:
class IPhone extends PhoneBelongToJack{
void ringtone(){
System.out.println("Iphone mobile phone ring"); //这部手机是Iphone
}
void systemName(){
System.out.println("My system is ios"); // ios操作系统
}
}
但是,事情往往没有那么容易,杰克是一名很没有品味的手机发烧友,他想要将iPhone手机的系统界面做成Symbian系统样式的,并且他居然还成功了。
一天,一个电话打进来,于是问题出现了!
杰克的iPhone的铃声是iPhone铃声,但是它的系统已经变成了“Symbian”系统,这怎么办呢?难道还要再弄一个子类出来?这显然不合适,
于是,我们看到继承并不是解决这类问题的合适方法。
正确的方法是结合“组合”、“委托”的思想和正确的OO设计原则——将变化从不变中分离出来/针对接口编程,不针对具体实现编程,利用“Strategy”模式来设计系统,重新设计的代码应该是这样的:
class PhoneBelongToJack{
private String ownerName = "Jack";
private RingTone ringTone ; //将可变部分分离出来单独聚类成行为类接口
private SystemInPhone system ; //将可变部分分离出来单独聚类成行为类接口
public PhoneBelongToJack(RingTone ringTone, SystemInPhone system) { //针对接口编程的好处是:程序可以在运行时动态的选择行为的具体实现而不用写死在类中
super();
this.ringTone = ringTone;
this.system = system;
}
void ringtone(){
ringTone.ringtone(); //将行为的执行委托给行为类对象完成,而本对象不关心行为的具体实现方式
}
void systemName(){
system.systemName(); //将行为的执行委托给行为类对象完成,而本对象不关心行为的具体实现方式
}
void rang(){
ringtone();
systemName();
System.out.println("\n"+ ownerName +"! You have a phone call.");
}
}
interface RingTone{ // 抽象出接口便于形成行为的“算法族”
void ringtone();
}
interface SystemInPhone{ // 抽象出接口便于形成行为的“算法族”
void systemName();
}
class OldRingTone implements RingTone{ // 铃声这一“算法族”包含的铃声实现之一
public void ringtone(){
System.out.println("Old mobile phone ring");
}
}
class NokiaRingTone implements RingTone{ // 铃声这一“算法族”包含的铃声实现之一
public void ringtone(){
System.out.println("Nokia mobile phone ring");
}
}
class IphoneRingTone implements RingTone{ // 铃声这一“算法族”包含的铃声实现之一
@Override
public void ringtone() {
// TODO Auto-generated method stub
System.out.println("Iphone mobile phone ring");
}
}
class OldSystem implements SystemInPhone{ // 系统这一“算法族”包含的铃声实现之一
public void systemName(){
System.out.println("I have no system");
}
}
class NokiaSystem implements SystemInPhone{ // 系统这一“算法族”包含的铃声实现之一
public void systemName(){
System.out.println("My system is Symbian");
}
}
class IphoneSystem implements SystemInPhone{ // 系统这一“算法族”包含的铃声实现之一
public void systemName(){
System.out.println("My system is ios");
}
}
策略模式将可变化的对象行为作为对象的实例属性注入进对象,从而使对象获得灵活多变的行为表现。这样一来可变行为在产生变化时,只需将该行为实现为“算法族”的某一种行为,然后注入进对象之中即可,而不必改变原有封装的类的代码。