简体   繁体   English

委托方法或抽象类

[英]Delegate Method or Abstract Class

Here is my program: 这是我的计划:

class Program
{
    //DESIGN 1
    abstract class AFoo
    {
        public string Bar { get; set; }
        public abstract string SayHi();
    }

    class LoudFoo : AFoo
    {
        public override string SayHi()
        {
            return this.Bar.ToUpper();
        }
    }

    class QuietFoo : AFoo
    {
        public override string SayHi() { return this.Bar.ToLower(); }
    }

    //DESIGN 2
    class Foo{
        public string Bar { get; set; }
        public Func<Foo, string> SayHi { get; set; }
    }

    static void Main(string[] args)
    {
        //USING DESIGN 1
        var quietFoo2 = new QuietFoo{ Bar = "Mariane"};

        var loudFoo2 = new LoudFoo{ Bar = "Ginger"};

        Console.WriteLine(quietFoo2.SayHi());
        Console.WriteLine(loudFoo2.SayHi());

        //USING DESIGN 2
        var quietFoo = new Foo
        {
            Bar = "Felix",
            SayHi = (f) => { return f.Bar.ToLower(); }
        };
        var loudFoo = new Foo
        {
            Bar = "Oscar",
            SayHi = (f) => { return f.Bar.ToUpper(); }
        };
        Console.WriteLine(quietFoo.SayHi(quietFoo));
        Console.WriteLine(loudFoo.SayHi(loudFoo));

    }
}

I can accomplish the "same thing"-- actually not exactly the same thing, but similar things going two different routes. 我可以完成“同样的事情” - 实际上不完全相同的事情,但类似的事情走两条不同的路线。

Design 1) I can create an abstract class which forces the implementor of that class how to SayHi() 设计1)我可以创建一个抽象类,强制该类的实现者如何SayHi()

--or-- - 要么 -

Design 2) I could create a class defines a SayHi property which is a function. 设计2)我可以创建一个类来定义一个SayHi属性,它是一个函数。 (I'm calling it a delegate-- but I'm not sure that's the correct term for it here) (我称之为代表 - 但我不确定这是否是正确的术语)

Design 1 bothers me because it could lead to a profliferation of classes 设计1困扰我,因为它可以导致课程的丰富

yet.... 然而....

Design 2 bothers me because it feels redundant when I have to have Foo actually SayHi(). 设计2困扰我,因为当我必须让Foo实际上是SayHi()时感觉多余。

felix.SayHi(felix)

My question is whether it is better to use Design 1 or Design 2-- or perhaps neither of them. 我的问题是使用Design 1还是Design 2--或者两者都不是更好。 When I say better I am saying which is more practical in terms of being able to maintain my program. 当我说得更好时,我说的是能够维持我的程序更实用。 I ran into this when I created different classes which are going to be used to download files from different cloud API's (Google Drive, Box.com, DropBox)-- at first I created separate classes, but then I went the other route. 当我创建不同的类时,我遇到了这个问题,这些类将用于从不同的云API(Google Drive,Box.com,DropBox)下载文件 - 起初我创建了单独的类,但后来我走了另一条路。

When it comes to these types of design choices, I find it helps to think about the objects in terms of the problem domain you're trying to model. 当谈到这些类型的设计选择时,我发现根据您尝试建模的问题域来考虑对象会有所帮助。 You've shown LoudFoo and QuietFoo as differing in a single behavior, but this is a deliberately simplified example. 您已经将LoudFoo和QuietFoo显示为单个行为不同,但这是一个故意简化的示例。 In a real system, you may have compelling reasons to consider two objects as being conceptually distinct. 在实际系统中,您可能有令人信服的理由将两个对象视为概念上不同的。

In the former version, SayHi is an instrinsic part of the class behavior, which is appropriate if the nature of that behavior interacts with its internal state in some way. 在前一版本中,SayHi是类行为的一个内在部分,如果该行为的性质以某种方式与其内部状态相互作用,这是恰当的。 Perhaps the implementation of SayHi depends on properties of the object that are specific to that derived class type. 也许SayHi的实现依赖于特定于派生类类型的对象的属性。

In the latter version, SayingHi is a more like a tool that can be handed out to various instances. 在后一版本中,SayingHi更像是一个可以分发给各种实例的工具。 This is appropriate when there are no other reasons to distinguish between different types of Foo instances. 当没有其他理由来区分不同类型的Foo实例时,这是合适的。

Stream is a good example of the former pattern, where the various methods it provides are intrinsic to the nature of the streaming operation. Stream是前一种模式的一个很好的例子,它提供的各种方法对于流操作的本质是固有的。 The various derived classes will make use of different states to implement their methods. 各种派生类将使用不同的状态来实现其方法。

Comparer is a good example of the latter pattern, where lots of different object types want to operate using a notion of comparison. Comparer是后一种模式的一个很好的例子,其中许多不同的对象类型想要使用比较的概念来操作。 The classes that use this functionality don't need to have anything else in common other than wanting to consume this particular type of behavior. 除了想要使用此特定类型的行为之外,使用此功能的类不需要具有任何其他共同点。


Regarding your concrete application that prompted this question, what about the multi-class approach felt awkward? 关于提出这个问题的具体应用,那么多类方法又有什么尴尬? If there was redundancy creeping in, it likely indicates that the responsibilities could be factored in a different way that better modeled the problem. 如果存在冗余蔓延,则可能表明可以以更好地模拟问题的不同方式考虑责任。 It's hard to say more without knowing additional details about the specific problem, but likely a good approach would be a combination of the two you proposed, with some single class responsible for the sequencing of the operation and a separate heirarchy (or set of interface implementations) implementing operations specific to each service. 如果不知道有关特定问题的其他详细信息,很难说更多,但很可能一个好的方法是你提出的两个方法的组合,一些单个类负责操作的排序和一个单独的层次结构(或一组接口实现) )实施特定于每项服务的操作。 Essentially the interface (or base class) groups all of the various delegates you would pass in separately. 本质上,接口(或基类)将分别传递的所有各种代理组合在一起。 This is akin to how a StreamReader takes a Stream and augments it with additional behaviors that operate on the Stream. 这类似于StreamReader如何获取Stream并使用在Stream上运行的其他行为来增强它。

In Design 1 your behavior is implemented inside the class, but in Design 2 you're asking your caller to define the behavior. 在设计1中,您的行为是在类中实现的,但在设计2中,您要求调用者定义行为。

I'm leaning towards Design 1 because it keeps the behavior implementation black-boxed inside the class. 我倾向于设计1,因为它使行为实现在类中保持黑盒子。 Design 2 could have your implementation changed whenever somebody instantiates a new object. 只要有人实例化一个新对象,设计2就可以改变你的实现。 I also don't like how the implementation is the responsibility of the caller. 我也不喜欢实现是调用者的责任。

If how you implement SayHi changes you only have one place to change it in Design 1, but you could potentially have several places all over your code to change it if you used Design 2. 如果您如何实现SayHi更改,您只需在设计1中更改一个地方,但如果您使用设计2,则可能在您的代码中有多个位置可以更改它。

As a rule of thumb: less code == more maintainable. 根据经验:较少的代码==更易于维护。

In the specific case you have, you are also having a decoupled design - the logic of how to SayHi is separate from the class that says it, giving you the option to compose the behavior. 在具体的情况下,你也有一个脱钩的设计 - 如何将SayHi与说出它的类分开的逻辑,让你可以选择组合行为。 Low coupling is also a hallmark of code that is generally to maintain. 低耦合也是通常要维护的代码的标志。

My preference would be with the second design. 我倾向于第二种设计。

The first design is more standard, and the logic is consistent (means that any other class using the LoudFoo (or QuietFoo ) will have same result everywhere. However, it is reuseable, but only in its inherited path. Means that child class from LoudFoo (say DerivedLoudFoo cannot use SayHi logic defined in QuietFoo ). 第一个设计更标准,逻辑是一致的(意味着任何其他使用LoudFoo (或QuietFoo )的类在任何地方都会有相同的结果。但是,它是可重用的,但仅限于其继承的路径。表示来自LoudFoo子类(比如DerivedLoudFoo不能使用QuietFoo定义的SayHi逻辑)。

That may sound simple, but can be troublesome later on. 这可能听起来很简单,但以后可能会很麻烦。 You can read my answer at here for real-life case. 你可以在这里阅读我的答案,了解真实案例。

The second is more extendable, but the drawbacks are it can have different behavior. 第二个是更可扩展的,但缺点是它可能有不同的行为。 Don't use this for core business process (such as insert/update/delete), as it will hard to debug or modify. 不要将其用于核心业务流程(例如插入/更新/删除),因为它很难调试或修改。 However, this is best to use in Framework level for some methods such as OnAfterInsert , OnAfterSubmit . 但是,对于某些方法(如OnAfterInsertOnAfterSubmit ,最好在Framework级别中使用。

Assuming this is more than a totally made-up example and could actually be translated into real code (which I have a hard time figuring out what it could be), I find option 2 terrible. 假设这不仅仅是一个完全构成的例子,而且实际上可以被翻译成真正的代码(我很难弄清楚它可能是什么),我发现选项2很糟糕。

  • You can pretty much assign anything to SayHi , including a lambda that is not related to a Bar in any way, which doesn't seem to be your original intent. 你几乎可以给SayHi分配任何东西,包括一个与Bar无关的lambda,这似乎不是你原来的意图。

  • You're basically trying to stick a nicely crafted functional peg into a good old object-oriented hole. 你基本上是想把一个精心制作的功能钉挂在一个好的旧的面向对象的洞里。 Using a lambda you separated data ( Bar ) from behavior that operates on it, which is valid functional practice, but then by making Foo.SayHi a property, you're back to an OO style trying to encapsulate the two of them back into the same class. 使用lambda将数据( Bar )与在其上运行的行为分开,这是有效的功能实践,但是通过使Foo.SayHi成为属性,您将回到OO样式,尝试将其中的两个封装回到同班。 Seems a bit contrived. 似乎有点做作。

Design 2 bothers me because it feels redundant when I have to have Foo actually SayHi(). 设计2困扰我,因为当我必须让Foo实际上是SayHi()时感觉多余。

If Foo class is redefined to 如果将Foo类重新定义为

class Foo
    public property Bar as string
    public property SayHi as func(of string)
end class

Then closure can be used to create and invoke SayHi function without passing Foo as a function parameter: 然后可以使用闭包来创建和调用SayHi函数,而不将Foo作为函数参数传递:

dim Bar = "Felix"

dim Felix as new Foo with {
    .Bar = Bar,
    .SayHi = function() Bar.toLower
}

dim FelixSays = Felix.SayHi()

I'm leaning towards Design 1 because it keeps the behavior implementation black-boxed inside the class. 我倾向于设计1,因为它使行为实现在类中保持黑盒子。

Design 2 is always ready to black-box behavior implementation, for example inside a factory method: 设计2总是准备好进行黑盒行为实现,例如在工厂方法中:

function CreateQuietFoo(Bar as string) as Foo
    return new Foo with {
        .Bar = Bar,
        .SayHi = function() Bar.toLower
    }
end function

dim Felix = CreateQuietFoo("Felix")
dim Oscar = CreateQuietFoo("Oscar")

This way the caller doesn't have to provide SayHi method to create a quiet Foo instance, he simply uses CreateQuietFoo factory method. 这样调用者不必提供SayHi方法来创建一个安静的Foo实例,他只是使用CreateQuietFoo工厂方法。

My question is whether it is better to use Design 1 or Design 2-- or perhaps neither of them. 我的问题是使用Design 1还是Design 2--或者两者都不是更好。

Use Design 2 if you prefer Composition over Inheritance . 如果您更喜欢Composition over Inheritance,请使用Design 2。 It makes code more flexible. 它使代码更灵活。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM