从抽象基类返回规范表示子类是否可以接受? [英] Is it acceptable to return a canonical representation subclass from an abstract base class?

查看:121
本文介绍了从抽象基类返回规范表示子类是否可以接受?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编辑2: TL; DR :有没有办法不破坏OO最佳实践,同时仍然满足一系列相同类型的东西必须转换为规范的东西的约束那种?

Edit 2: TL;DR: Is there a way not to break OO best practices while still satisfying the constraint that a bunch of things of the same kind have to be convertible to a canonical thing of that kind?

另外,请记住我的问题是关于一般情况,而不是具体的例子。这不是作业问题。

Also, please keep in mind that my question is about the general situation, not the specific example. This isn't a homework problem.

假设您有以下内容:


  • 实现通用功能的抽象基类;

  • 一个用作规范表示的具体派生类。

现在假设您希望基类的任何继承者可以转换为规范表示。实现此目的的一种方法是在基类中使用一个抽象方法,该方法旨在将继承者的转换作为规范派生类的实例返回。

Now suppose you want any inheritor of the base class to be convertible to the canonical representation. One way of doing this is by having an abstract method in the base class that is meant to return a conversion of the inheritor as an instance of the canonical derived class.

但是,似乎普遍认为基类不应该知道它们的任何派生类,在一般情况下,我同意。但是,在这种情况下,这似乎是最好的解决方案,因为它允许任意数量的派生类,每个派生类都有自己的实现,我们不需要知道任何事情,通过转换为每个派生的规范表示可以互操作class必须实现。

However, it seems to be generally accepted that base classes should not know about any of their derived classes, and in the general case, I agree. However, in this scenario, this seems to be the best solution because it enables any number of derived classes, each with their own implementation we don't need to know anything about, to be interoperable via the conversion to the canonical representation that every derived class has to implement.

你会采用不同的方式吗?为什么以及如何?

Would you do it differently? Why and how?

几何点的示例:

// 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值都很好定义的含义。


Edit 1: The reason AbstractPoint and ScreenPoint are not the same class is semantic. An AbstractPoint does not have a defined coordinate system, therefore the values of X and Y in an AbstractPoint instance are meaningless. A ScreenPoint does have a defined coordinate system, therefore the values of X and Y in a ScreenPoint instance have a well-defined meaning.

如果 ScreenPoint 是基类,那么 ArbitraryPoint 将是 ScreenPoint ,但情况并非如此。 ArbitraryPoint 可以转换为 ScreenPoint ,但这并不意味着是 - a ScreenPoint

If ScreenPoint were the base class, then an ArbitraryPoint would be a ScreenPoint, which is not the case. An ArbitraryPoint can be converted to a ScreenPoint, but that does not mean that it is-a ScreenPoint.

如果您仍然不相信,请考虑任意坐标系 ACS1 可以定义为对屏幕坐标系 SCS 具有动态偏移量。这意味着两个坐标系之间的映射可能随时间变化,即点 ACS1(1,1)可以映射到 SCS(10,10) )在某一时刻, SCS(42,877)在另一时刻。

If you are still unconvinced, consider that an arbitrary coordinate system ACS1 can be defined as having a dynamic offset to the screen coordinate system SCS. This means the mapping between the two coordinate systems can vary with time, i.e. the point ACS1 (1, 1) can map to SCS (10, 10) at one moment, and SCS (42, 877) at another moment.

推荐答案

这种设计通常是代码气味。基类不应该知道它们的派生类,因为它创建了一个循环依赖。循环依赖通常会导致复杂的设计,很难推断出应该在哪些类中进行。在Java中,了解其派生类的基类甚至可能在极少数情况下导致死锁(我不知道C#)。

This kind of design is usually a code smell. Base classes should not know about their derived classes because it creates a circular dependency. And circular dependencies usually lead to complicated designs where it's difficult to reason about what classes should be doing. In Java, base classes knowing about their derived classes can even result in deadlock in some rare cases (I don't know about C#).

但是,当你确切地知道自己在做什么时,你可以在特殊情况下违反一般规则,特别是如果你想要实现的目标很简单。

However, you can break general rules in special cases, when you know exactly what you are doing, specially if what you are trying to achieve is simple enough.

你的情况似乎很简单。拥有 AbstractPoint ScreenPoint ,因为不同的类是正确的。但实际上它们一起工作:所有 AbstractPoint s应该能够转换为 ScreenPoint (这可能是最多的) AbstractPoint 的合同中的重要功能?)。由于没有另一个不存在, AbstractPoint 知道 ScreenPoint 是没有错的。

And your case here seems to be simple enough. Having AbstractPoint and ScreenPoint as different classes is correct. But in fact they "work together": All AbstractPoints should be able to convert to ScreenPoint (that's maybe the most important functionality in AbstractPoint's contract?). Since one cannot exist without the other, there is nothing wrong about AbstractPoint knowing about ScreenPoint.

更新

在另一种设计中:创建名为<$ c的界面$ C> CanonicalPoint 。
AbstractPoint 有一个名为 ToCanonicalPoint 的方法,它返回 CanonicalPoint
AbstractPoint 的所有派生类必须实现此并返回 CanonicalPoint
ScreenPoint AbstractPoint 的派生类,它实现 CanonicalPoint 界面。
您甚至可以拥有多个实现 CanonicalPoint 的派生类。
注意:如果 AbstractPoint CanonicalPoint 有共同的方法,两者都可以实现另一个
接口叫做 Pointable ,它声明了所有这些方法。

In a different design: Create an interface called CanonicalPoint. AbstractPoint has a method called ToCanonicalPoint, which returns CanonicalPoint. All derived classes of AbstractPoint have to implement this and return CanonicalPoint. ScreenPoint is a derived class of AbstractPoint which implements the CanonicalPoint interface. You could even have more than one derived class that implements CanonicalPoint. Note: If AbstractPoint and CanonicalPoint have methods in common, both can implement another interface called, say, Pointable, that declares all of these methods.

这篇关于从抽象基类返回规范表示子类是否可以接受?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆