MVP中的组合与继承

时间:2013-01-27 04:42:48

标签: design-patterns user-interface inheritance mvp composition

我正在使用MVP模式开发大规模应用程序。在开发过程中,我提出了是否应该使用组合或继承的问题。例如:我们假设我有一个名为 Foo 的表单,字段为 A B 。在应用程序的其他部分,我有一个 Bar 形式,其字段 A B ,但附加字段 C

目前,代码是使用继承方法编写的,其中 Bar 形式的视图继承自 Foo 形式。然后,演示者处理与模型略有不同的数据。这很简单,但是如果遵循“是A”的经验法则,我就会打败我,因为即使形式不同,它们也会处理常见的输入(A和B)。

然而,在这里,我一直在考虑“继承的组合”和Liskov Substitution Principle,并认为我应该使用组合而不是继承。然而,由于我使用的是MVP,它比预期的要复杂得多,因为我必须有一个表格 Foo 的演示者,字段 A B 然后是 Bar 的演示者,字段 C ,并且引用 Foo 的演示者,以便它可以注入字段一个 B

问题在于它已被证明是更多代码,因为我必须在 Foo 的演示者中添加一些getter getter和setter才能将数据传递给酒吧即可。如果我打破MVP以提供作曲,这种感觉就像。

所以我的问题是:

使用合成而不是继承,我的情况真的更好吗?为什么呢?

使用合成“打破”MVP吗?

5 个答案:

答案 0 :(得分:9)

  

使用合成而不是继承,我的情况真的更好吗?为什么呢?

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

  

使用合成“打破”MVP吗?

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

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

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

答案 1 :(得分:8)

  

当表单不同时,它们处理公共输入(A和B)。

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

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

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

答案 2 :(得分:6)

更清洁的作品就是上课:

模型:A,B,C,Foo,Bar
观看次数: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。但是,我会考虑以更多的方式继承视图,因为视图可能适合或不适合继承,具体取决于它们的行为。

答案 3 :(得分:4)

当然,当Foo没有扩展Bar时,你需要添加更多代码,因为你有额外的getter和setter。但是,最大的好处是Foo不再依赖Bar了。这看起来可能是一个非常小的好处,但想象一下,如果你使用超过50个类的inhenritance它会是什么样子...它会是地狱,没有任何逻辑,如果你不得不改变一个使用的组件会很复杂在一个由其他几个类扩展的类中。

出于维护原因,请避免使用继承。正如你所说,“一个酒吧不是一个Foo”,所以Bar不应该扩展Foo。对于我所经历的,继承永远不是一个好的解决方案,并且应该仅用于一类类(例如,当使用复合模式时)。

答案 4 :(得分:4)

让我们从基础知识开始,这是您必须了解的关于类的最重要的事情,一个子类总是超类的完整实例。因此,如果在超类中定义字段变量,则在创建子类的实例时始终会创建此字段。你可以使用super.getVariable()来获取子类中的变量来重用一个字段(类变量,字段,标志,这在OO编程中都是一样的)。但是你也可以从外部调用subclassInstance.getVariable(),你将获得相同的字段(不需要通过子类更改它)。所以你经常不需要打电话给#34; super"在你的子类中,因为你通常只想从外部获取/设置它的超类(包括抽象类!)的字段。由于你应该总是将字段变量设置为私有,我总是建议永远不要调用" super"访问任何字段变量(因为即使使用super,您也无法访问超类的私有方法/字段...实际上是Java中最大的错误之一,因为它无法提供类的完全封装树对其他类...所以你需要调用通常的protected方法,如super.getField(),这很烦人但很有必要。)

现在让我们从数据模型开始:你有modelA和modelB。您可以从超类继承它们,也可以从Object继承它们。但是如果你只从Object继承,你可以定义一个简单的(Java!)接口,并将这个接口实现到modelA和modelB中。然后你只通过界面处理这两个类(它们都是"接口对象"并且可以一般地处理)。如果你有modelC和modelB组成的modelC,你只需要使用modelC中的两个模型的实例(有时也称为" dereference"),不再需要代码。因此,您可以选择尽可能小而简单的模型(" beans")。基于组件的实现,这是数据结构的常用方法。

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

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

用你的话说:形式 Foo 的大多数代码都将在一个抽象的超类 Foobar 中,这个类将能够处理 A B 。然后你继承表格 Foo 并从中形成,而 C 可能大部分仍然是 Foobar 的子集,你在中添加了能力来处理 C ,这就是设定的差异。

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

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

你说你会在你的表格(或其演示者)中实施setter / getter吗?然后你还必须使用模型 modelA modelB (但没有modelC,因为 C 仅用于 Bar 和不是 Foo )。这些模型用作包装器,用于在 Foo Bar 之间传输数据。此数据流应由演示者控制,而不是由 Foo Bar 控制。

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

所以通常只有两种方法可以做到这一点:没有演示者/控制器或者没有它。如果没有,则需要将大量swing组件代码复制粘贴到presenter类中。所以呢?是的,当你使用摇摆组件时,你总是已经使用(M)VP模式,它不可能做到与众不同!

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

当我们开始谈论GUI组件与GUI演示时,那就是这样。可以尽可能多地创建"尽可能多的演示者组件",因为你可以使用一个简单的HTML网站,并使用方法" include(。 ..)&#34 ;.但我可以向您保证,基于组件的设计并不总能提高代码的可维护性!如果我只用一个班级做一些事情,而且我可以做到清晰可读,我宁愿选择一个班级,而不是十个班级。一个演示者=一个类,或者更具体:一个GUI框架/标签=一个类。

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

用你的话说:GUI Pres1 使用 Foo A B C 。如果GUI Pres2 具有不同的标志,则 Pres2 仅在另一个类中 。否则,您将设置标志 Pres1 和标志 Pres2 ,并在方法中检查此标志。截至if(flag="Pres1"){} else if(flag="Pres2"){}。像这样,您将获得最大的灵活性和代码可重用性。

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

"成分"总是意味着"带有构造函数的东西"。但是,有时您只是使用方法而不是组件来做某事!因此,如果有人告诉你"组件设计就是一切,他会告诉你"基于构造函数的设计就是每个人都能做到#34;。但是要创建构造函数,您需要有字段变量/标志!没有字段变量,只是为了它而创建一个新类完全是胡说八道。

注意:"组件"意思不是一种方法。很明显,你会在GUI类中使用很多方法来处理事情,所以最后你只需要调用一些方法。所以不要混合组件和方法!我总是建议一种强大的面向方法的设计,因为它只是使用较少的代码行。因此,尽可能多地定义方法......但也可以尽量减少类/组件。