The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

A somewhat vague question on object design

I keep running into a problem in a lot of my more complex programs, and I'm wondering if anyone can share some insights. (These are web applications, FYI.)

Often, I'll have one or two objects that contain some important data. Then I might have an object that's responsible for updating the data, and a couple objects that read different parts of the data. I'll usually pass the data object to these reader/writer objects' constructors, so they can store a reference to it, and let them have at it.

The problem is, the data object starts to become like a global variable, with all the usual problems associated with globals. For example, it gets hard to predict what values it will contain at any given time. And the order in which the reader/writer objects get to operate on it starts to become very important.

One thing I've tried is, instead of passing the data object to the constructors, just passing it to the methods where it's used. But this tends to make things more confusing. The state of the reader/writer objects is still dependent on the data object, so it makes more sense that they contain a reference to it. Plus it gets annoying when you have to pass it to every method.

Does anyone have any guidelines that might help avoid this type of problem? I hope I'm explaining myself (somewhat) clearly.
Monday, March 31, 2008
This is a common problem, and it usually indicates that you're not partitioning things properly. In general, an object should fully encapsulate some part of the functionality of the system. This means it should have (or have access to) all of the data and operations that it needs to do some specific set of related tasks.

If you have "data objects" that exist only to be passed back and forth to the objects that do the "real work", then you probably need to think about how the various classes can be split up and recombined to reduce the amount of data that needs to be shared that way.

I wrote a little bit about this on my blog once:
Mark Bessey Send private email
Monday, March 31, 2008
Another option is to reverse the dependancy relationship: pass the clients to the 'global' object and have it call them to notify them when things have changed or need to be done. This is commonly called delegation. The delegate objects implement a well known interface that is called by the central model object (the 'global' in your parlance) when the model needs things done (things the model doesn't really know HOW to do, but it knows they need to be done).

Delegation allows you to restrict the communication of model details to the clients: all details MUST pass through the public interface, which is usually implemented as specific method names and formal method parameters. The advantage is that, in order to extend the external interpretation of the model data, you make changes in the delegate interface used by the model (all of which is coded in the model) rather than scattering code all over the clients. You also have the model determining when things happen, which makes things much more predictable.

Depending on the language you are using building delegate relationships could be dead simple (any dynamic language and languages using duck-typing) or rather difficult (early bound and static bound languages like C++).
Jeffrey Dutky Send private email
Monday, March 31, 2008
"just passing it to the methods where it's used. But this tends to make things more confusing. The state of the reader/writer objects is still dependent on the data object, so it makes more sense that they contain a reference to it. Plus it gets annoying when you have to pass it to every method."
Do this. If it gets annoying enough you redesign your objects until the annoyance goes away(hopefully in a good way.) and +1 to Mr. Bessy.
Monday, March 31, 2008
Thanks for the ideas so far.

Mark, I looked at your blog entry. You distinguish between looking at an object as a combination of data and methods, and looking at it as a model of some part of the problem domain. I think this is part of my trouble --- my classes are modeling parts of the problem domain, but the data is shared across the whole problem domain.

For various reasons, I don't think it makes sense to break up my "data" class into multiple parts. There are several places where I need to be able to use all of the data at once.

For example, in my current project the data object represents all the fields in a multi-page form. I have one object that's responsible for the logic of moving between pages, and deciding which pages you can get to from which other pages.

Now, this page-logic object sometimes needs read access to the data object, because its values can affect the available pages. It also needs read/write access to the current page number, which is also in the data object.

So currently the page-logic object has its own reference to the data object. I like this because there's no duplication of data. If the page-logic object needs to change the page number, it just writes it to the data object. If it had its own copy of the page number, I would have to keep it in sync with the data object's copy. But maybe this would give me better separation. I've done it both ways, and haven't been crazy about either one. :(
Tuesday, April 01, 2008
Look up Model-View-Controller (MVC)

From your description you have the data (Model), the multi page form (View) and the page logic (controller) and just need to get them working together properly

Tuesday, April 01, 2008
You could use a singleton to avoid duplication of data and ensure that an object contains a persistent dataset across all concerns
Richard Corsale Send private email
Tuesday, April 01, 2008
One approach to manage application state, is to avoid mutable objects. If you set your mind to it, you can turn many stateful objects into immutable objects, thereby making them much easier to deal with. For example, instead of letting getters/setters manipulate the state of an object, they might return a clone of the object with the changed state. When you do this, you'll find that it becomes a lot easier to figure out which code does what. You still ultimately need some stateful gateway-objects, that you can pass your immutable objects to, but deferring it unto the last possible moment, makes things much clearer.
Troels Knak-Nielsen Send private email
Wednesday, April 02, 2008

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
Powered by FogBugz