繁体   English   中英

从类或抽象类继承

[英]Inherit from a class or an abstract class

如果你有几个类希望它们从基类继承以获得通用功能,那么你应该使用类还是抽象类来实现基类?

这取决于,如果您从未希望能够实例化基类,那么请将其抽象化。 否则将其保留为普通班级。

如果基类不应该被实例化,那么使它成为一个抽象类 - 如果基类需要实例化,那么不要使它抽象。

在这个例子中,使基类抽象是有意义的,因为基类没有任何具体含义:

abstract class WritingImplement
{
    public abstract void Write();
}

class Pencil : WritingImplement
{
    public override void Write() { }
}

但是在下一个示例中,您可以看到基类如何具有具体含义:

class Dog
{
    public virtual void Bark() { }
}

class GoldenRetriever : Dog
{
    public override void Bark() { }
}

这一切都非常主观 - 你应该能够根据你的特定领域的需求做出一个非常好的判断。

这取决于,有问题的基类是否有意义存在于它自己而不是从中派生出来? 如果答案是肯定的,那么它应该是一个普通的类,否则,它应该是一个抽象类。

我建议:

  • 建立一个界面。
  • 在基类中实现接口。
  • 使基类成为一个真正的类,而不是抽象的(见下面的原因)。

我更喜欢真正的类而不是抽象类的原因是抽象类无法实例化,这不必要地限制了未来的选项。 例如,稍后我可能需要基类提供的状态和方法但不能继承而不需要实现接口; 如果基类是抽象的我运气不好,但如果基类是常规类,那么我可以创建基类的实例并将其作为我的其他类的一个组件,并委托给实例重用提供的州/方法。

是的,这不会经常发生,但关键是:当没有理由这样做时,使基类抽象可以防止这种重用/解决方案。

现在,如果实例化基类会以某种方式危险,那么将其抽象化 - 或者最好使其不那么危险,如果可能的话;-)

把它想象成一个银行账户:

您可以创建一个名为“帐户”的通用抽象基本帐户,它包含客户详细信息等基本信息。

然后,您可以创建两个名为“SavingAccount”或“DebitAccount”的派生类,这些类可以具有自己的特定行为,同时受益于基类行为。

在这种情况下,客户必须拥有储蓄账户或借记账户,不允许使用通用的“账户”,因为在现实世界中仅仅拥有一个没有描述的账户并不是很受欢迎。

如果您可以根据需要创建类似的场景,那么抽象是可行的方法。

抽象类用于部分实现的类。

拥有一个抽象类的实例本身没有意义,它需要派生。 如果您希望能够创建基类,则它不能是抽象的。

我喜欢将抽象类视为具有一些预定义成员的接口,因为它们对所有子类都是通用的。

以不同的方式考虑这一点

我的基类是否是它自己的完整对象?

如果答案是否定的,则将其抽象化。 如果是,那么你可能想把它变成一个具体的类。

我会说如果你不打算单独调用基类,那么你应该将它定义为抽象类。

这取决于您是否希望基类自行实现。

作为抽象类,您无法从中创建对象。

抽象类非常适合预定义的功能,例如 - 当知道类应该暴露的最小确切行为但不知道应该使用什么数据来执行它或确切的实现。

abstract class ADataAccess
{
    abstract public void Save();
}

普通(非抽象)类可以用于类似的事情,但是你必须知道能够编写它们的实现细节。

public class DataAccess
{
    public void Save()
    {
        if ( _is_new )
        {
            Insert();
        }
        else if ( _is_modified )
        {
            Update();
        }
    }
}

此外,您可以使用接口(单独或在类上,无论是否为抽象)来定义相同类型的原型定义。

interface ISaveable
{
    void Save();
    void Insert();
    void Update();
}

class UserAccount : ISavable
{
    void ISavable.Save() { ... }
    void ISavable.Insert() { ... }
    void ISavable.Update() { ... }
}

另一种选择可能是使用泛型

class GenDataAccess<T>
{
    public void Save()
    {
        ...
    }
}

所有这些方法都可用于为要使用的类定义某个原型。 如何确保代码A可以与代码B交谈。当然,您可以根据自己的喜好混合和匹配以上所有内容。 没有明确的正确方法,但我喜欢定义接口和抽象类,然后参考接口。 这样就消除了更高级别的“管道”的一些思想要求,同时保持了最大的灵活性。 (让接口消除了使用抽象基类的要求,但将其留作选项)。

我想很多人应该再次重新启用基本的OO课程。

OOA / OOD的基本原理是抽象抽象抽象,直到你不能抽象为止。 如果你所看到的是一个抽象,那就这样吧,那就是你的OOA / OOD告诉你的。 但是,如果你坐在那里想知道“代码”是否应该是抽象的,那么你显然不知道该术语的意思,应该再次学习基本的OOA / OOD / OOP :-)

更重要的是,您应该学习设计模式和谐波理论,这将极大地帮助您的OO设计!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM