MVP中的组合与继承

Sed*_*nus 20 mvp user-interface inheritance design-patterns composition

我正在使用MVP模式开发大规模应用程序.在开发过程中,我提出了是否应该使用组合或继承的问题.例如:让我们假设我有一个名为形式美孚与场一个.在本申请的另一部分我有一个表格具有相同字段但附加字段Ç.

目前,代码是使用继承方法编写的,其中表单Bar从表单Foo继承.然后,演示者处理与模型略有不同的数据.这很简单,但是如果遵循"是A"的经验法则,是否打败我,因为即使表格不同,它们也处理公共输入(A和B).

然而,在这里我一直在考虑"继承的构成"和Liskov替换原则,并认为我应该使用组合而不是继承.然而,因为我使用的是MVP,所以它比预期的要复杂得多,因为我必须有一个表格Foo的演示者,字段AB,然后是一个带有字段C的Bar的演示者,以及对Foo的演示者的引用,以便它可以将字段AB注入其中.

问题是它已被证明是更多的代码,因为我必须在Foo的演示者中添加一些排序getter和setter,以便能够将数据传递给Bar.如果我打破MVP以提供作曲,这感觉就像某种程度.

所以我的问题是:

对于我的情况,使用组合而不是继承是否真的更好?为什么?

使用组合"打破"MVP?

joe*_*son 10

对于我的情况,使用组合而不是继承是否真的更好?为什么?

是.因为组合在更大的应用程序中更可靠,更安全,更易于维护,更易于发现,更易于记录,更易于理解.恕我直言.:)

使用组合"打破"MVP?

是.它打破了你现在正在做的那种简单的MVP.使用Composition可以选择如何结合代码,这对于较大的应用程序非常有用.它确实使用了更多的代码,因为你必须具体了解你的耦合方式.

对于一个简单的应用程序来说,增长是非常合理的,并且成为从简单的MVP继承到更复杂的组合的过渡的良好候选者.这是一个解耦步骤,可以以新的方式重新耦合.

这类似于有多少简单的Web应用程序正在转换为前/后API驱动的应用程序.这实质上是前端用户视图与后端存储模型的分离.


man*_*rid 8

当表格不同时,他们处理共同的输入(A和B).

这意味着Foo presenter在概念上与Bar presenter不同,只是碰巧共享一些常见的输入,所以它们不应该通过继承来关联.提取处理公共输入到实用程序类的代码,并在Foo presenter和Bar presenter中重用它.

如果Foo的概念发生变化,它不会影响Bar(反之亦然:如果Bar的概念在不改变Foo概念的情况下也无法改变,那么它就是 "A"关系,并且确实可以使用继承)

如果有疑问,总是喜欢作文


Dan*_*rod 6

一个更清洁的组合将是有类:

型号:A,B,C,美孚,酒吧
观看次数:AView,BView,CView中,FooView,BarView
主持人们:APresentor,BPresentor,CPresentor,FooPresentor,BarPresentor

在FooView包含AView和BView的情况下,BarView包含AView,BView和CView,并且呈现器具有类似的组成.

这个组合使A,B和C(连同它们的视图和呈现器)模块化,因此您可以根据需要混合和匹配,并且复合类(Foo和Bar)处理集成.

这可以与继承一起使用:如果Bar是Foo的特定情况,那么Bar应该继承自Foor,而BarPresentor可以继承自FooPresentor.但是,我会考虑以更多的方式继承视图,因为视图可能适合继承,也可能不适合继承,具体取决于它们的行为.


Mar*_*cus 5

让我们从基础开始,这是您必须了解的关于类的最重要的事情,即子类始终也是超类的完整实例。因此,如果您在超类中定义字段变量,则在创建子类的实例时始终会创建该字段。您可以使用 super.getVariable() 在子类中获取该变量以重用一个字段(类变量、字段、标志,这在 OO 编程中都是一样的)。但是您也可以从外部调用 subclassInstance.getVariable() 并且您将获得相同的字段(无需通过子类更改它)。因此,您通常根本不需要在子类中调用“super”,因为您通常只想从外部获取/设置它的超类(包括抽象类!)的字段。由于您应该始终将字段变量设置为私有,因此我始终建议永远不要调用“super”来访问任何字段变量(因为即使使用 super,您也可以'protected super.getField() 之类的方法,这很烦人但很有必要)。

现在让我们从数据模型开始:您有模型 A 和模型 B。您可以从超类或仅从 Object 继承它们。但是如果你只继承自Object,你可以定义一个简单的(Java!)接口,并将这个接口实现到modelA和modelB中。然后您只需通过接口处理这两个类(它们都是“接口对象”并且可以通用处理)。如果您的模型C 由模型A 和模型B 组成,您只需在模型C 中使用两个模型的实例(有时也称为“取消引用”),不需要更多代码。所以你可以选择尽可能小而简单的模型(“beans”)。基于组件的实现,这是数据结构的常用方法。

如果您有 GUI 或表单,它看起来会有所不同。您可能有很多共同的代码,并且您不希望将这些代码分成几十个不同的类,然后将这些组件放在一个控制器/演示器类中。因此,您可以定义一个抽象类,其中包含所有共享字段/标志以及访问和更改它们的几种方法。然后,您从 formA 和 formB 调用这些常用方法并重用代码。

集合论对你有什么说明吗?你有两个圆,A和B,A和B的交集。抽象类是交集,子类formA和formB是集合差。学习编写正确的程序......就是理解集合论。;-)

用你的话来说:形式Foo 的大部分代码将在一个抽象超类Foobar 中,这个类将能够处理AB。然后你继承了表单Foo和表单Bar,虽然C可能大部分仍然是Foobar的子集,但你在Bar 中添加了 hability来处理C,这就是集合差异。

最终,Bar在任何时候都不会是Foo,它们都只是Foobar。你有一些新的共享字段/标志吗?没问题,您将他们的代码迁移到Foobar,并且您可以在两个子类中使用它!

但是如果有一天你需要第三个组件FooToo,它与Foo略有不同呢?没问题,让FooBarFoo一个抽象类扩展FooBar的,然后创建美孚FooToo作为子类。最终的结果将是一个类树,其中根是(通常)抽象类,而叶子是真正的类,这种结构提供了代码的最大化重用(并且不改变类名,所以你不需要改变已经使用类Foo 的任何其他代码)。

你说你会在你的表单(或其演示者)中实现 setter/getter 吗?然后您还必须使用模型modelAmodelB(但没有 modelC 因为C仅用于Bar而不是Foo)。这些模型用作包装器以在FooBar之间传输数据。并且这个数据流应该由演示者控制,而不是由FooBar 控制

所以你的问题最终是这样的:主持人是什么?实际上,presenter 是运行 GUI 组件和数据模型的代码。“框架”一方面使用 GUI 组件,另一方面使用数据模型的 getter/setter。它是两层之间的中间件,即GUI层和数据层,甚至是不同GUI组件和不同数据模型之间的中间件。

所以通常只有两种方法可以做到:不使用演示者/控制器或使用它。否则,您需要将大量的 Swing 组件代码复制粘贴到演示器类中。所以呢?是的,当您使用摆动组件时,您将始终已经使用 (M)VP 模式,这是不可能的!

所以说,要创建框架,您需要使用组件设计,因为您希望为使用您的框架的程序员提供最大的灵活性。但是一个高效的系统并不等同于一个框架,这是许多框架程序员认为的错误。因此,如果框架程序员告诉您“基于组件的实现就是一切”,那么他可能是错的。仅仅因为他正在为他的框架编写组件,这并不意味着您需要为演示者做同样的事情!

所以这就是我们开始讨论 GUI 组件与 GUI 表示的时候。创建“尽可能多的演示器组件”是可能的,就像您可以使用一个简单的 HTML 站点并使用“include(...)”方法从中创建数十个 PHP 站点一样。但我可以向你保证,基于组件的设计并不总是能提高代码的可维护性!如果我只用一节课就可以做一些事情,而且我可以做到清晰易读,我宁愿选择一节课,而不是十节课。一位演示者 = 一类,或者更具体地说:一个 GUI 框架/选项卡 = 一类。

再说一次,如果您有 2 个相似的框架/选项卡,但它们不一样,该怎么办?将共享代码迁移到一个抽象类中,并创建两个子类,对吗?不,您首先需要考虑那些 GUI 的份额。他们有共享旗帜吗?因此,将标志移动到抽象超类中。但他们只是表现不同吗?好吧,您只需在同一个类中实现两个不同的方法,并在需要时调用它们。这是最重要的。

用您的话来说:GUI Pres1 完全使用FooBarABC。并且 GUI Pres2在具有不同标志的情况下属于另一个类。否则你将设置一个标志Pres1和一个标志Pres2并在你的方法中检查这个标志。由if(flag="Pres1"){} else if(flag="Pres2"){}. 这样,您将获得最大的灵活性和代码可重用性。

不要将 Java 类视为不灵活、不可重用、不可更改的东西。如果您是一名优秀的程序员,那么一旦需要,您将直观地更改程序的结构。您不需要考虑人工概念,您只需要了解面向对象的编程模式。

“组件”总是意味着“带有构造函数的东西”。但是,有时您只会使用方法而不是组件来做某事!因此,如果有人告诉您“组件设计就是一切”,他会告诉您“基于构造函数的设计就是一切”。但是要创建构造函数,您需要有字段变量/标志!没有字段变量,仅仅为了它而创建一个新类完全是无稽之谈。

注意:“组件”的意思不是方法。很明显,您将在 GUI 类中使用很多方法来非常轻松地处理事情,所以最后您只需要调用一些方法。所以不要混合组件和方法!我总是建议一个强大的面向方法的设计,因为这完全是为了使用更少的代码行。所以定义尽可能多的方法......但也尽可能少的类/组件。