c# - 从抽象基类返回规范表示子类是否可以接受?

标签 c# oop inheritance design-patterns

编辑 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: AbstractPointScreenPoint 不是同一个类的原因是语义。 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#)。

    但是,您可以在特殊情况下打破一般规则,当您确切地知道自己在做什么时,特别是如果您要实现的目标足够简单。

    你的情况似乎很简单。将 AbstractPointScreenPoint 作为不同的类是正确的。但实际上它们“一起工作”:所有 AbstractPoint 都应该能够转换为 ScreenPoint (这可能是 AbstractPoint 契约(Contract)中最重要的功能?)。由于没有另一个就不能存在,因此 AbstractPoint 了解 ScreenPoint 并没有错。

    更新

    在不同的设计中:创建一个名为 CanonicalPoint 的接口(interface)。AbstractPoint 有一个名为 ToCanonicalPoint 的方法,它返回 CanonicalPointAbstractPoint 的所有派生类都必须实现它并返回 CanonicalPointScreenPointAbstractPoint 的派生类,它实现了 CanonicalPoint 接口(interface)。
    您甚至可以拥有多个实现 CanonicalPoint 的派生类。
    注意:如果 AbstractPointCanonicalPoint 有共同的方法,两者都可以实现另一个
    称为 Pointable 的接口(interface),它声明了所有这些方法。

    关于c# - 从抽象基类返回规范表示子类是否可以接受?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46985824/

    相关文章:

    php - 如何从另一个类调用非静态方法

    kotlin - 如何解决 Rust 中的继承问题?

    c# - 在 .NET Core 中连接到 Oracle 数据库时出现 Kerberos 错误

    c# - Directory.GetFiles 保持上次访问时间

    c# - LINQ,第三个嵌套子项的属性值等于什么?

    c# - 了解接口(interface) V 类

    c# - JSON 解析银行假日日历

    java - 如何在父类(super class)中重用@Autowired bean?

    c# - 我可以从 Dapper 查询返回多个派生类型的集合吗

    C++ 初始化列表忽略调用父类构造函数