我在库中有一个抽象类。我正在努力使其尽可能容易地正确实现此类的派生。问题是我需要分三步初始化对象:抓取一个文件,执行一些中间步骤,然后使用该文件。第一步和最后一步是特定于派生类的。这是一个精简的示例。
abstract class Base
{
// grabs a resource file specified by the implementing class
protected abstract void InitilaizationStep1();
// performs some simple-but-subtle boilerplate stuff
private void InitilaizationStep2() { return; }
// works with the resource file
protected abstract void InitilaizationStep3();
protected Base()
{
InitilaizationStep1();
InitilaizationStep2();
InitilaizationStep3();
}
}
麻烦的当然是构造函数中的虚方法调用了。如果库的使用者不能指望派生类被完全初始化,恐怕他们会发现自己在使用类时受到限制。
我可以将构造函数中的逻辑提取到 protected Initialize()
方法中,但随后实现者可能会调用 Step1()
和 Step3()
而不是直接调用 Initialize()
。问题的症结在于,如果跳过Step2()
,就不会出现明显的错误;只是在某些情况下表现糟糕。
我觉得无论哪种方式都存在严重且不明显的“陷阱”,图书馆的 future 用户将不得不解决这个问题。我应该使用其他设计来实现这种初始化吗?
如有必要,我可以提供更多详细信息;我只是想提供最简单的例子来表达问题。
最佳答案
我会考虑创建一个 abstract factory负责使用 template method 实例化和初始化派生类的实例用于初始化。
举个例子:
public abstract class Widget
{
protected abstract void InitializeStep1();
protected abstract void InitializeStep2();
protected abstract void InitializeStep3();
protected internal void Initialize()
{
InitializeStep1();
InitializeStep2();
InitializeStep3();
}
protected Widget() { }
}
public static class WidgetFactory
{
public static CreateWidget<T>() where T : Widget, new()
{
T newWidget = new T();
newWidget.Initialize();
return newWidget;
}
}
// consumer code...
var someWidget = WidgetFactory.CreateWidget<DerivedWidget>();
此工厂代码可以得到显着改进 - 特别是如果您愿意使用 IoC 容器来处理此职责...
如果您无法控制派生类,您可能无法阻止它们提供可调用的公共(public)构造函数 - 但至少您可以建立消费者可以遵守的使用模式。
并不总是可以防止您的类的用户搬起石头砸自己的脚 - 但是,您可以提供基础设施来帮助消费者在熟悉设计后正确使用您的代码。
关于c# - 如何避免从基类构造函数调用虚方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1155305/