Frogs second design
Another OO model of frogs. This design appeared in the 2006 427 exam.
Following criticism of the Frogs Design, the designer has studied design patterns and tried again.
- As before, this design models the lifecycles of Frogs.
- Getters and setters are omitted from the diagram, but may be assumed where necessary.
- There is one Biologist, Igor, who manages the Frogs. Igor moves around as required for experiments.
- There are many Frogs. The design minimises Frogs' knowledge of each other, instead using Igor to coordinate Frog behaviour. Whenever a Frog changes state, it tells Igor by calling frogChanged(), and Igor may choose to move() Frogs, or tell them to grow(), breed(), etc.
- A Toad can masquerade as a Frog, because Igor can’t tell the difference. FakeFrog makes a Toad conform to the type of Frog.
- Igor doesn’t eat Frogs any more, ever since he accidentally ate a Toad.
- A RealFrog can advancePhase() from an Egg to a Tadpole to an Adult to Dead by changing its Phase object.
- Phase objects are provided by a PhaseSelector. For every tadpole or adult Frog.
- PhaseSelector makes one Tadpole or Adult phase object. However, it uses a single Egg instance and a single Dead instance for all Frogs, because these two Phases don't need any data except birthDay and deathDay, which can be retrieved from Frog.
- A Frog can breed() to make offspring of the appropriate type.
- Tasks such as exporting XML and displaying Frog information are separated into FrogWalker subclasses. A Frog object can accept() a FrogWalker and call the WalkRealFrog() or WalkFakeFrog() method as appropriate for that Frog. It then tells offspring Frogs to accept the FrogWalker.
GoF Design Patterns
- Mediator - Frogs let Biologist know when they change and the Biologist may tell other frogs to take appropriate action. Biologist is the mediator while Frog is the Colleague.
- State - Phase: Phase is the State in this design pattern, its subclasses are the Concrete States and RealFrog is the context. The frog forwards some commands on to its current state so that its behavior changes as its state changes.
- Adapter - FakeFrog: A FakeFrog appears to be a frog but really has the behavior of a toad. FakeFrog is the adapter class, Frog is the target and Toad is the adaptee. This is an object based adapter.
- Visitor - FrogWalker: FrogWalker is a Visitor while its two subclasses are the Concrete Visitors.
- Flyweight - Some of the state objects (Dead and Egg) are shared amongst all frogs because they don't have any frog-specific state to store. Dead and Egg are the Concrete Flyweights, Phase is the Flyweight, and Tadpole and Adult are the Unshared Concrete Flyweights since they are not shared. The sharing is facilitated by the PhaseSelector class, which corresponds to the FlyweightFactory class in the GoF pattern.
- Singleton - Biologist
- Factory Method - Frog: is the Creator and the concrete implementations of Frog, RealFrog and FakeFrog are the ConcreteCreators. In this case Frog is also the Product, RealFrog and FakeFrog are the ConcreteProducts. This somewhat resembles the Prototype pattern but it can be argued that the 'breed()' method is not creating a copy, only a new default instance of Frog.
These patterns do not actually exist in the design, but might be viewed as patterns by a casual observer.
- Observer - Although Biologist could be viewed as observing a Frog, it is not an observer because there are no methods on Frog equivalent to addObserver and removeObserver, and because there is only one Biologist (ie a Frog can't have multiple observers). Biologist is in fact an agent class.
- FakeFrog breaks the LSP as this type of frog is a toad but toads can't breed with frogs. This breaks the contract.
- Biologist is an agent class.