设计/应用程序结构和关注点分离问题
所以这个问题是这里的后续问题(如何处理多个事件参数)。这个问题让我开始思考这个问题,但它的不同之处足以保证它有自己的线索。
我正在创建一个游戏(出于娱乐和学习目的),并且想知道我是否使用了良好的设计标准。我想我可能已经在关注点分离方面走上了OTT,或者只是把整个事情弄错了,但我希望情况并非如此。我重写它没有问题,因为我想学习“最佳实践”及其实际应用。
编辑
让我更多地解释一下这个游戏,它是基于 Jawbreaker 的,这是一个在许多手机上都能找到的游戏 (此处找到快速演示)。目标是选择成组的球,将其从比赛中移除并获得尽可能多的分数。
我正在尝试稍微扩展它,并使用不同类型的板,球以不同的方式移动,以及不同的球类型,球可能只是去它们被告知的地方,或者它们可能会沿途做一些事情。
下面是我创建的对象的结构,顶行显示 DLL 及其对象,第二行显示这些对象引用的内容:
(来源:ggpht.com)
这是我尝试进行UML:
alt text http://yuml.me/3279d2ac
单击此处链接到 UML 的完整页面,使其更大一点,希望更易于阅读
对象 DLL 包含基本的游戏中使用的物体,球和棋盘。它们不包含任何关于它们如何对情况做出反应/反应的逻辑(球确实实现了 CompareTo 和 Equals 方法)。 IBall 可能有 X 种实现(对于 IBoard 也是如此,尽管我想象不会有那么多)。
InstanceManager DLL 被用作创建对象的一种方式,不确定这是否 100% 需要,它是否可以放入对象 DLL 中。工厂是静态类,具有各种重载方法来创建 IBall 对象。 BallFactory 可以采用 BallType 枚举、Drawing.Color 对象等。BoardFactory 非常相似。 Jawbreaker 是一个单例对象,它处理诸如持有经常使用的随机对象和一些 GameConfiguration 数据之类的事情(与本主题不太相关)。
引擎 DLL 是大部分工作发生的地方。 LogicFactories 采用 BallType 和 BoardType 对象来创建相关的逻辑对象。逻辑对象用于控制 IBall 和 IBoard 对象的工作方式。 BallLogic 告诉球当其事件发生时它可以做什么。例如,当选择一个球时,球逻辑上会调用一个方法,表示已选择棋盘 Y 上的球 X。然后,球可以执行其类型的球应该/可以执行的任何操作。 BoardLogic 非常相似,处理董事会的行为方式。
引擎对象是另一个单例,它是 GUI 与整个游戏交互的方式。 GUI 不会直接实例化任何其他对象。
因此,总而言之,IBall 和 IBoard 类仅保存有关它们的数据,而 Logic 类则处理所有功能。
我想知道的是:
1)这是一种明智的做法吗?
2)(一般来说)逻辑应该与对象/数据分开吗?
3)我在关注点分离方面是否走得太远了?
4)关于设计/结构的任何其他评论
编辑
我使用几个单例的部分原因是为了简单地访问一个地方的数据而无需一直保留对象,也只是因为它是一款单一游戏,不会扩展到高端或跨多台机器。我确实知道它们不是很好,也不是我经常使用的东西,但感谢您的评论。
感谢您的想法和反馈。
So this question is a sort of follow on from here (how to deal with multiple event args). That question led me onto thinking about this but is different enough to warrant its own thread.
I am creating a game (for fun and learning purpose) and would like to know if I am using good standards for design or not. I think I might have gone OTT on separation of concerns, or just got the whole thing wrong, but I am hoping that isn’t the case. I have no problem rewriting it as I want to learn “best practices” and the practical application of them.
EDIT
Let me explain a little more about the game, It is based on Jawbreaker a game found on many mobile phones (quick demo found here). Objective is to select balls that are in groups to remove them from play and score as many points as possible.
I am trying to expand it a little and have different types of boards, balls move in different ways, and different ball types, the balls may just go where they are told to, or they might do something along the way.
So Here is the structure of the objects I have created, top row shows the DLL’s with their Objects, second row shows what those objects reference:
(source: ggpht.com)
Here is my attempt at doing the UML:
alt text http://yuml.me/3279d2ac
Click here to link to full page for UML, makes it a little larger and hopefully easier to read
The Objects DLL holds the basic objects used in the game, Balls and a Board. They do not contain any logic about how they act / react to situations (the ball does implement the CompareTo and the Equals methods). There could be X number of implementations of IBall (and the same for IBoard, although there will not be that many I imagine).
The InstanceManager DLL is used as a way of Creating Objects, not sure this was 100% needed it could have gone in the Objects DLL. The Factories are static classes that have various overloaded methods to create IBall objects. The BallFactory can take the BallType Enum, A Drawing.Color object, etc. The BoardFactory is very similar. Jawbreaker is a singleton object that deals with things like holding a Random Object as it is used very regularly and some GameConfiguration data, (not so relevant to this topic).
The Engine DLL is where most of the work happens. The LogicFactories take the BallType and BoardType objects to create the relevant Logic Objects. The logic objects are used to control how an IBall and IBoard object works. The BallLogic tells the ball what it can do when its events fire. For example when a Ball is selected a method is called on the Ball Logic saying Ball X on Board Y has been selected. The Ball can then do whatever its type of ball should / could do. The BoardLogic is very similar and deals with how the board acts.
The Engine Object is another singleton and is how the GUI would interact with the whole game. The GUI would not instantiate any of the other objects directly.
So to summarize the IBall and IBoard classes hold just the data about them, the Logic classes deal with all the functionality.
What I would like to know is:
1) Is this a sensible approach?
2) Should (in general) Logic be separate from the Objects / data?
3) Have I gone too far with the separation of concerns?
4) Any other comments about the design / structure
EDIT
The reason I have used a couple of singletons is partly for simplicity of accessing the data in one place without keeping hold of objects all the time and also just because it is a single game and will not be scaled to either high end or across multiple machines. I do understand that they are not great and its not something I use regularly, but appreciate the comments.
Thank you for your thoughts and feedback.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您的故障看起来正在朝着正确的方向发展。但是我不确定您是否试图将数据与逻辑的表示形式分开。看一下 MVC、观察者和策略模式。
请记住这一点:当您设计松散耦合的应用程序时,需要进行权衡。您可以轻松地扩展应用程序,但会降低性能和内存使用量。另外,我不想这么说,但不要创建不必要的接口。如果您知道现在或稍后要扩展对象的功能,则创建接口,如果在实现它之前不考虑它。
Your breakdown looks like it is going in the right direction. However I'm not sure you are trying to seperatate the data from the presentation from the logic. Take a look at the MVC, observer, and strategy patterns.
Keep this in mind: There is a tradeoff for when you are designing an application to be loosely couple. You get the ability to extend the application with less effort, however you do loose performance and memory usage. Also, I hate to say this, but don't create unnecessary interfaces. If you know you are going to extend the functionality of the object now or later on create the interface, if not think about it before you implement it.
如果不更好地了解您想要做什么,就很难批评您的设计。我知道它涉及球和板,但除此之外,我不知道。
尽量避免单例。是的,我知道 GoF 书中列出了它,并且我知道这是一种常见模式,但根据我的经验,它最终成为一种反模式。
您提到 IBall 和 IBoard 没有任何关于它们如何交互的逻辑。这是否意味着他们没有任何方法?他们的方法只是私有数据的 getter 和 setter 吗?如果 IBall 和 IBoard 只是结构(或它们的等价物),那么它们不需要是接口。您能否详细说明您可以发送到 IBall 和 IBoard 的消息类型?
有时。如果您有普通的旧数据,那么将逻辑与普通数据分开就可以了。当然,那么你就不再是在进行面向对象编程了——你正在编写过程代码。我认为您正在努力摆脱不将任何行为硬编码到 Ball 中的愿望。也许其他一些实体应该负责决定球如何交互,但您仍然有空间让球参与决策。这就是策略模式(以及其他模式)发挥作用的地方。想想现实世界中的事物是如何运作的 - 物理球和物理板如何协调它们的交互?如果他们互相交谈,他们会说什么?看看是否可以在代码中实现它。
最后,谈谈设计。我认为想要“让设计正确”是件好事。另一方面,这是成为架构宇航员的道路。考虑一下您的代码库当前存在哪些问题。
设计并不存在于真空中——它是由世界的现状决定的。只要看看人们提出的一些疯狂的手机概念艺术,您就会看到糟糕设计的好例子。手机受到当前技术的限制,无视现实世界限制的设计只不过是梦想。软件领域也是如此。注意代码告诉你它需要什么,你就会做得很好。
你编写(和完成)的软件越多,你拥有的经验就越多,你的审美感就会越好。不要让“做错事”的恐惧阻止您完成软件。坚持下去,让它发挥作用,然后看看你从这个过程中学到了什么。
It's hard to critique your design without a better idea of what you are trying to do. I know that it involves balls and boards, but other than that, I have no idea.
Try to avoid singletons as much as possible. Yes, I know that the GoF book lists it, and I know that it is a common pattern, but in my experience it ends up becoming an anti-pattern.
You mention that IBall and IBoard don't have any logic about how they interact. Does that imply that they don't have any methods? Are their methods just getters and setters for their private data? If IBall and IBoard are just structs (or their equivalent), then they don't need to be interfaces. Could you flesh out your description to talk about the kinds of messages that you can send to an IBall and to an IBoard?
Sometimes. If you have plain old data, then separating the logic from the plain data is fine. Of course, then you're not doing OOP anymore - you are writing procedural code. I think you're struggling with the desire to not hard-code any behavior into Ball. Perhaps some other entity should be responsible for deciding how balls interact, but you still have room for Ball to participate in the decision. This is where the strategy pattern (among other patterns) would come into play. Think about how things work in the real world - how would a physical ball and a physical board coordinate their interactions? If they talked to each other, what would they say? See if you can implement that in your code.
Finally, a word about design. I think it's good to want to "get the design right." On the other hand, that's the path towards becoming an architecture astronaut. Consider what problems you currently have with your code base.
Design doesn't exist in a vacuum - it is informed by the current state of the world. Just look at some of the crazy cell phone concept art that people come up with, and you'll see good examples of bad design. Cell phones are limited by current technology, and designs that ignore the restrictions of the real world are nothing more than dreams. The same is true in software. Pay attention to what the code is telling you it needs, and you'll do fine.
The more software you write (and finish), the more experience you will have, and the better your sense of aesthetic will become. Don't let fear of "doing it wrong" stop you from finishing your software. Persevere, make it work, and then see what you learned from the process.