编辑 2: TL;DR : 有没有办法不打破面向对象的最佳实践,同时仍然满足一堆同类事物必须可以转换为那种规范事物的约束?
另外,请记住,我的问题是关于一般情况,而不是具体示例。这不是家庭作业的问题。
假设您有以下内容:
现在假设您希望基类的任何继承者都可以转换为规范表示。这样做的一种方法是在基类中有一个抽象方法,该方法旨在将继承者的转换返回为规范派生类的实例。
然而,似乎普遍接受的是基类不应该知道它们的任何派生类,在一般情况下,我同意。然而,在这种情况下,这似乎是最好的解决方案,因为它支持任意数量的派生类,每个派生类都有自己的实现,我们不需要知道任何事情,通过转换为每个派生的规范表示可以互操作类必须实现。
你会做不同的事情吗?为什么以及如何?
几何点的示例:
// an abstract point has no coordinate system information, so the values
// of X and Y are meaningless
public abstract class AbstractPoint {
public int X;
public int Y;
public abstract ScreenPoint ToScreenPoint();
}
// a point in the predefined screen coordinate system; the meaning of X
// and Y is known
public class ScreenPoint : AbstractPoint {
public ScreenPoint(int x, int y) {
X = x;
Y = y;
}
public override ScreenPoint ToScreenPoint()
=> new ScreenPoint(X, Y);
}
// there can be any number of classes like this; we don't know anything
// about their coordinate systems and we don't care as long as we can
// convert them to `ScreenPoint`s
public class ArbitraryPoint : AbstractPoint {
private int arbitraryTransformation;
public ArbitraryPoint(int x, int y) {
X = x;
Y = y;
}
public override ScreenPoint ToScreenPoint()
=> new ScreenPoint(X * arbitraryTransformation, Y * arbitraryTransformation);
// (other code)
}
编辑 1:
AbstractPoint
和 ScreenPoint
不是同一个类的原因是语义。 AbstractPoint
没有定义的坐标系,因此 AbstractPoint
实例中 X 和 Y 的值是没有意义的。 ScreenPoint
确实具有定义的坐标系,因此 ScreenPoint
实例中的 X 和 Y 值具有明确定义的含义。如果
ScreenPoint
是基类,那么 ArbitraryPoint
将是 ScreenPoint
,事实并非如此。 ArbitraryPoint
可以转换为 ScreenPoint
,但这并不意味着它 is-a
ScreenPoint
。如果您仍然不相信,请考虑可以将任意坐标系
ACS1
定义为对屏幕坐标系 SCS
具有动态偏移量。这意味着两个坐标系之间的映射可以随时间变化,即点 ACS1 (1, 1)
可以在某一时刻映射到 SCS (10, 10)
,而在另一时刻映射到 SCS (42, 877)
。
最佳答案
这种设计通常是代码异味。基类不应该知道它们的派生类,因为它会创建循环依赖。循环依赖通常会导致复杂的设计,在这种情况下很难推断类应该做什么。在 Java 中,基类知道它们的派生类甚至会在极少数情况下导致死锁(我不知道 C#)。
但是,您可以在特殊情况下打破一般规则,当您确切地知道自己在做什么时,特别是如果您要实现的目标足够简单。
你的情况似乎很简单。将 AbstractPoint
和 ScreenPoint
作为不同的类是正确的。但实际上它们“一起工作”:所有 AbstractPoint
都应该能够转换为 ScreenPoint
(这可能是 AbstractPoint
契约(Contract)中最重要的功能?)。由于没有另一个就不能存在,因此 AbstractPoint
了解 ScreenPoint
并没有错。
更新
在不同的设计中:创建一个名为 CanonicalPoint
的接口(interface)。AbstractPoint
有一个名为 ToCanonicalPoint
的方法,它返回 CanonicalPoint
。AbstractPoint
的所有派生类都必须实现它并返回 CanonicalPoint
。ScreenPoint
是 AbstractPoint
的派生类,它实现了 CanonicalPoint
接口(interface)。
您甚至可以拥有多个实现 CanonicalPoint
的派生类。
注意:如果 AbstractPoint
和 CanonicalPoint
有共同的方法,两者都可以实现另一个
称为 Pointable
的接口(interface),它声明了所有这些方法。
关于c# - 从抽象基类返回规范表示子类是否可以接受?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46985824/