Good programming style when handling multiple objects
Asked Answered
D

5

10

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 class 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?

Diverge answered 23/4, 2010 at 15:23 Comment(0)
E
7

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.

Erect answered 23/4, 2010 at 15:29 Comment(2)
This isn't really a Singleton, since the access is done through static interfaces. But it does suffer from many of the same disadvantages, so I'll refrain from marking down.Zuzana
Thanks Matt. I've changed the code so that only the main logic file creates and has direct access to the GameAssets object. This is then passed to other classes/methods. It still doesn't seem right though, perhaps because my logic is top down in design. I end up passing this object all over the place. But perhaps that's another question in itself - how does one split game logic into small bits? I'll have to think about it.Diverge
D
2

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.

Dewhurst answered 23/4, 2010 at 15:31 Comment(5)
The question about who should flange B's x is more complicated one than you've indicated, and at the heart of good design. It is not a given that B should flange its own data. For example: who wants the result? And does class C flange its own data in the same way?Zuzana
You're absolutely right, but I still think it's a helpful trick to think about encapsulation.Dewhurst
+1... Some people have been much further than that and are arguing that the very presence of getters proves that you're not doing OO. Nearly everybody, everywhere is using getters hence people will violently disagree (because people don't like when they feel that the "best practice" they're following since 15 years are "attacked"). Yet it is a fascinating thought. When I read about it, instead of thinking "silly you, I use getters all the time, I know getters are all the sh!t" I thought "oh, that's interesting" : )Lime
@WizardOfOdds I think sometimes it's the other way around, and your class should have a getter but not a setter. In other words, I think OOD is about restricting manipulation of data to a single class of responsibility. Not necessarily restricting write or restricting read, but restricting one or both of these. Concrete example: a class url that parses a long URL string makes perfect sense (to me) to have getters for the various components of the string, but it would be silly to have setters for any of them.Assyriology
@WilhelmTell of Purple-Magenta: (nice nick) oh I hear your regarding setters: I'm personally nearly always modeling everything using only Java interfaces and I'm typically never putting setters there (I'm also high on "OO over immutable objects" so that's another reason I'm not using many setters, if any). But I think the point about having no getters is that instead of "getting" some value you let the objects interacts between themselves: you pass an A to a B and the B does what it needs to do to the A and/or it gives you back a C. That's how I understood it when I read it : )Lime
D
0

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.

Dragonfly answered 23/4, 2010 at 15:31 Comment(0)
C
0

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.

Culver answered 23/4, 2010 at 15:31 Comment(0)
A
0

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.

Alliaceous answered 23/4, 2010 at 22:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.