如果你有多个类需要继承自一个基类以实现共同的功能,那么你应该使用一个普通的类还是抽象类来实现基类呢?
这取决于情况,如果你永远不想实例化基类,则使其成为抽象类。否则将其保留为普通类。
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() { }
}
其实这都很主观 - 你应该根据你的特定领域的需求来做出相当好的判断。
这要看情况,如果基类本身存在的意义就不需要从它派生出其他类,那么它应该是一个普通类;否则,它应该是一个抽象类。
想象一下像银行账户一样:
您可以创建一个名为“Account”的通用抽象基本帐户,其中包含客户详细信息等基本信息。
然后,您可以创建两个派生类别,称为“SavingAccount”或“DebitAccount”,它们可以具有自己的特定行为,同时受益于基类别行为。
这是客户必须拥有储蓄账户或借记账户的情况,不允许使用通用的“Account”,因为在现实世界中仅拥有无描述的账户并不常见。
如果您需要创建类似的场景,则抽象是正确的选择。
抽象类是部分实现的类。
单独使用抽象类没有意义,它需要被派生。如果您想创建基类,则不能将其设置为抽象。
我喜欢将抽象类视为接口,因为它们具有一些预定义成员,因为这些成员对所有子类都是通用的。
以不同的方式思考这个问题
我的基类本身是否是一个完整的对象?
如果答案是否定的,那么将其定义为抽象类。如果答案是肯定的,那么您可能想将其定义为具体类。
抽象类非常适合预定义的功能,例如 - 当我们知道一个类应该公开的最小确切行为,但不知道它应该使用哪些数据来执行或具体实现。
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通信的方法。当然,您可以根据自己的喜好混合和匹配上述所有内容。没有明确的正确方式,但我喜欢定义接口和抽象类,然后引用这些接口。这种方式消除了在更高级别的类中进行“管道连接”的一些思考要求,同时保持了最大的灵活性。(使用接口可以避免使用抽象基类的要求,但仍将其作为一种选择留下)。
如果你不打算单独调用基类,那么你应该将它定义为抽象类。
这取决于你想要基类自行实现还是不需要。
作为一个抽象类,你不能直接从中创建对象。