design-patterns - 好的OO设计-Singleton设计模式

标签 design-patterns singleton

突然之间,我有点OO危机。在过去的几年中,我充分利用了Singleton对象。我在很多地方都用过它们。

例如,在设计MVC Java应用程序时,我将创建一个Singleton'SystemRegistry'类来存储模型和视图类(我只在简单的应用程序上工作,从来没有出现过多个视图的需求)。

当我创建模型并查看对象(不是单例对象,只是普通对象)时,我将执行以下操作:

SystemRegistry.getInstance().setModel(model);


在我的控制器类(用于不同GUI项目的很多事件处理程序)中,可以按如下方式访问视图或模型:

SystemRegistry.getInstance().getView();


我永远不会在应用程序的模型部分中使用SystemRegistry类,但有时会在我看来使用它来访问(但很少(如果有,则要修改))来自模型的信息。

从我阅读的内容(尤其是Steve Yegge's article)来看,这似乎是设计应用程序的一种糟糕方法。关于构建代码的更好方法的任何想法。

另外,我如何设计类的另一个方面(可能与单例无关或无关)是使用“经理类型”类。一个示例是我用C ++创建的(非常简单)的基于OpenGL的游戏引擎。

主要类是GameEngine。过度争用类存储了许多Manager,并处理了主循环,而其他则没有。此类中存储的一些管理器如下:
ObjectManager,RenderingManager,LightingManager,EventManager(包括输入),HUDManager,FrameRateManager,WindowManager等。可能还有更多。

基本上,这些类处理游戏引擎的不同方面。名称非常简单明了,因此您应该能够很好地了解它们的用法。

现在,这是一个可重用的基础,我可以在不同的项目中使用它,并且需要对其进行理想的更改。

在每个新游戏中,我都将GameEngine的实例创建为全类变量(大多数游戏逻辑存储在单个类中),并设置不同的管理器(例如,加载窗口坐标或照明文件中的详细信息,设置FPS等)。要在ObjectManager中注册一个对象,我将执行以下操作:

Player player = new Player();
gameEngine.getObjectManager().addObject(player);


现在,此对象将存储在ObjectManager类的向量中,并在GameEngine在每帧中调用ObjectManager drawObjects()方法时绘制。

在关于Singletons的那篇文章之后,我现在可能有点偏执(也许没有足够的时间来思考),但是我开始进行第二次猜测,想知道我设计GameEngine的方式是否正确( (因为缺少更好的词),而不仅仅是陷入Singleton模式所共有的陷阱。

对我的帖子的任何评论将不胜感激。

编辑:感谢您的答案。我非常感谢他们。如果可能的话,我希望有人可以给我一些有关上面发布的两个项目方案的提示。如何避免使用Singletons / Managers?

对于第一个,DI是正确的答案吗?我是否应该授予视图访问模型的权限(这可能更多是MVC响应)?该视图是否将从实现接口中受益(以便可以插入多个不同的视图)?

在第二种情况下,该应用程序还可以如何构造?抱怨只是使用Manager类而不是更具体的名称吗?还是在某些情况下可以进一步细分类(例如ObjectHolder,ObjectDrawer,ObjectUpdater)?

最佳答案

好吧,你为什么写单身人士?如果您了解单身性膀胱炎出了什么问题,就知道将来要注意什么。

您为何一秒钟认为单身人士是解决任何问题的好方法?因为一本书这么说?不要盲目相信书。书籍需要像其他任何人一样证明其主张。如果我告诉您,如果您将显示器倒置,您的代码看起来会更好,举证责任就在我身上。即使我是某种代码的上帝。即使全世界每天有数百万开发人员敬拜我,但如果我无法证明我的建议仍然不值一提,并且如果我不能让您选择“行,那是有道理的。我理解您为什么建议这样做,并且我想不出更好的方法来做到这一点。”

对于某些设计模式书也是如此。因此,他们写道:“设计模式很棒”,“单例是设计模式,因此它也很棒”。所以呢?他们可以证明这一主张的合理性吗(不,至少在单例情况下不能如此),因此请忽略它。

如果其他人建议您使用单例,则逻辑是相同的:这些人是否真的提出了一个很好的论据,说明为什么这是个好主意?

而且,如果您自己提出了辩解,那么今天您能看到问题出在哪里吗?您忘记了什么?

避免将来犯太多错误的唯一方法是从已经犯的错误中学习。单身人士或经理阶层如何因您的防守而滑落?当您第一次了解它们时,您应该注意什么?或对此事,稍后,当您在代码中自由使用它们时。那里有什么警告信号应该使您感到奇怪:“这些单身人士真的是正确的道路吗?”作为程序员,您必须相信自己的想法。您必须相信自己会做出明智的决定。唯一的方法就是在做出错误决定时汲取教训。因为我们都经常这样做。我们能做的最好的事情就是确保我们不会重复做出同样的错误决定。

对于经理班级,他们的问题是每个班级应该完全承担一项责任。 “经理阶层”的职责是什么?它....呃....管理的东西。如果您无法定义简洁的职责范围,则该类是错误的。这些对象究竟需要管理什么?管理它们意味着什么?标准库已经提供了用于存储一组对象的容器类。

然后,您需要一些代码来负责绘制存储在其中的所有对象。但这不必是与存储对象相同的对象。

还有什么需要管理的?找出这些对象的“管理”是什么意思,然后知道进行管理需要哪些类。它很可能不是单个的整体任务,而是几种不同类型的职责,应将其划分为不同的类。

关于单例,我不会重复我之前多次说过的话,所以这里是link

最后一条建议:

螺丝OOP。真。好的代码不是OOP的同义词。有时候,上课是工作的好工具。有时,它们只是使所有内容杂乱无章,并将最简单的代码隐藏在无穷无尽的抽象层后面。

研究其他范例。如果您使用的是C ++,则需要了解通用编程。 STL和Boost是很好的示例,说明了如何利用通用编程编写代码来解决许多问题,这些问题比等效的OOP代码更干净,更好,更有效。

无论使用哪种语言,都可以从函数式编程中学到很多宝贵的经验。

有时,普通的旧过程编程只是您需要的简单而漂亮的工具。

“好的设计”的关键是不要尝试“好的OO设计”。如果这样做,则将自己锁定在该范例中。您不妨尝试找到一种用锤子建造房屋的好方法。我并不是说这是不可能的,但是如果您还允许自己使用其他工具,则有很多更好的方法来建造更好的房屋。

至于您的编辑:


对于第一个,DI是正确的答案吗?我是否应该授予视图访问模型的权限(这可能更多是MVC响应)?该视图是否将从实现接口中受益(以便可以插入多个不同的视图)?


DI将是避免单例的一种选择。但是不要忘了老式的低技术选择:只需将引用手动传递给需要了解“前”单身人士的对象。

渲染器需要了解游戏对象列表。 (或者真的吗?也许只有一个方法需要提供对象列表。也许renderer类本身不需要它),因此应该为renderer的构造函数提供对该列表的引用。真的就是全部。当X需要访问Y时,可以在函数参数的构造函数中将对Y的引用传递给它。与DI相比,这种方法的好处在于,您必须使依赖关系变得明确。您知道渲染器知道可渲染对象的列表,因为您可以看到将引用传递给构造函数。而且您必须将这种依赖关系写出来,从而给您提供了一个很好的机会来停止并询问“这是否必要”吗?而且您有消除依赖的动机:这意味着更少的输入!

使用单例或DI时最终遇到的许多依赖都是不必要的。这两个工具都使在不同模块之间传播依赖关系变得轻松而轻松,这就是您要做的。然后,您最终得到一个设计,您的渲染器知道键盘输入,输入处理程序必须知道如何保存游戏。

DI的另一个可能的缺点是一个简单的技术问题。并非所有语言都具有良好的DI库。某种语言几乎不可能编写健壮且通用的DI库。


在第二种情况下,该应用程序还可以如何构造?抱怨只是使用Manager类而不是更具体的名称吗?还是在某些情况下可以进一步细分类(例如ObjectHolder,ObjectDrawer,ObjectUpdater)?


我认为只是重命名它们是一个好的开始,是的。就像我上面说的,“管理”对象是什么意思?如果我管理这些对象,我应该怎么做?
您提到的三个班级听起来不错。当然,当您深入研究这些类的设计时,您可能会想知道为什么甚至需要ObjectHolder。这不是标准库容器/集合类的功能吗?您是否需要专门的类来“保存对象”,还是仅使用List<GameObject>就可以摆脱困境?

因此,我认为您的选择实际上都归结为同一件事。如果您可以给该类一个更具体的名称,则可能不需要将其拆分为多个较小的类。但是,如果您无法想到一个可以清楚地表明该类应该做什么的名称,则可能需要将其分解为多个类。

想象一下,您将代码搁置了半年。回到它,您是否会知道“ ObjectManager”类的目的是什么?可能不是,但是您将非常了解“ ObjectRenderer”的用途。它渲染对象。

关于design-patterns - 好的OO设计-Singleton设计模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4059077/

相关文章:

Java:对外部构造对象的依赖注入(inject)?

java - 多个 WAR 文件的同一个 tomcat 单例

Java单例模式在多线程中输出两个相同的对象?

c# - 工厂模式能解决我的问题吗?

design-patterns - 访问者模式和递归

css - 表格行上的背景图案重复

actionscript-3 - MVC - 理论问题

java - 通过枚举实现的单例在模块化趋势中仍然值得(即 Java 9+ 模块化和 Jigsaw 项目)

javascript - 使用 Javascript 命名空间/单例不会接受传递给成员方法的参数

java - 单例类与静态方法和字段?