问题
I'm using MVP pattern to develop a large scale application. While working in the development I have come up with the question whether if composition or inheritance should be used. For example: Let's assume that I have a form called Foo with fields A and B. In other part of the application I have a form Bar that has the same fields A and B but an additional field C.
Currently, the code is written with the inheritance approach in where the view the form Bar inherits from form Foo. The presenters then handle the data a little different with the model. This works out pretty simply but beats me whether if follows the rule of thumb of "is A" since even when the forms are different they handle common inputs (A and B).
However, here I've been thinking of "composition over inheritance" and the Liskov Substitution Principle and come to think that I should be using composition instead of inheritance. However since I'm using MVP it have been more complicated than expected because I'll have to have a presenter for form Foo with fields A and B then a presenter for Bar with with field C and a reference to the presenter of Foo so that it can inject the fields A and B into it.
The problem is that it it has proven to be more code since I will have to add some sort getters and setters in the presenter of Foo for it to be able to pass the data to Bar. This feels somehow like if I am breaking MVP in order to provide composition.
So my questions are:
Is it really better for my case to use composition over inheritance? Why?
Does using composition "break" MVP?
回答1:
Is it really better for my case to use composition over inheritance? Why?
Yes. Because composition is more reliable, more secure, more maintainable, more discoverable, more documentable, and more comprehensible in larger apps. IMHO. :)
Does using composition "break" MVP?
Yes. It breaks the kind of simple MVP you're doing now. Composition lets you choose how to couple your code, and this is very good for larger apps. It does use more code because you have to become specific about how you're coupling.
It is very reasonable for a simple app to grow, and to become a good candidate for a transition from simple MVP inheritance to more sophisticated composition. This is a decoupling step that enables recoupling in new ways.
This is similar to how many simple web apps are transitioning to become front/back API-driven apps. This is essentially a decoupling of the front-end user views from the back-end storage models.
回答2:
when the forms are different they handle common inputs (A and B).
this means Foo presenter is conceptually different from Bar presenter and just happen to share some common input so they should not be related by inheritance. extract the code that handles common inputs into utility class and reuse that in both Foo presenter and Bar presenter.
in case concept of Foo changes, it will not affect Bar (and the other way around: if concept of Bar cannot change without changing concept of Foo too, then it is "is A" relationship and inheritance could be used indeed)
when in doubt, always prefer composition
回答3:
A cleaner composition would be to have classes:
Models: A, B, C, Foo, Bar
Views: AView, BView, CView, FooView, BarView
Presentors: APresentor, BPresentor, CPresentor, FooPresentor, BarPresentor
Where FooView contains an AView and a BView, BarView contains an AView, BView and a CView and the presentors have similar composition.
This composition makes A, B and C (together with their views and presentors) modular, so that you can mix and match as you like and the composite classes (Foo and Bar) deal with the integration.
This could be used together with inheritance: If Bar is a specific case of Foo then Bar should inherit from Foor, and BarPresentor may inherit from FooPresentor. I would, however, consider inheritance of the views in a more per-case fashion, as the views may or may not be suitable for inheritance, depending on their behavior.
回答4:
Of course, when Foo does not extends Bar, you need to add more code, because you have extra getters and setters. But the very big benefit is that Foo does not depends on Bar anymore. This may look a very minor benefit, but imagine what it would look like if you use inhenritance with more than 50 classes... it would be hell, without any logic, and it would be very complicated if you had to change a component used in a class that is extended by several other classes.
For maintenace reason, avoid using inheritance. As you said, "a Bar is not a Foo", so Bar should not extend Foo. For what I have experienced, inheritance is never a good solution, and should be used only for a family of classes (when using a composite pattern for example).
回答5:
Let's begin with the basics, the most important thing you must know about classes, that a subclass is always also a full instance of the superclass. So if you define a field variable in a superclass, this field is always created if you create an instance of the subclass. You may use super.getVariable() to get that variable in the subclass to reuse a field (class variable, field, flag, that's all the same thing in OO programming). But you can also just call subclassInstance.getVariable() from the outside and you'll get the same field (without any need to change it by a subclass). So you often don't need to call "super" in your subclass at all, since you usually just want to get/set a field of it's superclass (including abstract classes!) from the outside. Since you should always set field variables as private, I always suggest to never call "super" to access any field variables (because even with super you can't access the private methods/field of your superclass...actually one of the greatest errors in Java, since it can't provide full encapsulation of a class tree towards other classes...so you need to call usually protected
methods like super.getField(), which is annoying but necessairy).
Now let's start with data models: You have modelA and modelB. You could inherit them from a superclass, or just from Object. But if you only inherit from Object, you may define a simple (Java!) interface, and implement this interface into modelA and modelB. Then you handle those two classes just via the interface (they are both "interface objects" and can be handled generically). If you have modelC consisting of modelA and modelB, you just use an instance (sometimes also called "dereference") of both models inside modelC, no more code is needed. So you may choose this models as small and simple as possible ("beans"). A component based implementation, that's the usual way for data structures.
If you have GUI's or forms, it looks different. You probably got much code in common, and you don't want to split this code up in dozens of different classes and then pull the components together in a controller/presenter class. So you could define an abstract class, which contains all shared fields/flags and several methods to access and change them. And then, you call these common methods from the formA and the formB and reuse the code.
Does the set theory say anything to you? You've two circles, A and B, and the intersection of A and B. The abstract class is the intersection, the subclasses formA and formB are the set differences. Learning to code correct programms...is understanding the set theory. ;-)
To say it in your words: most of the code of the form Foo will be in an abstract superclass Foobar, this class will be able to handle A and B. Then you inherit form Foo and form Bar from it, and while C will probably mostly remain a subset of Foobar, you add the hability in Bar to handle C, that's the set difference.
In the end, Bar won't be Foo at anytime, both of them will be only Foobar. You got some new shared fields/flags? No problem, you migrate their code into Foobar, and you can use it in both subclasses!
But what if one day you need a third component FooToo which is slightly different than Foo? No problem, make FooBarFoo an abstract class extends FooBar and then you create Foo and FooToo as subclasses. The end result will be a class tree where the roots are (usually) abstract classes, and the leafs are real classes, this structure provides maximised reuse of code (and doesn't change the class names, so you don't need to change any other code alreay using the class Foo).
You said that you will implement setter/getter into your forms (or its presenter)? Then you must also use the models modelA and modelB (but no modelC since C is only used in Bar and not Foo). These models are used as wrapper to transport data between Foo and Bar. And this data flow should be controlled by the presenter, not by Foo or Bar.
So your question ends up to be this: What is a presenter all about? In fact, the presenter is the code that runs the GUI components and the data models. It's the "frame" that makes use of the GUI components in one hand, and uses the getter/setter of the data models in the other hand. It's the middleware between the two layers, the GUI layer and the data layer, and even between different GUI components and different data model.
So there are usually only two ways how to do it: Without presenter/controller or with it. Without, you would need to copy-paste a lot of swing component code into your presenter class. So what? Yeah right, when you use a swing component, you'll always already use the (M)VP pattern, it's not possible to do it different!
So said, to create a framework, you need to use a component design, since you want to provide maximised flexibility to a programmer working with your framework. But a productive system isn't the same as a framework, that's the error that many framework programmers think. So if a framework programmer tells you "a component based implementation is everything", well, he may be wrong. Just because he is programming components for his framework, this doesn't mean that you need to do the same for your presenter!
So that's when we start talking about GUI components vs. GUI presentation. It's possible to create "as many presenter components as possible", as you could take a simple HTML site and make dozens of PHP sites out of it by using the method "include(...)". But I can assure you, component based design doesn't always improve the maintainability of the code! If I can do something with just one class, and I can do it clear and readable, I'll rather go with one class, not with ten. One presenter = one class, or to be more specific: One GUI frame/tab = one class.
And again, if you've got 2 similar frames/tabs, but they're not the same, what to do? Migrate the shared code into an abstract class, and create the 2 subclasses, right? No, you first need to think about what those GUI's share. Have they shared flags? So move the flag into an abstract superclass. But do they just behave different? Well, you just implement two different methods inside the same class, and you call them whenever you need them. That's most important.
To say it in your words: The GUI Pres1 uses Foo, Bar, A, B and C altogether. And the GUI Pres2 is only in another class if it has different flags. Otherwise you will set a flag Pres1 and a flag Pres2 and check this flag inside your methods. By if(flag="Pres1"){} else if(flag="Pres2"){}
. Like this, you'll get a maximum of flexibility and code reusability.
Don't look at Java classes as something unflexible, unreusable, unchangeable. As soon it's needed, you are going to change the structure of your programm intuitively, if you're a good programmer. You don't need to think of artificial concepts, you just need to understand object oriented programming patterns.
"Component" always means "something with a constructor". But, sometimes you'll just use a method instead of a component to do something! So if somebody tells you "component design is everything", he tells you "constructor based design is everyhing". But to create constructors, you need to have field variables/flags! Without field variables, it's fully nonsense to create a new class, just for the sake of it.
Attention: "Component" means not a method. It's clear that you'll use a lot of methods inside your GUI class(es) handling things very easily, so in the end you just call a few methods. So don't mix components and methods! I always suggest a strong method-oriented design, because it's all about using less lines of code. So define as many methods as you can...but also as less classes/components as you can.
来源:https://stackoverflow.com/questions/14544504/composition-vs-inheritance-in-mvp