您应该定义一个接口,然后您的代码应该只接受实现该接口的对象。虽然使用abstract
和创建公共基类非常诱人,但这种方法(几乎)根据定义(几乎)是错误的。
在 C# 和其他不允许多重继承的语言中,创建一个基类只是为了定义一个其他人必须填充的空“骨架”并强制每个人使用它是非常有限的。每个人都必须继承它,因此,例如,他们将无法重用自己现有的类层次结构,或者必须创建乏味的桥梁或 (...)。
如果您的抽象基类除了一个/几个/几十个/数百个抽象方法、事件和属性之外什么都没有——它应该是一个接口,因为它只定义了“通用要求”。
使用通用事物创建新抽象基础的唯一合理理由是实际提供默认实现。尽管如此,对于这样的基类,也应该定义一个接口,基类应该实现它并允许覆盖甚至可能将某些东西标记为实际上是抽象的——但您的代码仍然应该通过接口引用所有内容,而不是基类。这样的抽象基础应该是实现者的帮助/快捷方式,而不是强制性的。如果有人想从头开始做所有事情 - 他将实现接口并忽略示例代码的基础,您的代码仍然可以很好地使用它。尽管如此,所有通用代码都可以作为一组在该接口上运行的静态帮助程序类提供。
抽象基类实际上是需要的,并且在某些极端情况下不能被接口取代,例如,当您必须强制派生类具有参数构造函数时,或者您自己必须从某些东西派生时,即。像 WPF Visual 或 UIElement 或 DependencyObject --- 因此微软的设计在这里也有一点缺陷。他们强制从基类派生,它在许多地方影响了开发人员(例如,来自 EntityFramework 的数据模型对象不是 DependencyObjects 等)。尽管如此,我认为他们应该从中抽象出来——看看 Visual 和朋友,没有多少内部例程不能提升到接口。我不认为他们这样做只是“因为”,而是,我认为它是关于性能和削减演员/方法调度。
请注意,我所说的一切并不完全符合您在问题中提出的内容。在那里,您已经假设“父/基类将处理 XYZ”。这意味着,您从一开始就放弃了界面方法。使用接口,您将只定义 click 和 cancelclick 必须存在,但您将无法强制/提供“基本实现”。这些事情你可以用基类来做。因此,我认为您应该采取开放/混合的方法:定义接口,定义静态可重用处理程序,仅使用接口,并为一些“懒惰的编码器”提供基类:
public interface IClickable
{
ICommand CancelCommand { get; }
void CancelClick();
bool CanCancelClick();
}
public static class ClickableDefaultImpl
{
public static void DefaultCancelClick(IClickable obj)
{
... do the common things on the OBJ
}
public static bool DefaultCanCancelClick(IClickable obj)
{
... do the common things on the OBJ
}
}
public abstract class Clickable : IClickable
{
public void CancelClick() { ClickableDefaultImpl.CancelClick(this); }
public bool CanCancelClick() { return ClickableDefaultImpl.CanCancelClick(this); }
}
这可能看起来很臃肿,但它对定制非常开放。尽管具有开放性,但几乎没有办法强制每个人都必须使用“ClickableImpl”。有一些方法,但是..它们会包括更多的膨胀和时间/内存开销,我认为它们现在不值得描述。
请记住估计将来将使用此代码的人员、方式和数量。如果是用于一/二/五用途,请坚持使用抽象基础。但是如果你感觉到有几十个或几百个子实现,你最好加上那个小膨胀——以后可能会节省很多时间。