Strategy Pattern
本文总结《Design Pattern in Java 》 Chap23 Strategy. 以为用户推荐一款烟火制品选择采取不同推荐策略为例。先讲解如果不采用策略模式会怎样,然后将这些代码重构为策略模式。
1 、如果不使用策略模式 ,Customer::getRecommended() 中封装了复杂的策略选择和策略执行流程。 见图一
关键代码(choice & execution of various strategies) :
public Firework getRecommended() {
//1 、如果有促销的烟火制品,从 config/strategy.dat 中取出并返回
try {
Properties p = new Properties();
p.load(ClassLoader.getSystemResourceAsStream ( "config/strategy.dat" ));
String promotedName = p.getProperty( "promote" );
if (promotedName != null ) {
Firework f = Firework.lookup (promotedName);
if (f != null )
return f;
}
} catch (Exception ignored) {
// If resource missing or it failed to load,
// fall through to the next approach.
ignored.printStackTrace();
}
//2 、注册用户使用 Rel8 引擎
if (isRegistered()) {
return (Firework) Rel8.advise ( this );
}
//3 、未注册用户,若是大客户,使用 LikeMyStuff 引擎
if (isBigSpender()) {
return (Firework) LikeMyStuff.suggest ( this );
}
//4 、未注册用户,若是小客户,随意推荐
return Firework.getRandom ();
}
由以上代码可知,主要有这样几种推荐策略:
A. 从config/strategy.dat 中读取指定推荐的烟火制品
B.Rel8 引擎
C.LikeMyStuff 引擎
D. 随机推荐
2 、使用策略模式 ,对以上代码进行重构
现在,Customer 中的关键代码是:
public static void main(String[] args) {
Firework recommendation = new Customer2().getRecommended();
System. out .println(recommendation.toString());
}
public Firework getRecommended() {
Firework recommend = getAdvisor().recommend( this );
return recommend;
}
private Advisor getAdvisor() {
if ( advisor == null ) {
if ( promotionAdvisor .hasItem())
advisor = promotionAdvisor ;
else if (isRegistered())
advisor = groupAdvisor ;
else if (isBigSpender())
advisor = itemAdvisor ;
else
advisor = randomAdvisor ;
}
return advisor ;
}
可见,我们将策略选择流程封装在getAdvisor() 中了,getAdvisor() 返回一个策略接口Advisor 的子类实例;调用这个被返回的子类实例的recommend() 方法(重写Advisor 中的recommend() 方法),可以得到推荐的Firework 实例。
*****************************************************************
*****************************************************************
另外,对于以前写得很松散的以下两种推荐方式,分别写Advisor 子类对其进行封装。封装前如下:
public Firework getRecommended() {
//1 、如果有促销的烟火制品,从 config/strategy.dat 中取出并返回
try {
Properties p = new Properties();
p.load(ClassLoader.getSystemResourceAsStream ( "config/strategy.dat" ));
String promotedName = p.getProperty( "promote" );
if (promotedName != null ) {
Firework f = Firework.lookup (promotedName);
if (f != null )
return f;
}
} catch (Exception ignored) {
// If resource missing or it failed to load,
// fall through to the next approach.
ignored.printStackTrace();
}
//2 、注册用户使用 Rel8 引擎
//3 、未注册用户,若是大客户,使用 LikeMyStuff 引擎
//4 、未注册用户,若是小客户,随意推荐
return Firework.getRandom ();
}
封装后如下:
public class PromotionAdvisor implements Advisor {
private Firework promoted ;
public PromotionAdvisor() {
try {
Properties p = new Properties();
p.load(ClassLoader.
getSystemResourceAsStream ( "config/strategy.dat" ));
String promotedFireworkName = p.getProperty( "promote" );
if (promotedFireworkName != null )
promoted = Firework.lookup (promotedFireworkName);
} catch (Exception ignored) {
promoted = null ;
}
}
public boolean hasItem() {
return promoted != null ;
}
public Firework recommend(Customer c) {
return promoted ;
}
}
public class RandomAdvisor implements Advisor {
public Firework recommend(Customer c) {
return Firework.getRandom ();
}
}
3 、总结
如何记忆策略模式呢:
(1) 这里有一个概念(策略选择==> customer2.getAdvisor() )、两个实体(策略调用者==> customer2 ; 策略==> advisor接口实例 ):
(2) 具体细节 是: “策略调用者”调用其“策略选择”customer2.getAdvisor()选择一个具体的advisor接口实例(如groupAdvisor实例)赋予其成员对象advisor;advisor接口提供统一接口方法advisor.recommend()供实际执行某一个“策略”。