关于Head First设计模式中的策略模式的困惑

大家好,最近在jdon学习了一段时间,买了本关于模式的书来学习,<Head first 设计模式>中的第一个模式策略模式(Strategy),其中有三个设计原则:
1、找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起
2、针对接口编程,而不是针对实现编程
3、多用组合,少用继承。

策略模式定义了算法族,分别封装起来,让它们之间可以互想替换,此模式让算法的变化独立于使用算法的客户。

它举了一个SimuDuck的列子,使用的是策略模式,如下图
http://www.jdon.com/jivejdon/imageShow.jsp?id=0

其实我有点困惑,根据原则1 2 3为什么不把行为再拿出来,抽像一个更高级别的接口呢,而是把飞行行为和呱呱叫行为单独出来呢?假如鸭子要求还有其它行为,那不是又要修改Duck类吗?我觉得因该抽像出一个Behavoir接口(只是一个像征意义的类型,没有实体),飞行行为和呱呱叫行为因该实现行为接口,这样有新的行为时,就不需修改Duck。请banq指点一下,这样可以吗


[该贴被freesea于2009-03-23 20:38修改过]

Head first

我觉得 每个模式都有其 适应的 场合
像 策略模式 就 适应于 解耦 对象中的行为算法(可以是一个行为算法中的一小块,当然也可以是全部), 而不是 封装 对象的所有行为, 所以, duck类 要添加 行为的 话, 与 策略模式 就没有 本质上的 联系, 因为, 如果, 你添加的 行为 是 FlyWithQuack, 我 想你是 不 需要 添加 一个 新接口的。(当然, 修改类 是 必需的)

再有 就是, behavior的 整体封装,
我觉得, 还是 要 看场合, 要是 , 对象的 行为算法 之间 有 很 必然的 联系, 无法 分割, 那 整体封装,是 最好的

但 像 例子中 那样,分割开后是 最好的,因为 , 倘若封装在一起, 两个算法其中 只要有一个 发生 变化的话,
你就 需要 重新 写 一个 实现类, 也就是 说 你 违反的 原则1


BluE11
我其实没看明白你所说的意思,我可能表过不太清楚,用代码来表达吧

Behvior.java
interface Behavior{
}
----------------------------------
FlyBehavior.java
----------------------------------
interface FlyBehavior extends Behavior{
}
----------------------------------
QuackBehavior.java
----------------------------------
interface QuackBehavior extends Behavior{
}
----------------------------------
Duck.java
----------------------------------
public abstract class Duck{
Behavior bh;//关键在这里,不管有什么样的行为,
//这个duck类都不用修改了
}
而不是像原来那样
public abstract class Duck{
FlyBehavior fb;//飞的行为
QuackBehavior qb;//叫的行为
......//如果再加其它行为,那么这里不是要修改么
}

如果像您这样的提取一个新的接口,而又希望用户只和此接口交互以防止Duck类的变化,那么这个新的接口也就会有被污染的可能,如果其子接口的只有一个行为语义的话,新接口还可以应对(如public vodi action()),多个的话就不能应对了,新接口承担了不相关的行为集,职责不单一;如果只有一个行为语义的话,新接口也就没有必要了。

我想 我 是 没 理解 你的 意思, 因为 我 发现 你的所表达的意思 是 矛盾的


interface FlyBehavior extends Behavior{
}
interface QuackBehavior extends Behavior{
}

这是 两种 行为 的 接口



public abstract class Duck{
Behavior bh;//关键在这里,不管有什么样的行为,
//这个duck类都不用修改了
}

这里 只有 一个 行为 接口,
如果 你 只想要 Duck 有 一种 行为, 那么 我 同意 你的 说法, 但 这 就和 策略模式 不相关了
但 如果 Duck 要 添加 一种 行为(同时 有 2 种待行为), 你 说说看 怎么 不 修改 Duck,
除了 装饰模式外, 你 可能 只 能 将 两种行为 都 放到 Behavior 的 一个 实现里, 这样 是 不科学 的 。。。。

不知道 , 这次 我 理解 对了 没有


是我上面的代码矛盾了,呵呵

其实我想表达的意思就是你后面所说的,谢谢了,我明白了

如果想为Duck添加多种行为为不修改Duck的话也是可以的,见以下:
【UML】见下图

【相关代码】
[Duck.java]
public abstract class Duck {
public List<Behavior> behaviors;
public Duck(){}
public void swim(){
System.out.println("所有鸭子都会游泳");
}
public abstract void display();
public void performAction(){
Iterator<Behavior> it=behaviors.iterator();
while(it.hasNext()){
Behavior b=it.next();
b.action();
}
}
}
=================================================================
[Behavior.java]
public interface Behavior {
public void action();
}
=================================================================
[FlyWithWings.java]
public class FlyWithWings implements FlyBehavior {
public void action() {
System.out.println("使用翅膀飞翔");
}
}
=================================================================
[BehaviorTest.java]
public class BehaviorTest {
public static void main(String[] args) {
//定义行为
List<Behavior> behaviors=new ArrayList<Behavior>();
behaviors.add(new FlyNoWay());
//behaviors.add(new FlyWithWings());
behaviors.add(new Squeak());
//为对象添加行为
Duck duck1=new RubberDuck(behaviors);
duck1.swim();
duck1.display();
duck1.performAction();
}
}
【结果显示】
所有鸭子都会游泳
橡皮鸭
不会飞
叽叽

当想添加新的行为,只需要实现FlyBehavior或QuackBehavior接口,然后在对象中添加行为就可以了,[对象]与[行为]分割开来,不互相影响。
[注]不过这样设计对于行为类的方法命令有污染(被统一命名了,混淆了不同行为类的行为用意),还是不建议使用。
[该贴被seagar于2009-04-01 16:39修改过]


原则1已经说明了,行为种类是不是不可变的要考虑进去,设计适当粒度的模型更有效,尽管这些完全可以更加抽象。