我们可以使用策略模式和依赖注入完全取代继承吗?


10

例如:

var duckBehaviors = new Duckbehavior();
duckBehaviors.quackBehavior = new Quack();
duckBehaviors.flyBehavior = new FlyWithWings();
Duck mallardDuck = new Duck(DuckTypes.MallardDuck, duckBehaviors)

由于Duck类包含所有行为(抽象),因此似乎不需要创建新类MallardDuck(扩展了Duck)。

参考:Head First设计模式,第1章。


Duckbehavior.quackBehavior您的代码中“字段”和“其他字段” 的类型是什么?
max630

您的示例中没有依赖项注入。
戴维·康拉德

11
当您是中级开发人员时,继承是很棒的,因为围绕它重构技巧和设计模式已有很长的历史。但是,我曾与大多数经验丰富的开发人员进行过交流,他们尽可能地希望基于组合的浅层继承层次结构。继承会导致不必要的紧密耦合,并使更改变得困难。
Mark Rogers


5
@DavidConrad:OP不在班级内部进行更新。DI / IoC是关于注入依赖关系的,不是关于容器的,也不是关于从不使用“ new”的。这是一个完全正交的问题。此外,您始终必须在某处更改代码- 无论是合成根目录还是某些配置文件。这里的控件在Duck类型中是反向的,因为控制依赖项创建的不是Duck ctor,而是某些外部上下文;这是一个清晰的玩具示例,外部环境仅由调用代码表示就可以了。
FilipMilovanović19年

Answers:


21

当然可以,但是我们称其为组成和授权。策略模式和依赖注入在结构上看似相似,但目的不同。

策略模式允许在同一界面下对行为进行运行时修改。我可以告诉野鸭飞翔,看着它飞翔。然后将其换成喷气飞行员鸭,并与达美航空公司一起观看它的飞行。在程序运行时执行此操作是一种策略模式。

依赖注入是一种避免硬编码依赖的技术,因此依赖可以独立更改,而无需在更改客户端时对其进行修改。客户只是表达他们的需求,而不知道如何满足他们。因此,如何满足它们是在其他地方(通常在主要方面)决定的。您不需要两只鸭子就可以使用此技术。只是使用鸭子而又不知道或不在意哪只鸭子的东西。不会造鸭或去寻找它的东西,但是非常乐意使用您将其交给鸭的任何东西。

如果我有一个具体的鸭子课程,我可以让它实现它的飞行行为。我什至可以根据状态变量将行为从双翼飞行切换为带三角洲飞行。这个变量可以是一个布尔值,一个int值,也可以是一个FlyBehavior具有fly执行任何飞行样式的方法的,而无需我用if对其进行测试。现在,我可以更改飞行样式,而无需更改鸭子的类型。现在,绿头鸭可以成为飞行员。这就是组成和授权。鸭子由FlyBehavior组成,可以将飞行请求委托给它。您可以通过这种方式一次替换所有的鸭子行为,或者为每种行为或两者之间的任何组合保留某种东西。

这给您除继承之外所有与继承相同的权力。继承使您可以表达在Duck子类型中要覆盖的Duck方法。组合和委派要求Duck从一开始就明确委派给子类型。这要灵活得多,但是需要更多的键盘输入,Duck必须知道它的发生。

但是,许多人认为必须从一开始就明确设计继承。而且,如果还没有这样做,则应将类标记为封闭/最终禁止继承。如果您采用这种观点,那么继承与组合和委派相比确实没有任何优势。因为那样的话,您要么必须从一开始就设计可扩展性,要么以后愿意将其分解。

拆掉东西实际上是一个受欢迎的选择。请注意,在某些情况下这是个问题。如果您已经独立部署了不打算在下一个发行版中进行更新的库或代码模块,那么最终可能会遇到无法了解您最新知识的类版本。

虽然以后愿意拆掉东西可以使您摆脱设计的束缚,但是对于能够使用鸭子的东西进行设计而不必知道鸭子在使用时实际会做什么的事情,有一些非常强大的功能。那不知道是有力的东西。它使您可以暂时停止思考鸭子,而思考其余的代码。

“我们可以”和“我们应该”是不同的问题。优先考虑继承而不是不使用继承。在某些情况下,继承最有意义。我将向您展示我最喜欢的示例

public class LoginFailure : System.ApplicationException {}

继承使您可以仅在一行中创建具有更具体的描述性名称的异常。

尝试通过合成来做到这一点,您会一团糟。同样,也没有继承溜溜球问题的风险,因为这里没有可重用和鼓励继承链的数据或方法。这一切都是一个好名字。永远不要低估好名声的价值。


1
永远不要低估好名声的价值 ”。皇帝的新衣时间:LoginException不是一个好名字。这是经典的“蓝精灵命名”,如果我带走的话Exception,我所拥有的只是Login它,它什么也没告诉我。
David Arno

令人遗憾的是,我们坚持将Exception每个异常类都放在末尾的荒谬惯例,但是请不要将“被其卡住”与“良好”混为一谈。
David Arno

@DavidArno如果您可以提出一个更好的名字,我会很高兴。在这里,我们的好处是不会陷入现有代码库的约定中。如果您有能力用指尖改变世界,那么您会如何命名呢?
candied_orange

我想他们的名字,StackOverflowOutOfMemoryNullReferenceAccessLoginFailure等,基本上采取“异常”断名。并且,如果需要,请对其进行修复,以使其描述出了问题所在。
David Arno

@DavidArno按您的命令。
candied_orange

7

您几乎可以将任何方法替换为任何其他方法,并且仍然可以生产可用的软件。然而,有些解决方案比其他解决方案更适合特定问题。

这取决于很多事情,哪一个是更可取的。申请中的现有技术,团队经验,预期的未来发展,个人喜好以及新手要努力解决的困难,仅举几例。

随着您在与他人的创作上变得越来越经验丰富和更加挣扎,您可能会更加强调最后的沉思。

继承仍然是有效且强大的建模工具,不一定总是最灵活的,但它为可能希望清楚地映射到问题域的新手提供了强有力的指导。


-1

继承并不像以前那样重要。它仍然很重要,将其删除将是一个严重的错误。

一个极端的例子是Objective-C,其中所有内容都是NSObject的子类(编译器实际上不允许您声明不具有基类的类,因此,未内置于编译器中的所有内容都必须是某个对象的子类。内置到编译器中)。NSObject内置了许多有用的东西,您不必错过。

另一个有趣的示例是UIView,这是用于iOS开发的UIKit中的基本“视图”类。这是一个一方面有点像抽象类的类,它声明应该填充子类的功能,但它本身也很有用。它具有UIKit提供的子类,这些子类通常按原样使用。由开发人员在视图中安装子视图组成。并且经常有开发人员定义的子类,它们经常使用组合。没有严格的单一规则,甚至没有规则,您可以使用最适合自己要求的东西。


您的“极端示例”大致是大多数现代OO语言的工作方式:Python子类type,Java中的每个非原始类都继承Object,JavaScript中的所有内容都有以结尾的原型Object,等等
。– Delioth

-1

[我会冒昧回答名义问题。]

我们可以使用策略模式和依赖注入完全取代继承吗?

是的...除了策略模式本身使用继承。策略模式适用于接口继承。用组合+策略替换继承会将继承移到另一个位置。尽管如此,这种替换通常还是值得做的,因为它可以使层次结构分开


2
策略模式在接口继承上起作用 ”。记住,策略模式是一种设计模式。不是实施模式。因此可以使用接口来实现,但是也可以使用例如函数的哈希/字典来实现。
David Arno

3
接口继承根本不是继承,它只是一种契约或分类器。您也可以将委托用于策略模式(我更喜欢使用委托)。
Deepak Mishra

另外,dude:dude: ...适用于接口继承,适用于通用术语“ interface”的称赞。我认为班级设计不佳或不称职是提出这个问题的基本原因。解释为什么/如何读书;细节决定成败。我从事的继承设计令人着迷,而深层次的继承却是地狱。我已经看到interface通过继承固定的疯狂设计。这里隐藏的问题是实现变形依赖行为的不一致。P,S。继承与接口不是互斥的,
–radarbob

@DavidArno评论:如果附加到OP问题,则此评论会很好。从技术上讲,OP问题的说法有误,但我们仍然可以理解。问题不是这个特定的答案。
Radarbob

@DeepakMishra评论:。“接口”是类的所有公共成员。不幸的是,“接口”的含义已超载。我们必须小心使用编程语言的关键字来区分一般含义interface
Radarbob,

-2

不可以。如果野鸭鸭子需要的参数实例化与其他类型的鸭子不同,那将是一场噩梦。您还应该能够实例化鸭子吗?更像是野鸭,Mandarin鸭或其他种类的鸭。

另外,我可能希望有关类型的一些逻辑,以便更好地使用类型层次结构。

现在,如果代码重用对于某些功能而言成为问题,我们可以通过在构造函数中传递函数来编写代码。

同样,这实际上取决于您的用例。如果您只有一只鸭子和一只野鸭,那么类层次结构是一个简单得多的解决方案。

但是,在下面的示例中,您想使用策略模式:如果您有客户类别,并且希望传入一个计费策略(界面),该策略可以是欢乐时光计费策略或常规计费策略,则可以通过它在构造函数中。这样,您不必创建两个不同类别的客户,也不需要层次结构。您只有一个客户类别。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.