aspose file tools*
The moose likes Java in General and the fly likes designing for small objects or Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » Java in General
Bookmark "designing for small objects or "breaking up a god-class"" Watch "designing for small objects or "breaking up a god-class"" New topic
Author

designing for small objects or "breaking up a god-class"

Derik Davenport
Ranch Hand

Joined: May 30, 2011
Posts: 57
Suppose my software needs to control a motor (just 2 wires in (power) and 2 wires out (position), and there are 25 tasks that motor needs to be able to do. When my program is done I tend to end up with a single motor object class with 25 "task" methods and another 10 or so "support" methods. It ends up with 500 to 1000 lines of code and it is a mess. When trying to break up a class that has become unwieldy I often find myself asking "what are the smaller objects" here. But I don't see lots of small composite objects, I only see one really large and complex object. I don't see how to break it up in to smaller more manageable objects. There is just "the motor", the variables that describe its state, and a very long list of tasks that all seem to be equally deserving to be lumped into the "motor" class.

So the question for this thread is: How are the techniques that other people use to identify a suitable set of smaller objects from a large and complex one? Is there some book with examples or excercizes on how to break complex things into smaller objects? I have a feeling that a good understanding of patterns would be useful here, but I am not convinced that is the entire answer here.


As for me, I have one approach that I have found somewhat useful. I start by examining all of the variables (fields) in the class. If there is a subset of all the fields, that can be combined with a subset of methods that exclusively use those same fields, then the fields and the methods can be cleanly removed from the original class and placed into a new class, an instance of which is now owned by the resulting "original" class. An example is order.

Consider a large class with (private) variables "a" through "m" and methods "A" through "X". Perhaps it is true that "a" and "b" are referenced only by the methods "A","B" and "C". If it is also true that methods A, B, and C do not reference any other variable, then I can create a new class that consists of a, b, and the methods A, B, and C. I can then delete those same methods and variables from the original class and replace them with a single reference to an instance of the new class.

That kind of break up is easy. But sometimes it just doesn't get you very far. If, for example, method C also uses variables "d" and "e" then I can't put method C into the new class containing variables a and b unless I am willing to pass method C pointers to "d" and "e" every time I need to call it. Either that, or I can make "d" and "e" public variables and/or create accessor functions for them. However I do it, the resulting new class is still "tied" to the original class. That is to say, I have not reduced the complexity of my large complex object, by finding smaller sub-objects. Rather I have just found a way to break my large complex object into more than one source file. I don't know that this helps readability or maintainability.

I am interested to learn how other programmers handle this.



Ivan Jozsef Balazs
Rancher

Joined: May 22, 2012
Posts: 877
    
    5
Derik Davenport wrote:I tend to end up with a single motor object class with 25 "task" methods and another 10 or so "support" methods.


You can group some aspects of its behavior into separate interfaces, and albeit one class implements everything, the API might get more handy.

Should we take String as an example or as a counter-example?

The class String (lets take the Java 1.5 version as an example) has 13 constructors and 63 methods (excluding the inherited ones) and implements 3 interfaces, if I did not make a mistake in the counting.

Winston Gutkowski
Bartender

Joined: Mar 17, 2011
Posts: 8207
    
  23

Derik Davenport wrote:As for me, I have one approach that I have found somewhat useful. I start by examining all of the variables (fields) in the class. If there is a subset of all the fields, that can be combined with a subset of methods that exclusively use those same fields, then the fields and the methods can be cleanly removed from the original class and placed into a new class, an instance of which is now owned by the resulting "original" class.
...
I am interested to learn how other programmers handle this.

The approach you cite is one possible way, but it relies on your original monolith being correct. If it isn't, you're likely to propagate any inconsistencies into your new classes.

My suggestion: Take a step back.

Forget about Java for a moment and write out a description of what your monolithic class does (NOTE: NOT how it does it). And write it in English.

When you've done that, you can use a technique called 'lexical analysis':
Take your description and read through it - several times - noting down verbs, proper nouns and events (if you have them).
Roughly speaking, verbs translate to methods, nouns and events to classes (or interfaces; or possibly even a set of classes); but it's not an exact mapping, just a guideline.

For example, just from your initial description, I'd say that you have at least three classes all bundled together: a Motor, a Task, and a SupportTask; and from the sound of it, those last two might be related - perhaps by having a Task interface, which is implemented by two classes: RegularTask and SupportTask.

You might also want something like a Controller, that acts as an interface (and I mean that in it's English sense, not necessarily a Java interface) between a Task and the Motor, translating a task into actions for the Motor to perform.

Without more information, it's difficult to suggest much more; except to remember that programming is much more than coding: it's about thinking.
And if nothing else, the exercise I described above will get you thinking about the problem.

HIH

Winston


Isn't it funny how there's always time and money enough to do it WRONG?
Articles by Winston can be found here
Derik Davenport
Ranch Hand

Joined: May 30, 2011
Posts: 57
Sorry for the late reply. Curiously I did not get notification that either of these replies had occurred.

Ivan said...
Should we take String as an example or as a counter-example?
And I think that is a good point. That class is huge. And yet, how could it be otherwise? Perhaps I am just being paranoid about how big my classes are. When I came from C, I found I could usually keep files (groups of related functions with shared (file scope) variables) to less than 500 lines or so. More than that and they start to get hard to edit. I have no idea how many lines are in String, but it might be 10 times that size. And what a pain it must be to trudge around in there and edit that thing.


Winston said:
you can use a technique called 'lexical analysis':

I am going to try that on a few big classes and see if I come up with anything. The problem I have in splitting off tasks, is that all the tasks need to work with 3 or more of the state variables. So either those tasks need to get passed a large number of variables in thier constructors (or the method calls), or I need to pass a "this" pointer to the main class (the one holding the state variables) and make all those variables public. ouch.

programming is much more than coding: it's about thinking
I have always enjoyed the "thinking" aspect of writing programs. But there a few patterns that I keep seeing in my programs (like really large classes) and then I start to wonder if that is normal or if it is just that my thinking is in a rut.
Derik Davenport
Ranch Hand

Joined: May 30, 2011
Posts: 57
Winston,
I came back to this thread to post a follow up. As I did so, I re-read some of the previous postings and thereby stumbled across your article titled "A moment of Clarity".

Before I post my own moment here, I would like to comment on that one. You cited an example where you took a complex task, and made it simple by forking some of the logic (and the flags that maintained the state of that logic) in to separate classes you called rules. That is similar to the techniques I described in my first post of this thread for trying to find the separable objects in a god class. Namely, I take the state, and the functions that use that state, an turn it into a class. It is slightly different in your case because you are breaking a complex behavior into a group of serialized single behaviors. YOu listed the many benefits to your final solution and how how it could, with little work be made even more flexible and powerful. But one thing you didn't include in that list was that the final version could easily be made parallel, with each independent rule parsing the list on a separate stream. (not sure if that would really be helpful in all cases, but it would surely be helpful in some).

Anyway, thanks very much for taking the time to post that. I have gotten better at breaking down programs into objects. But I am alway eager to see clear examples of how this thought process works.
Campbell Ritchie
Sheriff

Joined: Oct 13, 2005
Posts: 39828
    
  28
Well done And what a lot of patience.
 
Don't get me started about those stupid light bulbs.
 
subject: designing for small objects or "breaking up a god-class"