【问题标题】:Using the Command Pattern with Parameters使用带参数的命令模式
【发布时间】:2017-10-07 21:47:17
【问题描述】:

我有一个这样的 ReloadableWeapon 类:

public class ReloadableWeapon {
    //keeping the design really simple, took out weapon logic.
    private int numberofbullets;

    public ReloadableWeapon(int numberofbullets){
        this.numberofbullets = numberofbullets;
    }

    public void attack(){
        numberofbullets--;
    }

    public void reload(int reloadBullets){
        this.numberofbullets += reloadBullets;
    }
}

使用以下interface

public interface Command {
    void execute();
}

并像这样使用它:

public class ReloadWeaponCommand implements Command {

    private int reloadBullets;
    private ReloadableWeapon weapon;

    //Is is okay to specify the number of bullets?
    public ReloadWeaponCommand(ReloadableWeapon weapon, int bullets){
        this.weapon = weapon;
        this.reloadBullets = bullets;
    }

    @Override
    public void execute() {
        weapon.reload(reloadBullets);
    }
}

客户:

    ReloadableWeapon chargeGun = new ReloadableWeapon(10);
    Command reload = new ReloadWeaponCommand(chargeGun,10);
    ReloadWeaponController controlReload = new ReloadWeaponController(reload);
    controlReload.executeCommand();

我想知道,对于命令pattern,对于我所看到的示例,除了命令所作用的对象之外,没有其他parameters

This example, alters the execute method to allow for a parameter

Another example, more close to what I have here, with parameters in the constructor

在命令pattern 中包含参数是不好的做法/代码味道,在这种情况下constructor 带有项目符号数?

【问题讨论】:

  • 命令模式只是说有一个对象封装了执行命令所需的所有信息,例如参数。就像在 Wikipedia 页面上一样:“此信息包括方法名称、拥有该方法的对象和方法参数的值。”如果您不能包含任何数据,那将是一个非常无用的模式:/
  • @DaveNewton - 基本上,wiki 是说可以在构造函数中传递子弹数量以重新加载命令?如果我理解正确的话。

标签: java oop design-patterns


【解决方案1】:

我认为在 execute 中添加参数不会是糟糕的设计或违反命令模式。

这完全取决于你想如何使用命令对象:单例或原型范围。

如果你使用 Prototype 作用域,你可以在 Constructor 方法中传递命令参数。每个命令实例都有自己的参数。

如果你使用单例范围(共享/重用实例),你可以在执行方法中传递命令参数。对于这种情况,命令的单例应该是线程安全的。这个解决方案也是 IoC/DI 框架的朋友。

【讨论】:

  • GoF 命令模式不允许将参数传递给 execute 方法。这违背了模式的目的,因为调用者需要如何调用命令的逻辑,并且命令是不可互换的。 Java 的典型命令接口是RunnableCallable。这些 API 不带参数并非偶然。
  • @jaco0646:好吧。 GoF 可以有品种。重要的是该模式必须正常工作/受信任并正确应用。
【解决方案2】:

这种模式的真正目的是允许定义动作,并在以后执行一次或多次。

您提供的代码是这种模式的一个很好的例子:您定义了“重新加载”操作,它为gun 充电bullets=10 弹药量。

现在,如果您决定修改此代码以添加bullets 作为参数,那么您将完全失去此模式的目的,因为您每次都必须定义弹药数量。

恕我直言,您可以保持代码不变。您将必须定义多个 ReloadWeaponCommand 实例,具有不同的 bullets 值。那么你可能不得不使用另一种模式(例如Strategy)在命令之间切换。

【讨论】:

  • 如果我知道每个子弹是10个并且永远不会改变,那没关系,正确,但是如果我想给出不同容量的子弹,那么策略模式更好,对吗?
  • 我不同意。这是带有参数的命令的完美示例。
  • @DaveNewton - 就我的理解而言,wiki 确认可以在构造函数中包含参数(项目符号数)以便稍后调用,对吗?我的代码好吗?
  • @DaveNewton,你可能误读了这个答案。它与 OP 一致。然后它提到了该模式的一个潜在后果,即改变 Command 实例的状态。我想我们都同意,这是一个带参数的命令的完美示例。
  • @DaveNewton,将参数添加到命令的execute() 方法会失去模式的目的。这就是 OP 和这个答案的重点。
【解决方案3】:

假设你手头有 95 颗子弹,你已经发出了 9 条 10 颗子弹的命令和 1 条 5 颗子弹的命令。并且您已经将这些命令提交给 Invoker,现在 Invoker 不必担心还剩下多少子弹。他只会执行命令。另一方面,如果调用者必须在运行时提供项目符号数,则可能是提供的项目符号数不可用。

我的意思是,Invoker 不必担心执行命令所需的任何额外信息。正如 wiki 中提到的“一个对象用于封装执行操作或稍后触发事件所需的所有信息”

【讨论】:

  • 策略模式会更好吗?
  • 我不认为策略模式最适合这个用例。您提供的示例非常适合命令模式。
  • 所以,你的意思是,在构造函数中有子弹的数量就可以了,对吗?
  • 策略模式有什么问题?我试过了,代码更短更容易理解
  • @Abstractioniseverything。因为 GoF 是指导方针,而不是命令。意见(这就是 GoF)、模式、范式等都会发生变化。
【解决方案4】:

使用带参数的命令模式

考虑相关的“扩展模式”以保持自上而下的控制范式“控制反转”。
这种模式,即命令模式,通常与 CompositeIteratorVisitor 设计模式一起使用。
命令是“First Class Objects”。因此,保护​​其封装的完整性至关重要。此外,将控制从上到下颠倒到自下而上,违反了面向对象设计的基本原则,尽管我看到人们一直在建议......

复合模式允许您将命令存储在迭代数据结构中。

在继续之前,当您的代码仍然易于管理时,请查看这些模式。

在此线程中提出了一些合理的观点。 @Loc 最接近 IMO,但是,如果您考虑上面提到的模式,那么,无论您的项目范围如何(看起来您打算制作游戏,这不是小任务),您都将能够控制低级依赖。正如@Loc 指出的那样,对于任何特定的实现,就它们所使用的数据而言,使用“依赖注入”的低类对象应该保持“在黑暗中”;这是(应该)为顶层层次结构保留的。 '编程接口,而不是实现'。

您似乎对此有一个概念。让我指出我在这一点上看到的可能错误的地方。实际上,已经有一对夫妇专注于沙粒,即“子弹”,你不是在这样的琐事服务于任何目的的地步,除了作为一个警告信号,你现在即将失去对更高级别的依赖关系的控制。

无论您是否能够看到它,颗粒状部分都可以而且应该在更高的层次上处理。我会提出几个建议。 @Loc 已经提到过松散限定的最佳实践“构造函数注入”,最好查看这个术语“依赖注入”。

以子弹为例因为它们已经出现在您的范围内。复合模式旨在处理许多不同但相关的第一类对象,例如命令。在迭代器模式和访问者模式之间,您可以将所有预实例化的命令以及未来的实例化存储在动态数据结构中,例如链接列表或二叉搜索树。在这一点上忘记策略
模式,一些可能的场景是一回事,但一开始就写自适应接口是没有意义的。

另外一件事,我没有看到任何迹象表明你正在从一个类中生成射弹,我的意思是子弹。但是,即使只是跟踪武器配置的问题,并且容量(int items)(我只是猜测这是弹丸数量必要变化的原因)使用堆栈结构或取决于实际情况是;一个循环队列。如果您实际上是从工厂生成射弹,或者如果您决定将来这样做,那么您已经准备好利用对象池;事实证明,这是出于这种明确的考虑。

并不是说这里的任何人都这样做过,但我发现有人建议错误处理或忽视任何已建立(尤其是 GoF)设计模式背后的特定动机是可以的,这尤其愚蠢。如果您发现自己不得不修改 GoF 设计模式,那么您使用的是错误的模式。只是说说而已

附:如果您绝对必须,为什么不使用模板解决方案,而不是更改故意特定的界面设计;

【讨论】:

    猜你喜欢
    • 2011-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-17
    相关资源
    最近更新 更多