问题描述
我有一个问题需要了解如何解决并行继承问题。
并行继承的定义
Fowler定义并行继承如下[1,第68页]:
问题
在图书中
Martin Fowler'重构:改进现有代码的设计'
根据具体情况,有几种方法可以解决这个问题。在这里,我提出了两种可能的解决方案:
注意:我将使用一些类似scala的伪代码,因为它简短易读
List Adapter&泛型
基于以下实现:
最初, ListView
可能有一个默认适配器,需要其适配器实现一些接口,我们称之为可命名
。并使用它来呈现每个项目。
class BasicListViewAdapter扩展ListAdapter [Nameable]
//在scala上它会是一个特征,但是我将其称为接口仅仅是为了示例
接口可命名{
def getName():String
}
当然,并非所有类都将以完全相同的方式呈现。这可以通过使用适配器来解决。
类合作伙伴实现可命名
类客户扩展合作伙伴{
def getName = {this.getClientCode + + this.getFullName}
/ * some implementation * /
}
class Supplier extends Partner {
def getName = {this.getCompanyName}
/ * some implementation * /
}
其中 ListView
的供应商将生成一个公司名称列表, ListView
的客户将显示一个客户代码列表及其各自的名称(无论如何,只是一个例子)。
现在,如果我们想制作更复杂的列表,我们可以使用自定义适配器提供ListView。
类PhoneNumberAdapter扩展ListAdapter [供应商] {/*...*/}
val supliersWithPhoneNumbers = new ListView
supliersWithPhoneNumbers.useAdapter(new SupplierWithPhoneNumberAdapter)
使用这种技术,每个模型类不再需要一个ListView类,只为特殊情况定义自定义适配器。默认适配器和层次结构越好,所需的代码就越少。
再举一个例子,你可以看看
Mixins / Traits
如果你的语言不是使用作文选择允许它(如果你在哲学上同意),你可以使用mixins或traits。我不会深入研究两者之间的差异,只需使用mixins(更多信息:)。
通过使用mixins,您可以以更精细的方式分解行为,解锁各种新模式和解决方案(如果添加一些新模式和解决方案,则更多结构类型和其他特性)。
例如,回到ListView案例,ListView可以合并每个渲染策略,如下所示:
trait NameAndPhoneNumber {
//我们要求将这个特性合并到某些类型的ListView
这个:ListView [{def getName:String}] =>
覆盖def render = ...
}
//其中ListView是covariant
新的ListView [Supplier] with NameAndPhoneNumber
其中 NameAndPhoneNumber
可以应用于多个班级,不仅是客户或供应商。
事实上,它可能会被重构为:
新的ListView [供应商] NameRendering with PhoneRendering
并使用 Stackable Traits 模式(更多信息(此处)[和(这里)[)
参考文献:
- 案例:
- (Stackable Traits Pattern)[
I have a problem to understand how to solve parallel inheritance.
Definition of Parallel Inheritance
Fowler definies parallel inheritance as follows [1, page 68]:
Problem
In the BookRefactoring in Large Software Projects: Performing Complex Restructurings, page 46 the author displays the following parallel inheritance:
Then he solves the parallel inheritance with composition and says:
The solution looks like this:
So the author and Fowler concludes that a parallel inheritance can be solved with
The problem which I see is the classes are still there and if I add a new class a new '*ListView' class must be added.
But for this problem Fowler says:
For the current case this means I move the method to display the entitiesin the entity classes?
So this will hurts the MVC principal or not?!
QUESTION
So how to solve parallel inheritance at alland especially in UI cases?
SOURCE:
1 Martin Fowler 'Refactoring: Improving the Design of Existing Code'
[Book: Refactoring in Large Software Projects: Performing Complex Restructurings, page 46]
There are several ways of solving this, depending on the case. Here I present two possible solutions:
Note: I will use some scala-like pseudo code for the examples just because is short and readable
List Adapter & Generics
Based on implementations from:
Initially, ListView
could have a default adapter which requires its adaptees to implement some interface, let's call it Nameable
. And uses it to render each item.
class BasicListViewAdapter extends ListAdapter[Nameable]
//on scala it would be a trait, but I'll call it interface just for the sake of the example
interface Nameable {
def getName():String
}
Of course, not all the classes will be rendered exactly the same way. This is solved by the use of an Adapter.
class Partner implements Nameable
class Customer extends Partner {
def getName = { this.getClientCode + " " + this.getFullName }
/* some implementation*/
}
class Supplier extends Partner {
def getName = { this.getCompanyName }
/* some implementation*/
}
Where a ListView
of suppliers will generate a list of company names and a ListView
of customers will display a list of client codes with their respective names (whatever, just an example).
Now, if we want to make more complex lists, we could feed the ListView with a custom adapter.
class PhoneNumberAdapter extends ListAdapter[Supplier] { /*...*/}
val supliersWithPhoneNumbers = new ListView
supliersWithPhoneNumbers.useAdapter(new SupplierWithPhoneNumberAdapter)
Using this technique, you no longer need one ListView class per model class, and only define custom adapters for special cases. The better your default adapter and your hierarchy, the less code you will need.
For another example, you can take a look at https://codereview.stackexchange.com/questions/55728/a-generic-mvc-arrayadapter-class
Mixins / Traits
Instead of using composition, if your language of choice allows it (and if you philosophically agree), you might use mixins or traits. I'll not dive in the differences between the two, and just use mixins (for more information: Mixins vs. Traits).
By using mixins you are able to decompose behaviour on a more refined way, unlocking a variety of new patterns and solutions (even more if you add some structural typing and other features).
For instance, back to the ListView case, each rendering strategy could be incorporated by the ListView, looking like:
trait NameAndPhoneNumber {
//we require this trait to be incorporated on some class of type ListView
this:ListView[{def getName:String }] =>
override def render = ...
}
//where ListView is covariant
new ListView[Supplier] with NameAndPhoneNumber
Where NameAndPhoneNumber
could be applied to several classes, not only Customer or Supplier.In fact, it might be refactored to:
new ListView[Supplier] with NameRendering with PhoneRendering
And make use of the Stackable Traits pattern (more info (here)[http://dl.acm.org/citation.cfm?id=2530439] and (here)[http://www.artima.com/scalazine/articles/stackable_trait_pattern.html])
References:
- Traits paper
- Mixins paper
- Sibiling pattern
- A case: Backbonejs
- (Stackable Traits Pattern)[http://www.artima.com/scalazine/articles/stackable_trait_pattern.html]
这篇关于如何在UI情况下解决并行继承问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!