8.2.2 命令设计模式

728 篇文章 1 订阅
19 篇文章 0 订阅

8.2.2 命令设计模式

 

    命令模式描述了一种在应用程序中表示动作的方法。相对于前一个的模式,它常把已知的行为(比如,列表的筛选)和缺少的部分(谓词)参数化,而命令模式经常存储一些“工作单元”,能够在以后调用。我们经常看到,指定处理或操作步骤的命令集合,用户可以选择。看一下图8.2,你会认识到,一个接口看起来像一个可以用单独函数替换的很好的候选。

8-2

图8.2 Invoker 存储实现 Command 接口的类的集合。当调用时,concrete 命令使用 Receiver 对象,它通常携带并修改了一些
状态。

 

    能够很容易地用一个函数替换类型是 Command 接口。另外,它还有一个单独的方法,作为一个暗示。这些类实现了可以转换成函数的接口(比如,ConcreteCommand),无论是使用 lambda 函数的语法来构造,还是更复杂的,写作普通函数的。

    我们提到过,命令模式和策略模式之间的区别是,Invoker 处理命令列表,并在需要时执行它们,非常相似于客户贷款的例子。我们有一个测试集合,用来检查客户是否合适,但是,不是声明 Command 接口,我们的函数版本使用 C#中的 Func委托,和 F# 中的函数类型,Client -> bool。调用是 TestClient 方法,它使用测试来检查客户。

 

注意

 

    我们解释过,如图8.2所示,Receiver 类,通常表示一些在命令调用时可以改变的状态。在一个典型的面向对象的程序中,这可能是一个应用程序状态的一部分。在图形编辑器中,我们可以使用命令来表示撤消历史记录。在这种情况下,状态可以是,能够应用步骤撤消图片。

    这并不是在函数编程中你愿意使用模式的方式。不修改状态,命令通常返回一些结果(比如,在我们的客户检查示例中的布尔值)。在函数
编程中,Receiver 可以是通过 lambda 函数捕获的一个值。

 

    虽然在函数式编程中,可变状态通常应该避免,但是,有一个例子说明,它还是有用的,即使在​​ F​​# 中。我们将看到,类似于命令模式的技术可以帮助我们隐藏来自外部世界的状态,如果我们要保持程序最纯粹的函数式,这是很重要的。首先看一个在 C# 中的类似想法,然后,再研究在 F# 中,使用 lambda 函数的通常实现。

 

在 C# 中使用命令模式捕获状态

 

    正如我们所解释的,命令模式经常处理可变状态,封装一些东西,像在我们的例子中的 Receiver 类。清单 8.6 展示了一个这样的例子,给我们的财务应用程序,创建了一个非常灵活的收入栓查。其目标是可以在以后配置这个检查,而不需要修改这个检查的集合。

 

Listing 8.6 Income test using the command pattern (C#)

 

class IncomeTest {
  public int MinimalIncome { get; set; }
  public IncomeTest() {
    MinimalIncome = 30000;
  }
  public bool TestIncome(Client client) {
    return client.Income < MinimalIncome;
  }
}
var incomeTest = new IncomeTest();
Func command =
  client => incomeTest.TestIncome(client);

tests.Add(command)

 

    我们首先创建一个携带可变状态的类,对应于命令设计模式的 Receiver 组件。状态是一个建议最低收入,这个类有一个可变属性,用来修改它。接下来的方法实现了这个检查本身,并比较给定客户的收入是否比存储在检查中的当前最小值大。

    清单 8.6 的下一个部分显示了我们如何能够创建一个新的检查。首先,我们创建一个包含这个检查的 IncomeTest 类的实例,然后,再创建一个 lambda 函数,调用其 TestIncome 方法。这个函数对应于 ConcreteCommand 组件,我们将它添加到这个检查集合。我们已经用函数值替换了抽象的 Command 类型,所以,不需要实现一个接口,我们使用 lambda 函数语法,创建一个有适当类型的函数。清单 8.6 用 lambda 函数语法,显式创建了这个函数,演示了它对应的设计模式,但我们可以把它写更简洁:

IncomeTest incomeTest = new IncomeTest();
tests.Add(incomeTest.TestIncome);

 

    C#编译器会自动创建一个委托实例,封装了 TestIncome 方法,如果该方法具有正确的签名,可以把它添加到这个集合中。一旦我们把这个检查添加到集合,就可以用 MinimalIncome 属性来配置这个检查:

 

TestClient(tests, john);
incomeTest.MinimalIncome = 45000;
TestClient(tests, john);

    (在第一行的结果是肯定的,在最后一行的结果是否定的。)

 

    这是一种常见的模式,被广泛用在 OOP 的命令风格上。从函数的观点看,应该谨慎使用:代码和注释应明确地归档,什么调用会影响可变状态。在这个例子中,使用 incomeTest 对象修改状态,这解释了为什么同一行代码,在不同的时间调用,可以得到不同的结果。在下一节,我们将看到如何使用 F#,以简单的方式,实现一个类似的功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值