我正在为3D建模程序编写插件。我有一个自定义类,它包装3D模型中的元素实例,然后从它包装的元素派生它的属性。当模型中的元素发生变化时,我希望我的类根据新几何更新其属性。
在下面的简化示例中。我有类AbsCurveBasd,Extrusion和Shell,它们都是相互派生的。这些类中的每一个都实现了一个RefreshFromBaseShape()方法,该方法根据类包装的当前baseShape更新特定属性。
我可以在RefreshFromBaseShape()的每个实现中调用base.RefreshFromBaseShape(),以确保更新所有属性。但我想知道是否有更好的方法,我不必记得在RefershFromBaseShape()的每个实现中都这样做?例如,因为AbsCurveBased没有无参数构造函数,所以除非构造函数调用基类构造函数,否则代码甚至不会编译。
public abstract class AbsCurveBased
{
internal Curve baseShape;
double Area{get;set;}
public AbsCurveBased(Curve baseShape)
{
this.baseShape = baseShape;
RefreshFromBaseShape();
}
public virtual void RefreshFromBaseShape()
{
//sets the Area property from the baseShape
}
}
public class Extrusion : AbsCurveBased
{
double Volume{get;set;}
double Height{get;set;}
public Extrusion(Curve baseShape):base(baseShape)
{
this.baseShape = baseShape;
RefreshFromBaseShape();
}
public override void RefreshFromBaseShape()
{
base.RefreshFromBaseShape();
//sets the Volume property based on the area and the height
}
}
public class Shell : Extrusion
{
double ShellVolume{get;set;}
double ShellThickness{get;set;}
public Shell(Curve baseShape): base(baseShape)
{
this.baseShape = baseShape;
RefreshFromBaseShape();
}
public void RefreshFromBaseShape()
{
base.RefreshFromBaseShape();
//sets this Shell Volume from the Extrusion properties and ShellThickness property
}
}
答案 0 :(得分:0)
你这样做的方式是一种非常典型的模式,它肯定没有任何问题 - 通常是这样的事情。没有办法强制base
调用,假设它发生的时间和地点始终取决于实施者。如果您真的热衷于强制它,那么您应该考虑使用受保护的事件:
public abstract class AbsCurveBased
{
internal Curve baseShape;
double Area{get;set;}
public AbsCurveBased(Curve baseShape)
{
this.baseShape = baseShape;
RefreshFromBaseShape();
}
public void RefreshFromBaseShape()
{
//sets the Area property from the baseShape
...
// call child handlers
var handler = RefreshingFromBaseShape;
if (handler != null)
handler();
}
protected event Action RefreshingFromBaseShape;
}
public class Shell : Extrusion
{
double ShellVolume{get;set;}
double ShellThickness{get;set;}
public Shell(Curve baseShape): base(baseShape)
{
this.RefreshingFromBaseShape += RefreshingFromBaseShapeHandler;
this.baseShape = baseShape;
RefreshFromBaseShape();
}
private void RefreshingFromBaseShapeHandler()
{
//sets this Shell Volume from the Extrusion properties and ShellThickness property
}
}
这样,继承链中的任何类只能控制其处理程序,并且不能取消注册其祖先的处理程序,或者在祖先插入它们之前将它们插入到链中。
对于你的特定情况,这似乎太复杂了,没有价值。
此外,如果只从构造函数调用RefreshFromBaseShape
,那么它可能应该是该构造函数的参数。考虑:
public abstract class AbsCurveBased
{
internal Curve baseShape;
double Area{get;set;}
public AbsCurveBased(Curve baseShape)
{
this.baseShape = baseShape;
//sets the Area property from the baseShape
}
protected AbsCurveBased(Curve baseShape, Action refreshFromBaseShape):
this(baseShape)
{
refreshFromBaseShape();
}
}
public class Shell : Extrusion
{
double ShellVolume{get;set;}
double ShellThickness{get;set;}
public Shell(Curve baseShape):
base(baseShape, RefreshFromBaseShape)
{
}
protected Shell(Curve baseShape, Action refreshFromBaseShape):
this(baseShape)
{
refreshFromBaseShape();
}
private void RefreshFromBaseShape()
{
//sets this Shell Volume from the Extrusion properties and ShellThickness property
}
}
答案 1 :(得分:0)
我不确定为什么你需要一个单独的虚拟方法来做到这一点。为什么每个类都不能在自己的构造函数中进行计算?在您的示例中,Area将在AbsCurveBased构造函数中计算;音量&挤出构造函数中的高度等。
通常,从构造函数调用虚函数是不好的做法,因为虚函数在构造函数完成之前在子类中调用。所以子类中的方法正在运行,而对象只是部分通过构造。
评论后更新
在这种情况下,我会在每个类中使用私有的DoCalculate()方法。这将由构造函数调用,并且还将在构造函数中不再调用RefreshFromBaseShape()。虽然这不是减少链接基本调用的必要性,但这是一种正常的模式。