COMP 310
|
HW05: Command-dispatching Ballworld with Inter-ball Interactions |
![]() ![]() ![]() ![]() ![]() ![]() |
To turn in assignment: branch your HW04 to a new HW05 project in SVN. Do NOT name your assignment anything other than "HW05"! Points WILL be deducted for misnamed projects.
In this assignment, you will get collisions and inter-ball interactions working in your Ballworld system. The interactions will be in response to some sort of criteria that determines whether or not the interaction will take place, for instance, a ball will only "kill" another ball if it overlaps (collides) with it. You are free to design whatever kinds of interactions and criteria you choose and are encouraged to seek your own models, designs and implementations for ball-to-ball interaction. You are NOT required to follow the design presented in class! Creativity and innovation will be rewarded!
Since this is the last Ballworld-based project, at least for a while, one of the goals here is to neatly "wrap up" this project. When you are finished, all the documentation should be in order, all the classes and interfaces should be in packages that make logical and architectural sense, etc. A basic rule-of-thumb criteria would be to make this project ready to hand off to another developer who is not familiar with your work and all that they will get is what is contained in the project (this is a very realistic situation!).
It is HIGHLY recommended that you do Steps 1-3 above FIRST and make sure that your code works before proceeding. This will create a much better organization of your code and will help clarify what your system does to you, making the rest of the assignment much, much easier. Fill in the missing parts of Steps 2 and 3 as you create the new classes and possibly packages in the remaining steps.
Start EARLY!! This assignment is due in a week, so ask questions early--do NOT wait until the last minute. If you encounter problems or if you have not followed the directions carefully in Step 1, it could take a significant amount of time with the staff's help to get your code back to working order, so get this done and out of the way so it won't derail you at the last moment.
There are some sticky issues regarding the initialization of the decoree strategy in the SwitcherStrategy. These issues include, but are not limited to:
The initialization of a strategy requires an input parameter of a host Ball, but there is no Ball available for such a parameter when the strategy switching occurs.
The decoree strategy is a single strategy instance that is shared amongst many host Balls, so which Ball should be used for initialization?
If the initialization of the decoree strategy mutates that strategy as per the properties of its host ball, since the strategy is shared by multiple balls, what should the net effect be on the decoree strategy?
Possible solutions, none of which is 100% satisfactory, include
By using a state design pattern or some sort of boolean flag value, delay the initialization until the first time the decoree strategy updates each of its host balls. This, unfortunately, does not address the last issue above.
Use a “null” Ball to perform the initialization of the decoree strategy. This, unfortunately, would negate any mutating effect the initialization might have on the host ball, e.g. setting the host’s color.
Overall, the issue is wrapped up in notions of state in the strategy and the ability to share a stateful object amongst multiple hosts. This is a non-trivial issue with non-trivial consequences and is certainly worth any the study by any student of OOP/D.
However, in the interest of minimizing distractions for Comp310 students in completing this assignment, you will NOT be required to perform a re-initialization of the decoree strategy when doing a switch operation on a SwitcherStrategy.
Do revisit the problem when you are more relaxed as it is a very serious issue that could potentially create serious consequences if ignored in a large system.
Static
Fields (or Methods)!In general, the only places you should ever be using a
static
field are:
- Singleton
objects
- serialVersionUID
values
- Null Objects, including invariant error and similar objects
The only static
method should be main()
.
static
arrays of strategiesHere is a scenario that pops up in student code where a composite strategy is being defined:
public class MyCompositePaintStrategy extends MultiPaintStrategy { private statics APaintStrategy[] myPStrats = {new EllipsePaintStrategy(), new RectanglePaintStrategy()}; public MyCompositePaintStrategy() { super(new AffineTransform(), myPStrats); } // etc … }
where there is a composite strategy defined along these lines:
public class MultiPaintStrategy extends APaintStrategy { public APaintStrategy[] pStrats; public MultiPaintStrategy(AffineTransform at, APaintStrategy[] pStrats) { super(at); this.pStrats = pStrats; } // etc … }
There are two fixes to the above code, one fast and the
other better. Both involve getting rid of the static
field in
MyComposite
:
public class MyCompositePaintStrategy extends MultiPaintStrategy { public MyCompositePaintStrategy() { super(new AffineTransform(), new APaintStrategy[]{new EllipsePaintStrategy(), new RectanglePaintStrategy()}); } …etc }
MultiPaintStrategy
to take a
varargpublic class MultiPaintStrategy extends APaintStrategy { public APaintStrategy[] _pStrats; public MultiPaintStrategy(AffineTransform at, APaintStrategy… pStrats) { super(at); _pStrats = pStrats; } // etc … }
Notice that that the only change is to replace a “[]
” with
“…
” in the MultiPaintStrategy
constructor's
signature. Nothing else changes because
the type of pStrats
was and still is
APaintStrategy[]
. See the
discussion on vararg parameters at the bottom of
Lec12.
The effect of this is to simplify
MyCompositePaintStrategy
considerably:
public class MyCompositePaintStrategy extends MultiPaintStrategy { public MyCompositePaintStrategy() { super(new AffineTransform(), new EllipsePaintStrategy(), new RectanglePaintStrategy()); } //etc… }
static
arrays of choicesSometimes students use a
static
field to hold the options for
classes that randomly choose an image or the like, for example like such:
public class MyRandomImagePaintStrategy extends ImagePaintStrategy { private static String[] imageFiles = {"images/humbird_animate.gif","images/sheep_animate.gif"}; // array of file names. public BirdSheepImagePaintStrategy() { super(imageFiles [Randomizer.Singleton.randomInt(0, 2)], 0.5); } }
One solution is to use Java's "ternary
conditional operator" or simply, the "?
"
operator:
public class MyRandomImagePaintStrategy extends ImagePaintStrategy { public MyRandomImagePaintStrategy() { super( ((Math.random() < 0.5)?"images/humbird_animate.gif" : "images/sheep_animate.gif") , 0.5); } }
For the more general case, where more than 2 choices are desired, use a dynamically created array as was done above and pick a random element from it:
public class MyRandomImagePaintStrategy extends ImagePaintStrategy { public MyRandomImagePaintStrategy() { super( (new String[]{"images/humbird_animate.gif", "images/sheep_animate.gif", "images/Mario_animate.gif", "images/Sonic_animate.gif"})[Randomizer.Singleton.randomInt(0, 4)], 0.5); } }
An even better solution is to recognize the process of
randomly choosing a filename has nothing to do with paint strategies and thus define a new method of
IRandomizer
and
Randomizer
to
randomly pick an element from a vararg input list:
public interface IRandomizer { // other methods elided /** * Return one of the vararg input parameters with equal probability. * The input parameters must all be of the same type and the return type * will match that type. * @paramThe type of the input parameters and thus the return type * @param choices vararg list of choices * @return One of the input parameters */ public abstract <T> T randomChoiceList(@SuppressWarnings("unchecked") T... choices ); // Suppress the warning about a generic vararg }
where Randomizer
implements the method as such:
public class Randomizer implements IRandomizer { // other methods elided /** * Return one of the vararg input parameters with equal probability. * The input parameters must all be of the same type and the return type * will match that type. * @paramThe type of the input parameters and thus the return type * @param choices vararg list of choices * @return One of the input parameters */ @Override public T randomChoiceList(@SuppressWarnings("unchecked") T... choices) { // Suppress the warning about a generic vararg return choices[randomInt(0, choices.length-1)]; } }
The constructor for the MyRandomPaintStrategy
paint strategy is
now very clear and clean:
public class MyRandomPaintStrategy extends ImagePaintStrategy { public MyRandomImagePaintStrategy() { super( Randomizer.Singleton.randomChoiceList("images/humbird_animate.gif", "images/sheep_animate.gif", "images/Mario_animate.gif", "images/Sonic_animate.gif"), 0.5); } }
© 2014 by Stephen Wong