避免并行继承层次结构
我有两个并行继承链:
Vehicle <- Car
<- Truck <- etc.
VehicleXMLFormatter <- CarXMLFormatter
<- TruckXMLFormatter <- etc.
我的经验是,随着并行继承层次结构的增长,它们可能会成为维护难题。
即不向我的主要类添加 toXML()、toSoap()、toYAML() 方法。
如何在不破坏关注点分离概念的情况下避免并行继承层次结构?
I have two parallel inheritance chains:
Vehicle <- Car
<- Truck <- etc.
VehicleXMLFormatter <- CarXMLFormatter
<- TruckXMLFormatter <- etc.
My experience has been that parallel inheritance hierarchies can become a maintenance headache as they grow.
i.e. NOT adding toXML(), toSoap(), toYAML()
methods to my principal classes.
How do I avoid a parallel inheritance hierarchy without breaking the concept of separation of concerns?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我正在考虑使用访客模式。
这样,您就可以避免额外的继承树,并将格式化逻辑与您的车辆类分开。
当然,当您创建新车辆时,您必须向 Formatter 接口添加另一个方法(并在 formatter 接口的所有实现中实现这个新方法)。
但是,我认为这比创建一个新的 Vehicle 类更好,并且为您拥有的每个 IVehicleFormatter 创建一个可以处理这种新型车辆的新类。
I am thinking of using the Visitor pattern.
With this, you avoid an extra inheritance tree, and keep the formatting logic separated from your Vehicle-classes.
Offcourse, when you create a new vehicle, you'll have to add another method to the Formatter interface (and implement this new method in all the implementations of the formatter interface).
But, I think that this is better then creating a new Vehicle class, and for every IVehicleFormatter you have, create a new class that can handle this new kind of vehicle.
另一种方法是采用推模型而不是拉模型。 通常,您需要不同的格式化程序,因为您破坏了封装,并且有类似的情况:
您将数据从特定类型提取到格式化程序中。
相反,创建一个与格式无关的数据接收器并反转流程,以便特定类型将数据推送到接收器
这意味着您仍然封装了数据,并且您只是将标记的数据提供给接收器。 然后,XML 接收器可能会忽略数据的某些部分,可能会对其中一些数据重新排序,然后写入 XML。 它甚至可以在内部委托给不同的接收器策略。 但接收器不一定需要关心车辆的类型,只需要关心如何以某种格式表示数据。 使用驻留全局 ID 而不是内联字符串有助于降低计算成本(仅当您编写 ASN.1 或其他严格格式时才重要)。
Another approach is to adopt a push model rather than a pull model. Typically you need different formatters because you're breaking encapsulation, and have something like:
where you're pulling data from the specific type into the formatter.
Instead, create a format-agnostic data sink and invert the flow so the specific type pushes data to the sink
That means you've still got the data encapsulated, and you're just feeding tagged data to the sink. An XML sink might then ignore certain parts of the data, maybe reorder some of it, and write the XML. It could even delegate to different sink strategy internally. But the sink doesn't necessarily need to care about the type of the vehicle, only how to represent the data in some format. Using interned global IDs rather than inline strings helps keep the computation cost down (only matters if you're writing ASN.1 or other tight formats).
您可以尝试避免格式化程序的继承。 只需制作一个可以处理
Car
、Truck
等的VehicleXmlFormatter
即可,通过划分之间的职责,可以轻松实现重用方法并找出良好的调度策略。 避免魔法过载; 格式化程序中的命名方法应尽可能具体(例如formatTruck(Truck ...)
而不是format(Truck ...)
)。仅当需要双重分派时才使用 Visitor:当您拥有
Vehicle
类型的对象并且希望将它们格式化为 XML 而不知道实际的具体类型时。 访问者本身并不能解决在格式化程序中实现重用的基本问题,并且可能会引入您可能不需要的额外复杂性。 上述方法重用的规则(切碎和分派)也适用于您的 Visitor 实现。You could try to avoid inheritance for your formatters. Simply make a
VehicleXmlFormatter
that can deal withCar
s,Truck
s, ... Reuse should be easy to achieve by chopping up the responsibilities between methods and by figuring out a good dispatch-strategy. Avoid overloading magic; be as specific as possible in naming methods in your formatter (e.g.formatTruck(Truck ...)
instead offormat(Truck ...)
).Only use Visitor if you need the double dispatch: when you have objects of type
Vehicle
and you want to format them into XML without knowing the actual concrete type. Visitor itself doesn't solve the base problem of achieving reuse in your formatter, and may introduce extra complexity you may not need. The rules above for reuse by methods (chopping up and dispatch) would apply to your Visitor implementation as well.您可以使用 Bridge_pattern
桥接模式将抽象与其实现分离,以便两者可以独立变化。
两个正交类层次结构(抽象层次结构和实现层次结构)相互链接使用组合(而不是继承)。这种组合有助于两个层次结构独立变化。
实现从来不涉及抽象。 抽象包含实现接口作为成员(通过组合)。
回到您的示例:
Vehicle
是抽象Car
和Truck
是 >RefinedAbstractionFormatter
是实现者XMLFormatter
、POJOFormatter
是ConcreteImplementor伪代码:
相关帖子:
什么时候使用桥接模式? 它与适配器模式有何不同?
You can use Bridge_pattern
Bridge pattern decouple an abstraction from its implementation so that the two can vary independently.
Two orthogonal class hierarchies (The Abstraction hierarchy and Implementation hierarchy) are linked using composition (and not inheritance).This composition helps both hierarchies to vary independently.
Implementation never refers Abstraction. Abstraction contains Implementation interface as a member (through composition).
Coming back to your example:
Vehicle
is AbstractionCar
andTruck
are RefinedAbstractionFormatter
is ImplementorXMLFormatter
,POJOFormatter
are ConcreteImplementorPseudo code:
related post:
When do you use the Bridge Pattern? How is it different from Adapter pattern?
为什么不让 IXMLFormatter 成为一个带有 toXML()、toSoap()、to YAML() 方法的接口,并让 Vehicle、Car 和 Truck 都实现它呢? 这种方法有什么问题吗?
Why not make IXMLFormatter an interface with toXML(), toSoap(), to YAML() methods and make the Vehicle, Car and Truck all implement that? What is wrong with that approach?
我想在 Frederiks 的答案中添加泛型。
I want to add generics to Frederiks answer.