I've been programming a software version of a board game. Thus far I have written the classes which will correspond to physical objects on the game board. I'm well into writing the program logic, however I've found that many of the logic classes require access to the same objects.
At first I was passing the appropriate objects to methods as they were called, but this was getting very tedious, particularly when the methods required many objects to perform their tasks. To solve this, I created a cla开发者_运维知识库ss which initialises and stores all the objects I need. This allows me to access an object from any class by calling Assets.dice(), for example.
But now that I've thought about it, this doesn't seem right. This is why I'm here, I fear that I've created some sort of god class. Is this fear unfounded, or have I created a recipe for disaster?
You've basically bumped into the singleton pattern. For the most part, it's a bad pattern. By allowing any part of your app to access essentially global data like this at just about any time, you end up with a spaghetti mess of code that's hard to maintain, debug, and most of all, test.
I think it is better to create a "Context", that contains the current dice, pieces, etc etc, and pass the context around as needed to methods/classes that need to use it. This is much cleaner, although yes it is a pain to have to pass it everywhere. But you gain the advantage that you can keep track of who is accessing the Context when, and also you can create mock Contexts for testing purposes. If you pass the context into a high level object, and it must pass it down to its sub components, you know from start to finish what the context is and where it came from.
Also, ideally, it's nice to make the Context immutable. That may not be possible. But if for each given turn if you can create a new Context that captures the current state and is immutable, you reduce even more surprise from your app.
It sounds like you're asking about a general philosophy of Object-Oriented Programming. In general, you'll find that modelling real-world objects to classes doesn't always make the best sense for your code.
One guideline that's helped me figure this stuff out is a dialogue between two anthropomorphic classes (if someone knows the original source of this quote, I'd appreciate a link!):
Class A says to Class B: "Give me the value of x."
Class B: "Why do you want the value of x?"
Class A: "So I can flange it."
Class B: "Ask me, and I'll flange it for you."
This helps drive home the point that a class is meant to encapsulate the data and perform manipulations on it. In general, this is a parable that's helped me organize my code better.
Another thing you may want to look at are some common Object-Oriented Design Patterns. Something like game dice might make more sense as a Singleton, since you don't need more than one instance of it.
If you'd like a good introduction to Design Patterns, I'd recommend picking up the excellent Head First Design Patterns book.
Is it really a "god" class, or merely a "context" which is a "container" for all those interrelated object instances of one game which is then passed alont to the different method calls (so that you have just one argument)? The latter is pretty common and I see nothing wrong with it, but in this case the container itself has no real functionality.
Thanks for bringing this question. I have been wondering about the same issue for sometime now. Passing objects across method and keep parameters limited need creation of classes which can hold all these objects.
Still I think the design is not bad as you do need a Domain class which passes across multiple layers. If this Domain class is not carry necessary objects and not performing any logic it should be fine.
Having that sort of a context class which has access to everything is very similar to having global variables. Same drawbacks apply. A global variable can be read and changed by any method. This couples everything that uses the global variable to each other. Coupling is bad because when things are coupled, a change in one object can cause something in the other object. When the degree of coupling increases it becomes very hard to manage the complexity (a butterfly flapping it's wings in our player class can cause an exception in your dice class). Change and additional development becomes harder. Hard to detect and obscure bugs become unavoidable.
So, for example, one day while testing you application you might notice that your dice object is acting weird. You call dice.roll() and see the it returns 1 sometimes. But that is not possible in this instance because your player is rolling two of them. You debug and somehow notice that the numberOfDice property got changed to 1 at some point. With a context method like yours it is not going to be easy to find who changed the numberOfDice to 1 because everybody has access to dice through your context object. You get the point?
So what is the solution? One of the metaphors that I like for OO programming is divide and conquer. You need to find behaviors, processes, objects that can be isolated from each other. You need to divide your problem into manageable pieces that can be isolated from the rest of the stuff going on in your application.
Learning how to do this is of course not easy and requires a lot of studying, reading, thinking, discussing and of course coding.
精彩评论