One of the main goals of the project was to explore the use of Design Patterns in the development of a CSG. I will briefly list the design patterns used in the project. Each of the Design Patterns will be described: its intent and applicability. It will be shown how each of the described Design Pattens was used in one or more of the sub-projects.
[note all quotations are from: Design Patterns {isbn: 0201633612}]
Strategy:
Intent: "Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it." (p. 315)
Applicability: "many related classes differ only in their behavior. Strategies provide a way to configure a class with one of many behaviors." (p. 316)
The MunchkinSimulator used the Strategy pattern to simplify the class hierarchy of the monster cards. Each monster adheres to the interface BadStuff. The behavior of the monsters varies in the implementation of doBadStuff(Player). The use of a BadStuff Strategy allows monster behavior to vary without subclassing.
Decorator:
Intent: "Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality." (p. 175)
Applicability: "To add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects." (p. 177)
Fluxx used a hybrid Strategy-Decorator pattern to modify the game's ruleset at runtmime. Each phase of the turn was implemented with a rule Strategy. By adding Decorator functionality to the rule subclasses, the basic rules could be augmented or overridden by rule added (at runtime) later in the game.
Composite:
Intent: "Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly" (p. 163)
Applicability: "you want to represent part-whole hierarchies of objects." "you want clients to ignore the difference between compositions of objects and individual objects." (p. 164)
Palace used a Composite structure for interactive element of the graphic interface. This allowed the mouse handler to treat the entire play area as a cohesive object. By providing the top-level component with a select message (with x and y coordinates) the appropriate sub-component would be returned. Similarly drawing was accomplished with a single call to the top-level component's draw method.
Visitor:
Intent: "Represents an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates." (p. 331)
Applicability: "many distinct and unrelated operations need to be performed on objects in an object structure, and you want to avoid 'polluting' their classes with these operations." (p. 333)
Visitors were used in Palace to modify both drawing and selection. The visual-interactive toolkit designed for Palace left selection and drawing to the discretion of the composite object. Rather than alter the underlying behavior of the visual-interactive toolkit, Visitors were used to traverse the composite. The Visitors modified the drawing and selection behavior to change the drawing order and allow multiple selection respectively.
Proxy:
Intent: "Provide a surrogate or placeholder for another object to control access to it." (p. 207)
Applicability: "Proxy is applicable and ever there is a need for a more versatile or sophisticated reference to an object than a simple pointer." (p. 208)
JAAC used Java's reflective Proxy class to wrap game-state objects. When a method was invoked on the wrapped object, the Proxy object would inspect the Method object associated with the invocation. Methods tagged with an "Undoable" annotation would be wrapped in Command objects.
Command:
Intent: "Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support global undo operations." (p. 233)
Applicability: "Specify, queue, and execute requests at different times. A command object can have a lifetime independent of the original request." "support undo. The command's execute operation can store state for reversing its effects in the command itself. The command interface must have an added Unexecute operation that reverses the effects of a previous call to Execute. Execute commands are stored in the history list. Unlimited-level undo and redo is achieved by traversing this list backwards and forwards calling Execute and Unexecute, respectively." (p. 235-6)
JAAC used Command objects to bring together Java's reflective Method objects with the invoking object and the parameters passed to the method. The methods that were wrapped specified an inverse method in a Java Annotation, which was retained at runtime. The commands were passed to an executor, executed and then stored in a history. This provided rollback functionality upon which min-max and depth first search algorithms were implemented.