在现有的项目中,我要继承一个声明为Singleton的Controller类(MVC),以便定义我自己的处理方式。如何适本地派生这个 Singleton 类?
首先,我扩展了上下文和这种继承的需要。
我添加到现有软件中的应用程序想要使用一个 MVC 模块,该模块执行的任务与我愿意执行的任务几乎相同。它使用相同的方法进行签名和轻微修改。重写我自己的 MVC 模块肯定会重复代码。现有模块本质上是面向其应用到软件的另一部分,我不能简单地使用相同的模块。但是被写成模型- View - Controller 模式,其中 Controller 是单例。我已经派生了 View。
其次,我怀疑我可以经典地派生 Singleton 类。
从继承类调用构造函数只会为父类调用 getinstance() 并且无法从派生类 (?) 返回对象。
第三,这就是我所看到的一些处理方式。请评论/帮助我改进!
我将整个 Singleton 类复制到一个可以称为 AbstractController 的类中。我两次派生这个类。第一个 child 是单例,采用父类的整体处理。第二个 child 是我的应用程序部分的 Controller ,具有自己重新定义的处理方式。
谢谢!
最佳答案
事实是,单例和继承不能很好地结合在一起。
是的,是的,Singleton 爱好者和 GoF 崇拜者会为此倾倒我,说“好吧,如果你让你的构造函数受到保护......”和“你不必在类上有 getInstance
方法,你可以把它...”,但它们只是证明了我的观点。单例必须跳过许多障碍才能成为单例和基类。
但只是为了回答这个问题,假设我们有一个单例基类。它甚至可以在某种程度上通过继承来加强其单一性。 (当构造函数不再是私有(private)的时,它会做少数可以工作的事情之一:如果另一个 Base
已经存在,它会抛出异常。)假设我们还有一个类 Derived
。继承自 Base
.因为我们允许继承,所以我们也可以说 Base
可以有任意数量的其他子类。 ,可能会或可能不会从 Derived
继承.
但是有一个问题——你要么已经遇到了,要么很快就会遇到这个问题。如果我们拨打 Base::getInstance
在没有构造对象的情况下,我们将得到一个空指针。我们想取回任何存在的单例对象(它可能是 Base
,和/或 Derived
,和/或 Other
)。但是要做到这一点并且仍然遵守所有规则是很困难的,因为只有几种方法可以做到——而且所有方法都有一些缺点。
Base
并将其归还。螺丝 Derived
和 Other
.最终结果:Base::getInstance()
总是准确返回 Base
. child 类从来没有玩过。有点违背目的,IMO。 getInstance
我们自己的派生类,并让调用者说 Derived::getInstance()
如果他们特别想要一个 Derived
.这显着增加了耦合(因为调用者现在必须知道专门请求一个 Derived
,并最终将自己绑定(bind)到该实现)。 initInstance
,因为我们并不特别关心它得到了什么——我们只是调用它以便它创建一个新的 Derived
并将其设置为 One真实实例。)所以(除非有任何未知的奇怪之处),它有点像这样......
class Base {
static Base * theOneTrueInstance;
public:
static Base & getInstance() {
if (!theOneTrueInstance) initInstance();
return *theOneTrueInstance;
}
static void initInstance() { new Base; }
protected:
Base() {
if (theOneTrueInstance) throw std::logic_error("Instance already exists");
theOneTrueInstance = this;
}
virtual ~Base() { } // so random strangers can't delete me
};
Base* Base::theOneTrueInstance = 0;
class Derived : public Base {
public:
static void initInstance() {
new Derived; // Derived() calls Base(), which sets this as "the instance"
}
protected:
Derived() { } // so we can't be instantiated by outsiders
~Derived() { } // so random strangers can't delete me
};
在你的初始化代码中,你说
Base::initInstance();
或 Derived::initInstance();
,取决于您希望单例是哪种类型。您必须从 Base::getInstance()
转换返回值为了使用任何 Derived
- 特定的函数,当然,但无需强制转换,您可以使用 Base
定义的任何函数,包括被 Derived
覆盖的虚函数.请注意,这种做法也有其自身的许多缺点:
initInstance
IMO 并不真正属于每个人都可以看到的 API 的方法。如果你愿意,你可以制作 initInstance
私有(private)并让你的 init 函数是一个 friend
,但是您的类(class)正在对自身之外的代码做出假设,而耦合的事情又开始发挥作用。 还要注意,上面的代码根本不是线程安全的。如果你需要那个,你就靠你自己了。
说真的,不那么痛苦的方法是忘记尝试强制单例。确保只有一个实例的最简单的方法是只创建一个。如果需要多处使用,可以考虑依赖注入(inject)。 (非框架版本相当于“将对象传递给需要它的东西”。:P)我去设计了上面的东西只是为了尝试证明自己在单例和继承方面是错误的,只是向自己重申了多么邪恶组合是。我不建议在真正的代码中真正做到这一点。
关于C++ 单例类——继承的好习惯,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9370749/