从类或抽象类继承

22

如果你有多个类需要继承自一个基类以实现共同的功能,那么你应该使用一个普通的类还是抽象类来实现基类呢?

11个回答

42

这取决于情况,如果你永远不想实例化基类,则使其成为抽象类。否则将其保留为普通类。


3
如果实例化基类没有任何意义,那就将其定义为抽象类,以便更好地说明。 - mbillard

16
如果基类不应该被实例化,则将其设置为抽象类;如果基类需要被实例化,则不要将其设置为抽象类。
在这个例子中,将基类设置为抽象类是有意义的,因为基类没有任何具体含义。
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() { }
}

其实这都很主观 - 你应该根据你的特定领域的需求来做出相当好的判断。


6

这要看情况,如果基类本身存在的意义就不需要从它派生出其他类,那么它应该是一个普通类;否则,它应该是一个抽象类。


4
我建议:
  • 创建一个接口。
  • 在基类中实现该接口。
  • 将基类变成一个真正的类,而不是抽象类(请参见下文原因)。
我更喜欢使用真正的类而不是抽象类的原因是,抽象类无法被实例化,这会不必要地限制未来的选项。例如,以后我可能需要基类提供的状态和方法,但不能继承并且不需要实现接口;如果基类是抽象的,那么我就没办法了,但如果基类是普通类,那么我可以创建一个基类的实例,并将其作为我的其他类的组件进行持有,并委托该实例来重用提供的状态/方法。
是的,这种情况并不经常发生,但关键是:使基类成为抽象类会阻止这种重用/解决方案,而没有理由这样做。
现在,如果实例化基类会有危险,那么将其变成抽象类 - 或者最好的方式是尽可能减少危险。;-)

3

想象一下像银行账户一样:

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

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

这是客户必须拥有储蓄账户或借记账户的情况,不允许使用通用的“Account”,因为在现实世界中仅拥有无描述的账户并不常见。

如果您需要创建类似的场景,则抽象是正确的选择。


1

抽象类是部分实现的类。

单独使用抽象类没有意义,它需要被派生。如果您想创建基类,则不能将其设置为抽象。

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


1

以不同的方式思考这个问题

我的基类本身是否是一个完整的对象?

如果答案是否定的,那么将其定义为抽象类。如果答案是肯定的,那么您可能想将其定义为具体类。


0

抽象类非常适合预定义的功能,例如 - 当我们知道一个类应该公开的最小确切行为,但不知道它应该使用哪些数据来执行或具体实现。

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通信的方法。当然,您可以根据自己的喜好混合和匹配上述所有内容。没有明确的正确方式,但我喜欢定义接口和抽象类,然后引用这些接口。这种方式消除了在更高级别的类中进行“管道连接”的一些思考要求,同时保持了最大的灵活性。(使用接口可以避免使用抽象基类的要求,但仍将其作为一种选择留下)。


0

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


我认为这还不够 - 只有在直接实例化没有意义时,才应该声明一个类为抽象类。你是否打算这样做是完全不同的问题。 - Alun Harford

0

这取决于你想要基类自行实现还是不需要。

作为一个抽象类,你不能直接从中创建对象。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接