On 12/18/2011 4:13 PM, Novice wrote:
Am I right in thinking that I should actually have an abstract class,
maybe called AbstractScore, and that it should have two subclasses,
Or is there a better way?
Generally, one wants to "prefer composition to inheritance." That is,
sub-classing (any object, abstract or otherwise(*)) should be
considered after trying to compose a class out of other classes seems
unsatisfactory.
((*) However if you must subclass, using an abstract class as the base
class is best.)
In this case, composition might lead you to use two dialogs, each with
different behaviors. Both dialogs might have-a "score" which they
share. This is more MVC, where the dialogs (the view, the V part),
are separate from the model (the M part). Model here means your
program logic (the score from a game) and view means the GUI (or some
other "front end").
class MyInterumScoreDialog extends Dialog {
private int score;
...
}
class MyFinalScoreDialog extends Dialog {
private int score;
...
}
This allows you to have two objects, with different behavior, but
which also share a state (the score). The score here would probably
be injected with a setter method or with the object's constructor.
This is the nice part of composition, that it works well with
"dependency injection."
However, with a bit more reflection, you might decide that the two
dialogs above share a lot of things in common, and only differ
slightly in their behaviors. So you might want to combine them into
one object, if possible. The things that are different are the text
they display, the score itself, the label on the button, and the
action taken when the button is clicked. If these things could be
represented in an abstract way, we'd have a more concise way of
expressing what is we want, and an even more composed object.
This type of operation is so common in GUIs (display a message,
collect a button press) that Swing devotes a whole slew of boilerplate
methods to it.
<http://docs.oracle.com/javase/tutorial/uiswing/components/dialog.html>
Let's see if I can pick one out of that list that seems to fit... Well
the bit on "Customizing the Button Text" seems closest.
Unfortunately, they don't provide a way to abstract the action taken.
Here's the basic code from the tutorial which I've tweaked a bit.
Object[] options = {"OK",};
int n = JOptionPane.showOptionDialog(frame,
"Your intermediate score is:\n"+score,
"Intermediate Score",
JOptionPane.DEFAULT_OPTION,
JOptionPane.QUESTION_MESSAGE,
null, //do not use a custom Icon
options, //the titles of buttons
options[0]); //default button title
Just FYI, I didn't test or compile that code. Caveat emptor.
So now what do we do about that action? Well we can just test a
return value, and take an appropriate action. We can do this inside a
"controller" (the C part of MVC), which would be some bit of code that
executes in response to a user action. In this case, probably
finishing a level activates the score dialog.
public class MyGame {
private int score;
private boolean gameOver; // == "score is final" when true
...
void endLevel() { // called at the end of each level
// ... other end of level stuff here ...
// display the score dialog
if( gameOver ) {
// display the final score, and exit
... JOptionPane.showOptionDialog( ...
System.exit(0);
} else { // intermediate score, display and keep going
... JOptionPane.showOptionDialog( ...
// just fall through, final score doesn't return
}
}
...
}
Note that you'll have to change some of the code from the JOptionPane
exampled I showed for the final block of code. E.g., the text strings
that say "intermediate" should be changed to say "final".
So this seems to separate concerns a bit more to me than inheritance,
or making a class with a special state (which seems to me to mix a bit
the "model" (game code) with the "view" (GUI)) in an odd fashion.
The way it's done here, the GUI is pretty ignorant of what our game's
state is (we just pass it strings, it has no idea what's going on) and
we've eliminated some complexity (that complexity being a special
combined model/GUI class that needs to hold the "final score" state)
by using a standard, built-in class (or method, in this case) that
gives us the behavior we need.
Sometimes, OO makes sense, but sometimes procedural is better.
Especially in "terminal" elements of a design (that is, the leaves or
lowest elements of your design). At some point you have to stop
making objects and just make the code actually do something. When to
do that is a matter of some experience, but also "how easy is this to
do?" Once it's easy enough, just do it.
In this case, I found a standard method that would do what I wanted
with only a little manipulation, so I took that way out. Once my
design reach a point that I could call that method, rather than
decompose further, or make more objects, I just yell "done!" and write
the method, and move on to the next design requirement.
this situation.
very similar dialogs. Therefore, my effort in asking the question and
think of these matters.