Oop 当使用多态性没有意义时,如何实现特定于类型的功能?
OOP语言未被正确利用的常见危险信号如下所示:Oop 当使用多态性没有意义时,如何实现特定于类型的功能?,oop,design-patterns,inheritance,if-statement,polymorphism,Oop,Design Patterns,Inheritance,If Statement,Polymorphism,OOP语言未被正确利用的常见危险信号如下所示: if (typeof(x) == T1) { DoSomethingWithT1(x); } else if (typeof(x) == T2) { DoSomethingWithT2(x); } IEnumerably<IBlahImplementor> blahImplementors; foreach (var implementor in blahImplementors) { blahImplemento
if (typeof(x) == T1)
{
DoSomethingWithT1(x);
}
else if (typeof(x) == T2)
{
DoSomethingWithT2(x);
}
IEnumerably<IBlahImplementor> blahImplementors;
foreach (var implementor in blahImplementors)
{
blahImplementors.Add(implementor.ForType, implementor);
}
// interface used to add external functionality to pricing models
public interface PricingModelVisitor {
void visitPricingModel1(PricingModel1 m);
void visitPricingModel2(PricingModel2 m);
...
}
// your existing base-class, with added abstract accept() method to accept a visitor
public abstract class PricingModelBase {
public abstract void accept(PricingModelVisitor v);
...
}
// concrete implementations of the PricingModelBase implement accept() by calling the
// appropriate method on the visitor, passing themselves as the argument
public class PricingModel1 : PricingModelBase {
public void accept(PricingModelVisitor v) { v.visitPricingModel1(this); }
...
}
public class PricingModel2 : PricingModel {
public void accept(PricingModelVisitor v) { v.visitPricingModel2(this); }
...
}
// concrete implementation of the visitor interface, in this case with the new
// functionality of adding the appropriate controls to a parent control
public class ParameterGuiVisitor : PricingModelVisitor {
private Control _parent;
public ParameterGuiVisitor(Control parent) { _parent = parent; }
visitPricingModel1(PricingModel1 m) {
// add controls to _parent for PricingModel1
}
visitPricingModel2(PricingModel2 m) {
// add controls to _parent for PricingModel1
}
}
此类设计问题的标准“修复”是通过继承基类型或实现公共接口(使用支持它的语言),使T1
和T2
共享一个接口;例如,在C#中,解决方案可能是:
public interface IT
{
void DoSomething();
}
但是,有时您希望实现基于对象类型而不同的功能,但该功能不属于该对象的类型;因此,多态性似乎是错误的方向
例如,考虑UI的情况,该UI提供给定数据块的视图。假设此视图能够根据显示的数据类型呈现各种布局和控件,那么在没有大量
if
/else
语句的情况下,您将如何实现此特定于类型的呈现
出于我希望显而易见的原因,在这种情况下,将呈现逻辑放在类型本身中给我的印象是一个非常糟糕的决定。另一方面,如果不将数据对象的类型与其可视化表示耦合,我很难看到如何避免if
/else
场景
这里有一个具体的例子:我开发了一个交易应用程序,它对各种市场产品使用许多不同的定价模型。这些不同的模型由从公共PricingModel
基继承的类型表示;每种类型都与一组完全不同的参数相关联。当用户想要查看特定定价模型(特定产品)的参数时,这些参数当前通过一个窗体显示,该窗体检测模型的类型并显示一组适当的控件。我的问题是如何比目前更优雅地实现它(使用一个大的if
/else
块)
我意识到这似乎是一个非常基本的问题;这只是我知识(坚实的OOP原则?设计模式?常识?)中的一个缺口,我认为是时候解决了。我们正在按类型将(Spring.Net)这样的功能注入字典中
IDictionary<Type, IBlahImplementor> blahImplementors;
blahImplementors[thingy.GetType()].Do(thingy);
然后将其添加到字典中,如下所示:
if (typeof(x) == T1)
{
DoSomethingWithT1(x);
}
else if (typeof(x) == T2)
{
DoSomethingWithT2(x);
}
IEnumerably<IBlahImplementor> blahImplementors;
foreach (var implementor in blahImplementors)
{
blahImplementors.Add(implementor.ForType, implementor);
}
// interface used to add external functionality to pricing models
public interface PricingModelVisitor {
void visitPricingModel1(PricingModel1 m);
void visitPricingModel2(PricingModel2 m);
...
}
// your existing base-class, with added abstract accept() method to accept a visitor
public abstract class PricingModelBase {
public abstract void accept(PricingModelVisitor v);
...
}
// concrete implementations of the PricingModelBase implement accept() by calling the
// appropriate method on the visitor, passing themselves as the argument
public class PricingModel1 : PricingModelBase {
public void accept(PricingModelVisitor v) { v.visitPricingModel1(this); }
...
}
public class PricingModel2 : PricingModel {
public void accept(PricingModelVisitor v) { v.visitPricingModel2(this); }
...
}
// concrete implementation of the visitor interface, in this case with the new
// functionality of adding the appropriate controls to a parent control
public class ParameterGuiVisitor : PricingModelVisitor {
private Control _parent;
public ParameterGuiVisitor(Control parent) { _parent = parent; }
visitPricingModel1(PricingModel1 m) {
// add controls to _parent for PricingModel1
}
visitPricingModel2(PricingModel2 m) {
// add controls to _parent for PricingModel1
}
}
您所描述的基本上就是应用程序的用例 编辑:对于您的具体示例,您可以像下面这样应用访问者模式:
if (typeof(x) == T1)
{
DoSomethingWithT1(x);
}
else if (typeof(x) == T2)
{
DoSomethingWithT2(x);
}
IEnumerably<IBlahImplementor> blahImplementors;
foreach (var implementor in blahImplementors)
{
blahImplementors.Add(implementor.ForType, implementor);
}
// interface used to add external functionality to pricing models
public interface PricingModelVisitor {
void visitPricingModel1(PricingModel1 m);
void visitPricingModel2(PricingModel2 m);
...
}
// your existing base-class, with added abstract accept() method to accept a visitor
public abstract class PricingModelBase {
public abstract void accept(PricingModelVisitor v);
...
}
// concrete implementations of the PricingModelBase implement accept() by calling the
// appropriate method on the visitor, passing themselves as the argument
public class PricingModel1 : PricingModelBase {
public void accept(PricingModelVisitor v) { v.visitPricingModel1(this); }
...
}
public class PricingModel2 : PricingModel {
public void accept(PricingModelVisitor v) { v.visitPricingModel2(this); }
...
}
// concrete implementation of the visitor interface, in this case with the new
// functionality of adding the appropriate controls to a parent control
public class ParameterGuiVisitor : PricingModelVisitor {
private Control _parent;
public ParameterGuiVisitor(Control parent) { _parent = parent; }
visitPricingModel1(PricingModel1 m) {
// add controls to _parent for PricingModel1
}
visitPricingModel2(PricingModel2 m) {
// add controls to _parent for PricingModel1
}
}
现在,当您需要显示PricingModelVisitor
的特定子类型的参数的编辑控件时,您可以简单地调用
somePricingModel.accept(new ParameterGuiVisitor(parentControl))
它将为您填充适当的GUI。您可以在描述和命令模式时使用公共接口方法来触发“功能不属于该对象的类型”的方法。我认为这不会打破坚实的OOP原则。访问者可能不够灵活。不能动态添加访问类型。您只能动态添加访问者的实现。您所说的“动态添加访问者类型”是什么意思?visitor模式允许您通过简单地向处理此新类型的visitor接口添加新方法来添加新类型。这将在编译时迫使您更新所有现有的visitor实现,以适当地处理此新类型,这在基于字典的解决方案中很容易被忽略。有时需要注入、轻松注册类型、在运行时添加类型,添加指定访问者界面的程序集不知道的类型等。我并不是说动态比静态好,我只是说对于这里描述的问题来说,它可能不够灵活。“有时它需要…”-问题中没有提到这些。