12

这是一个奇怪的 oop 问题。我想创建一组对象(在设计时已知),每个对象都有与之关联的某些功能。我可以通过提供可以包含“委托”的对象属性来做到这一点:

public class StateTransition {
    Func<bool> Condition { get; set; }
    Action ActionToTake { get; set; }
    Func<bool> VerifyActionWorked { get; set; }
}

StateTransition foo = new StateTransition {
    Condition = () => {//...}
    // etc
};

或者,我可以使用一个抽象类并为我要创建的每个对象实现它:

public abstract class StateTransition {
    public abstract bool Condition();
    public abstract void ActionToTake();
    public abstract bool VerifyActionWorked();
}

class Foo : StateTransition {
    public override bool Condition() {//...}
    // etc
}

Foo f = new Foo();

我意识到这两种方法的实际后果(在设计时创建与运行时创建)是完全不同的。

如何选择适合我的应用的方法?

4

7 回答 7

5

第一种方法看起来比原始委托更适合事件,但是......无论如何。

它们之间的关键因素是:谁控制着发生的事情

如果调用者可以在那里合法地做任何事情,那么事件方法就可以了。毕竟,系统不会强迫您将 a 子类化Button,以添加单击它时发生的事情(尽管您可以这样做)。

如果“可能发生的事情”得到很好的控制,并且您不希望每个调用者都做不同的事情,那么子类方法更合适。这也避免了每个调用者必须告诉它要做什么的需要,而“要做的事情”实际上可能是非常少的选项。基类型方法提供了控制子类的能力,例如通过仅internal在基类上具有构造函数(这样只有在同一程序集中或通过 标记的程序集中的类型[InternalsVisibleTo(...)]才能对其进行子类化)。

您还可以通过以下方式组合两者(覆盖与事件):

public class StateTransition {
    public event Func<bool> Condition;
    protected virtual bool OnCondition() {
        var handler = Condition;
        return handler == null ? false : handler();
    }
    public event Action ActionToTake;
    protected virtual void OnActionToTake() {
        var handler = ActionToTake;
        if(handler != null) handler();
    }
    public event Func<bool> VerifyActionWorked;
    protected virtual bool OnVerifyActionWorked() {
        var handler = VerifyActionWorked;
        return handler == null ? true : handler();
    }
    // TODO: think about default return values
}

委托/事件方法要考虑的另一件事是:如果委托是,你会怎么做null?如果您需要全部 3,那么在构造函数中要求全部 3 将是一个好主意。

于 2013-01-10T08:05:57.670 回答
2

如果您:

  • 想要动态创建对象,即根据某些条件为每个方法选择实现。
  • 想要在对象的生命周期内更改实现。

对于其他情况,我会推荐面向对象的方法。

于 2013-01-10T08:10:02.267 回答
1

如何选择适合我的应用的方法?

您的应用程序是否需要定义具有其他不同属性或其他不同方法的新转换对象?然后制作新的子类,覆盖方法(“多态性”)会更好。

或者。

您的应用程序是否需要定义只改变方法行为的转换对象?那么方法委托或方法事件会更好。

概括

当您的应用程序需要为不同的子类添加不同的功能(例如属性或方法)时,覆盖方法(“多态性”)会更好,而不仅仅是更改方法的实现。

于 2015-09-08T23:10:35.850 回答
0

如果您说您的对象在设计时已知,那么听起来您不需要动态更改对象的行为(在运行时)。所以我认为你没有理由使用“委托”方法。

于 2013-01-10T08:06:38.093 回答
0
  • 解决方案 1 有更多的活动部分,这允许更细粒度的关注点分离。您可以让一个对象决定Condition给定的StateTransition应该是什么,另一个对象定义ActionToTake,等等。或者您可以让一个对象决定所有这些,但基于不同的标准。大多数时候不是最有用的方法 IMO - 特别是考虑到轻微的额外复杂性成本。

  • 在解决方案 2 中,每个StateTransition导数都是一个有凝聚力的整体,它检查条件的方式不能与它执行操作或验证它的方式分开。

这两种解决方案都可用于实现控制反转——换句话说,它们都允许您说StateTransition' 的直接消费者将无法控制StateTransition将使用哪种风格,而是将决定委托给外部对象。

于 2013-01-10T11:00:08.097 回答
0

前提是这更像是一种观点,而不是坚如磐石的答案……

如果您只有一个委托或抽象/虚拟方法,我认为这两者或多或少是等价的。毕竟,您可以将委托视为一种方便的快捷方式,以避免为仅一种方法创建实现接口。

在这种情况下,如果您有三种方法,那么基类方法将是最实用的。

为了使这两件事完全等效,您可以使用具有空虚方法的基非抽象类,这样如果派生类不覆盖它,则与具有空委托属性相同。

于 2013-01-10T08:02:55.527 回答
0

如何选择适合我的应用的方法?

具有委托的方法使代码更具功能性,因为它从继承多态等经典Object Oriented Programming技术转变为传递函数和使用闭包等技术。Functional Programming

我倾向于在任何地方使用代表方法,因为

  1. 我更喜欢组合而不是继承

  2. 我发现委托的方法需要更少的代码来编写

例如,可以使用标准初始化机制StateTransition在 5 行代码中从委托闭包中创建一个具体实例:.NET

dim PizzaTransition as new StateTransition with {
    .Condition = function() Pizza.Baked,
    .ActionToTake = sub() Chef.Move(Pizza, Plate),
    .VerifyActionWorked = function() Plate.Contains(Pizza)
}
  1. 我发现围绕一个类构建Fluent API很容易,其中一组附加方法实现为扩展方法或在类内部。

例如,如果方法、、和CreateWhen添加到类中:DoVerifyStateTransition

public class StateTransition
    public property Condition as func(of boolean)
    public property ActionToTake as Action
    public property VerifyActionWorked as func(of boolean)

    public shared function Create() as StateTransition
        return new StateTransition
    end function

    public function When(Condition as func(of boolean)) as StateTransition
        me.Condition = Condition
        return me
    end function

    public function Do(Action as Action) as StateTransition
        me.ActionToTake = Action
        return me
    end function

    public function Verify(Verify as func(of boolean)) as StateTransition
        me.VerifyActionWorked = Check
        return me
    end function
end class

然后方法链也可用于创建具体StateTransition实例:

dim PizzaTransition = StateTransition.Create.
    When(function() Pizza.Baked).
    Do(sub() Chef.Move(Pizza, Plate).
    Verify(function() Plate.Contains(Pizza))
于 2015-09-08T22:11:36.747 回答