设计模式-策略设计模式

简单模拟鸭子应用

image

每一种鸭子的外观都不同,display()方法是抽象的。

现在需要鸭子可以飞(使用接口)

image

这个设计如何?是一个好的设计吗?

并非所有的子类都具有飞行和呱呱叫的行为,所以继承并不是适当的解决方式。虽然Flyable与Quackable可以解决一部分问题,但是却造成代码无法复用,这只能算是从一个恶梦跳进另一个恶梦。

设计原则:

找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

把会变化的部分取出并“封装”起来,好让其他部分不会受到影响。代码变化引起的不经意后果变少,系统变得更有弹性。

所有模式都提供了一套方法,让系统中某部分改变不会影响其他部分。

分开变化和不会变化的部分

Duck类内的fly()和quack()会随着鸭子的不同而改变。

为了要把这两个行为从Duck类中分开,我们将把它们从Duck类中取出来,建立一组新类来代表每个行为。

设计鸭子的行为

如何设计那组实现飞行和呱呱叫的行为的类呢?我们还想能够“指定”行为到鸭子的实例。比方说,我们想要产生一个新的绿头鸭实例,并指定特定“类型”的飞行行为给它。干脆顺便让鸭子的行为可以动态地改变好了。换句话说,我们应该在鸭子类中包含设定行为的方法,这样可以在“运行时”动态地“改变”绿头鸭的飞行行为。

设计原则:

针对接口编程,而不是针对实现编程。

从现在开始,鸭子的行为将被放在分开的类中,此类专门提供某行为接口的实现。这样鸭子类就不再需要知道行为的实现细节。

可以利用接口代表每个行为,比方说,FlyBehavior与QuackBehavior,而行为的每个实现都将实现其中的一个接口。

在我们的新设计中,鸭子的子类将使用接口(FlyBehavior与QuackBehavior)所表示的行为,所以实际的“实现”不会被绑死在鸭子的子类中。(换句话说,特定的具体行为编写在实现了FlyBehavior与QuackBehavior的类中)。

实现鸭子的行为

飞行类接口:
image

呱呱叫接口:
image

这样的设计,可以让飞行和呱呱叫的动作被其他的对象复用,因为这些行为已经和鸭子类无关了。

而我们可以新增一些行为,不会影响到既有的行为类,也不会影响“使用”到飞行行为的鸭子类。

整合鸭子的行为

关键在于,鸭子现在会将飞行和呱呱叫的动作“委托”(delegate)别人处理,而不是使用定义在Duck类(或子类)内的呱呱叫和飞行方法。

image

  1. 行为变量被声明为行为“接口”类型。
  2. 实例变量在运行时持有特定行为的引用。
  3. performQuack()和performFly()方法去掉fly()和quack()方法。

现在,我们来实现performQuack()方法

public class Duck{
    QuackBehavior quackBehavior;
    
    public void performQuack(){
        quackBehavior.quack();
    }
}

想进行呱呱叫的动作,Duck对象只要叫quackBehavior对象去呱呱叫就可以了。我们不在乎quackBehavior接口的对象到底是什么,我们只关心该对象知道如何进行呱呱叫就足够了。

public class MallardDuck extends Duck{
    public MallardDuck(){
        quackBehavior = new Quack();
        flyBehavior = new FlyWithWings();
    }
}

不对具体实现编程,但构造器中制造一个具体的Quack实现类,此时初始化实例变量的做法不够弹性。

测试Duck的代码

public abstract class Duck{
    //为行为接口类型声明两个引用变量,所有鸭子都继承
    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;
    
    public abstract void display();
    
    //委托给行为类
    public void performFly(){
        flyBehavior.fly();
    }
    
    public void performQuack(){
        quackBehavior.quack();
    }
}

动态设定行为

假设我们在鸭子子类中通过“设定方法”来设定鸭子的行为,而不是在i鸭子的构造器内实例化。

在Duck类中添加两个新方法:

public void setFlyBehavior(FlyBehavior fb){
    flyBehavior = fb;
}

public void setQuackBehavior(QuackBehavior qb){
    quackBehavior = qb;
}

从此以后,我们可以随时调用这两个方法改变鸭子的行为。

封装行为的大局观

重新设计后的类结构,所期望的一切都有:鸭子继承Duck,飞行行为实现FlyBehavior接口,呱呱叫行为实现QuackBehavior接口。

不再把鸭子的行为说成是“一组行为”,我们开始把行为想象成是“一族算法”。算法代表鸭子能做的事(不同的叫法和飞行法),这样的做法也能很容易地用于用一群类计算不同省地销售税金。

image

“有一个”可能比“是一个”更好

“有一个”关系相当有趣:每一个鸭子都有一个FlyBehavior和一个QuackBehavior,好将飞行和呱呱叫委托给它们代为处理。

将两个类结合起来使用,这就是组合,这种做法和“继承”不同的地方在于,鸭子的行为不是继承来的,而是和适当的行为对象“组合”来的。

设计原则:

多用组合,少用继承。

使用组合建立系统具有很大的弹性,不仅可将算法族封装成类,更可以“在运行时动态地改变行为”,只要组合地行为对象符合正确地接口标准即可。

策略模式

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值