本文介绍了良好的OO设计 - 单身设计模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

突然间我发生了一场OO危机。在过去的几年中,我已经很好地使用了Singleton对象。我在许多地方使用它们。



例如,在设计MVC Java应用程序时,我将创建一个Singleton'SystemRegistry'类来存储模型和查看类(我只在简单的应用程序上工作,并且不需要多个视图)。



当我创建我的模型并查看对象(不是单身,只是正常的对象),我会做一些像:

  SystemRegistry.getInstance()。setModel(model); 

在我的控制器类(对于不同的GUI项目几乎是事件处理程序),我会得到访问视图或模型如下:

  SystemRegistry.getInstance()。getView(); 

我永远不会在我的应用程序的模型部分使用SystemRegistry类,但有时会使用我认为从模型中访问(但很少,如果有的话修改)信息。



从我看过的(特别是),这似乎是一种很差的设计我的应用程序的方法。任何关于更好地构建我的代码的方法的想法。



另外,我设计类的另一个方面可能与Singletons有关,也可能与之不相关的经理类类。一个例子是我在C ++中创建的一个(非常简单的)基于OpenGL的游戏引擎。



主类是GameEngine。存储了一大堆管理器并处理主循环,而不是那些超级类。存储在这个类中的一些管理器是这样的:
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模式共享的同样的陷阱。 p>

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



编辑:感谢答案。我很感激他们如果可能的话,如果有人可以给我一些关于上面发布的两个项目场景的提示,我会很乐意。我如何避免使用单身人士/经理?



第一个,DI是否正确答复?我应该甚至给视图访问模型(这可能是更多的MVC响应)?该视图是否可以从实现一个界面中获益(以便可以插入多个不同的视图)?



在第二种情况下,还可以如何对应用程序进行结构化?难道只是使用Manager类而不是更具体的名称?或者在某些情况下,类可以进一步细分(例如ObjectHolder,ObjectDrawer,ObjectUpdater)?

解决方案



为什么你相信单身人士是一个很好的解决方案任何类型的问题?因为一本书这么说?不要盲目信任书。书籍需要像其他人一样证明自己的说法。如果我告诉你你的代码看起来好多了,如果你把你的显示器颠倒了,举证责任在我身上。即使如果我是某种代码神。即使世界上数以百万计的开发者每天都在崇拜我,如果我不能理解它,我的建议仍然是无价值的,如果我不能让你走好的,这是有道理的明白为什么你推荐这个,我不能想到一个更好的方式来做。



对于某种设计模式书也是如此。所以他们写道:设计模式很棒,单身是一种设计模式,因此也是非常棒的。所以呢?他们可以证明这个说法(不,不,至少不是在单身案件中),所以忽略它。



如果有人建议你使用单身人士,逻辑是一样的:这些人是否真的提出了一个很好的论点,为什么这是一个好主意?



如果你自己想出一个理由,今天看看有什么问题吗?你忘了考虑什么?



今后避免使太多错误的唯一方法是从错误中学习已经做好了单身人士还是经理人?当你第一次了解他们时,你应该注意些什么?或者在以后的时候,当你在你的代码中自由地使用它们时。有什么警告标志,那么你应该想知道这些单身人士真的是正确的出路吗?作为程序员,你必须相信自己的想法。你必须相信你会做出明智的决定。唯一的办法就是在做出错误的决定时学习。因为我们都做,太多了。我们可以做的最好的事情是确保我们不会再犯同样的错误决定。



关于经理类,他们的问题是每个班级都应该有一个责任。 经理人的职责是什么?它....呃....管理东西如果您无法定义简明扼要的责任范围,那么该类是错误的。究竟需要管理这些对象?它是什么意思来管理它们?标准库已经提供了用于存储一组对象的容器类。



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



还需要管理什么?找出这些对象意味着什么管理,然后知道你需要做什么类别的管理。很可能不是一个单一的任务,而是几种不同的责任,应该分成不同的类。



关于单身人士,我不会重复我的以前说过这么多次,所以这里是一个。



最后一条建议:



螺丝OOP。真。良好的代码与OOP不同。有时候课堂是一个很好的工具。有时,他们只是把所有东西都弄乱了,把最简单的代码埋在无尽的抽象层次之中。



查看其他范例。如果你在C ++中工作,你需要知道泛型编程。 STL和Boost是如何利用通用编程编写代码以解决许多比同等的OOP代码更干净,更好,更高效的问题的好例子。



而且无论语言如何,从功能性编程中都可以学到许多有价值的经验教训。



有时候,简单的旧程序编程只是你需要的漂亮和简单的工具。 / p>

好设计的关键是尝试好的OO设计。如果你这样做,你就把自己锁定在一个范例里。你也可以尝试找到一个使用锤子建造房子的好办法。我不是说这是不可能的,但是如果你还允许自己使用其他工具,还有更好的方法来建造更好的房子。



至于你的编辑:

DI将被一个选项来避免单身人士。但不要忘记老式的低科技选项:只需手动传递对需要了解您的前单身的对象的引用。



您的渲染器需要知道游戏对象列表。 (或者真的吗?也许它只有一个方法需要被赋予对象的列表,也许渲染器类本身不需要它)所以渲染器的构造函数应该被赋予该列表的引用。这真的是所有的。当X需要访问Y时,在函数参数的构造函数中将其引用为Y。与DI相比,这种方法的好处在于,使您的依赖性变得非常明确。您知道渲染器知道可渲染对象的列表,因为您可以看到传递给构造函数的引用。而你不得不写这个依赖关系,给你一个很好的机会停下来问这是必要的吗?你有消除依赖性的动机:这意味着更少的打字!



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



DI的另一个可能的缺点是一个简单的技术问题。并不是所有的语言都有很好的DI库可用。一些语言使得几乎不可能编写一个健壮和通用的DI库。

I想想只是重命名他们是一个好的开始,是的。像上面所说的那样,管理你的对象是什么意思?如果我管理这些对象,我期望做什么?
你提到的三个班级听起来像是一个很好的分工。当然,当您深入设计这些课程时,您可能会想知道为什么甚至需要一个 ObjectHolder 。标准库容器/集合类不是什么?你需要一个专门的持有对象类,或者你可以简单地使用列表< GameObject>



所以我认为你们的两个选择真的都是同样的事情。如果你可以给班级一个更具体的名字,那么你可能不需要把它分成多个较小的类。但是,如果你不能想到一个单一的名字来清楚这个课程应该做什么,那么它可能需要被分解成多个类。



想法ObjectManager类的目的是什么?可能不是,但是你会有一个很好的主意,ObjectRenderer是什么。它呈现对象。


All of a sudden I'm having a bit of an OO crisis. Over the last couple of years I've made quite good use of Singleton objects. I used them in many places.

For example, in designing an MVC Java application, I'd create a Singleton 'SystemRegistry' class to store the model and view classes (I've only worked on simple apps and the need for multiple views never came up).

When I create my model and view objects (which weren't singletons, just normal objects), I'd do something like:

SystemRegistry.getInstance().setModel(model);

In my controller classes (which were pretty much event handlers for different GUI items), I'd get access to the view or model as follows:

SystemRegistry.getInstance().getView();

I would never use the SystemRegistry class in the model portion of my app but would, at times, use it in my view to access (but rarely, if ever, to modify) information from the model.

From what I've read (notably Steve Yegge's article), this seems like a poor way to design my application. Any ideas as to better ways of structuring my code.

Also, another aspect of how I design classes, which may, or may not be related to Singletons, is the use of 'Manager-type' classes. An example is a (very simple) OpenGL-based game engine I created in C++.

The main class was GameEngine. It was the over-arcing class that stored a bunch of Managers and handled the main loop and what not. Some of the Managers stored in this class were things like:ObjectManager, RenderingManager, LightingManager, EventManager (includes input), HUDManager, FrameRateManager, WindowManager, etc. There were probably a few more as well.

Basically these classes handled the different aspects of the game engine. The names are pretty straightforward so you should be able to get a good idea of how they're used.

Now, this was meant to be a reusable base that I could use in different projects with the need to change it ideally.

In each new game, I would create an instance of the GameEngine as a class-wide variable (most of the game logic was stored in a single class) and set up the different managers (for example, loading the window co-ordinates or lighting details from a file, setting the FPS, etc). To register an object in the ObjectManager I would do something like:

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

This object will now be stored in a vector in the ObjectManager class and will be drawn when the GameEngine calls the ObjectManager drawObjects() method in each frame.

I might've gotten a bit paranoid now after that article on Singletons (and might not have had enough time to wrap my head around it), but I'm starting to second guess and wonder if the way I designed my GameEngine was proper (for lack of a better word) and didn't just fall into the same pitfalls shared by the Singleton pattern.

Any comments on my post would be much appreciated.

Edit: Thanks for the answers. I appreciate them greatly. If possible, I'd love if someone could give me some hints regarding the two project scenarios posted above. How could I have avoided the use of Singletons / Managers?

With the first one, would DI have been the correct response? Should I have even given the view access to the model (this is probably more of an MVC response)? Would the view benefit from implementing an interface (so that multiple different views can be plugged in)?

In the second case, how else could one have structured the application? Is the gripe simply the use of Manager classes as opposed to more specific names? Or is it that, in some cases, the classes can be further broken-down (e.g. ObjectHolder, ObjectDrawer, ObjectUpdater)?

解决方案

Well, why did you write singletons? If you understand what went wrong in your singletonitis, you know what to watch out for in the future.

Why did you believe for a second that singletons were a good solution to any kind of problem? Because a book said so? Don't blindly trust books. Books need to justify their claims just like anyone else. If I tell you your code looks much better if you turn your monitor upside down, the burden of proof is on me. Even if I'm some kind of code god. Even if millions of developers across the world worship me daily, my advice is still worth nothing if I can't justify it, and if I can't make you go "ok, that makes sense. I understand why you recommend this, and I can't think of a better way to do it".

And the same is true for a certain design patterns book. So they wrote that "design patterns are awesome", and that "the singleton is a design pattern, and therefore it, too, is awesome". So what? Can they justify this claim (no they can't, at least not in the singleton case), so ignore it.

If someone else suggested that you use a singleton, the logic is the same: did these people actually present a good argument for why it was a good idea?

And if you came up with a justification yourself, can you, today, see what was wrong with it? What did you forget to take into account?

The only way to avoid making too many mistakes in the future is to learn from the mistakes you've already made. How did singletons or manager classes slip by your defenses? What should you have been watching out for when you first learned about them? Or for that matter, later on, when you were using them liberally in your code. What warning signs were there that should have made you wonder "are these singletons really the right way to go?" As a programmer, you have to trust your own mind. You have to trust that you'll make sensible decisions. And the only way to do that is to learn from the bad decisions when you make them. Because we all do, all too often. The best we can do is to make sure we won't make the same bad decisions repeatedly.

Regarding manager classes, their problem is that every class should have exactly one responsibility. What is the responsibility of a "manager class"? It.... uh.... manages stuff. If you can't define a concise area of responsibility, then the class is wrong. What exactly needs managing about these objects? What does it mean to manage them? The standard library already provides container classes for storing a group of objects.

Then you need a bit of code with the responsibility for drawing all the objects stored there. But that doesn't have to be the same object as stores the objects.

What else needs managing? Find out what "managing" these objects means, and then you know what classes you need to do the managing. It's most likely not a single monolithic task, but several different kinds of responsibilities, which should be split out into different classes.

Regarding singletons, I won't repeat what I've said so many times before, so here's a link.

One final piece of advice:

Screw OOP. Really. Good code is not synonymous with OOP. Sometimes classes are a nice tool for the job. Sometimes, they just clutter everything up, and bury the simplest pieces of code behind endless layers of abstraction.

Look into other paradigms. If you're working in C++, you need to know about generic programming. The STL and Boost are nice examples of how to exploit generic programming to write code to solve many problems that is both cleaner, better and more efficient than the equivalent OOP code.

And regardless of language, there are many valuable lessons to be learned from functional programming.

And sometimes, plain old procedural programming is just the nice and simple tool you need.

The key to "good design" is not to attempt "good OO design". If you do that, you lock yourself into that one paradigm. You might as well try to find a good way to build a house using a hammer. I'm not saying it's not possible, but there are much better ways to build much better houses if you also allow yourself to use other tools.

As for your Edit:

DI would have been one option for avoiding singletons. But don't forget the old-fashioned low-tech option: simply manually pass a reference to the objects that need to know about your "former" singleton.

Your renderer needs to know about the list of game objects. (Or does it really? Maybe it just has one single method that needs to be given the list of objects. Maybe the renderer class in itself doesn't need it) So the renderer's constructor should be given a reference to that list. That's really all there is to it. When X needs access to Y, you pass it a reference to Y in a constructor of function parameter. The nice thing about this approach, compared to DI is that you make your dependencies painfully explicit. You know that the renderer knows about the list of renderable objects, because you can see the reference being passed to the constructor. And you had to write this dependency out, giving you a great opportunity to stop and ask "is this necessary"? And you have a motivation for eliminating dependencies: it means less typing!

Many of the dependencies you end up with when using singletons or DI are unnecessary. Both of these tools make it nice and easy to propagate dependencies between different modules, and so that's what you do. And then you end up with a design where your renderer knows about keyboard input, and the input handler has to know how to save the game.

Another possible shortcoming of DI is a simple question of technology. Not all languages have good DI libraries available. Some language make it near impossible to write a robust and generic DI library.

I think simply renaming them is a good start, yes. Like I said above, what does it mean to "manage" your objects? If I manage those objects, what am I expected to do?The three classes you mention sound like a good division. Of course, when you dig into designing those classes, you may wonder why you even need an ObjectHolder. Isn't that exactly what the standard library container/collection classes do? Do you need a dedicated class for "holding objects", or can you get away with simply using a List<GameObject>?

So I think both your options really come down to the same thing. If you can give the class a more specific name, then you probably don't need to split it out into multiple smaller classes. But if the you can't think of a single name that makes it clear what the class is supposed to do, then it probably needs to be broken down into multiple classes.

Imagine that you put your code away for half a year. When you come back to it, will you have any idea what the purpose of the "ObjectManager" class was? Probably not, but you'll have a pretty good idea what the "ObjectRenderer" is for. It renders objects.

这篇关于良好的OO设计 - 单身设计模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-14 15:41