Hello everyone, welcome back to my blog! In my previous post, I explored object-oriented design basics and the importance of UML diagrams for understanding class relationships. This week, I applied that knowledge to a practical assignment by refactoring the Duck Simulator project using several design patterns, and I want to share what I learned from the process.
Introduction
UML diagrams provide a visual blueprint for software systems, helping developers understand relationships, dependencies, and responsibilities of different classes. While useful on their own, combining UML with design patterns allows us to translate those visual models into flexible, reusable, and maintainable code. In the Duck Simulator project, I used UML to identify repetitive behavior and then applied Strategy, Singleton, and Factory patterns to improve the system’s design.
Using UML to Identify Problems
Originally, the Duck Simulator consisted of an abstract Duck
class and subclasses like MallardDuck
, RedHeadDuck
, RubberDuck
, and DecoyDuck
. Each duck implemented its own fly
and quack
methods. My UML class diagram made it clear that this design was repetitive: multiple subclasses had similar or identical behaviors. This repetition violates the DRY (Don’t Repeat Yourself) principle and makes the system harder to maintain or extend. The diagrams highlighted the exact areas where behavior abstraction could be applied, providing a clear roadmap for refactoring.
Applying the Strategy Pattern
The first refactor I implemented was the Strategy Pattern, which separates the fly
and quack
behaviors into FlyBehavior
and QuackBehavior
interfaces. Each duck is assigned a behavior object rather than hard-coding methods. Using UML, I could visualize how Duck
classes now depend on behavior interfaces, not concrete implementations. For example, RubberDuck
now uses the Squeak
behavior, and DecoyDuck
uses MuteQuack
. This change made it easy to swap behaviors dynamically and reduced duplicated code across subclasses.
Using the Singleton Pattern
Next, I noticed that all ducks shared identical behaviors like FlyWithWings
and Quack
. To avoid creating multiple unnecessary instances, I applied the Singleton Pattern. UML helped illustrate that each behavior class has a static instance
and a getInstance()
method. This ensured that ducks reused the same behavior object, saving memory and improving consistency.
Implementing the Simple Factory Pattern
Finally, I created a DuckFactory to centralize the creation of ducks with their associated behaviors. UML shows a clear dependency from the simulator to the factory, encapsulating construction logic and removing manual behavior assignments in the simulator. This simplified code maintenance and improved readability, while maintaining all Strategy and Singleton benefits.
Reflection
This assignment reinforced how UML and design patterns complement each other. The diagrams helped me see problems in the design, and patterns provided proven solutions. After completing the refactor, the Duck Simulator is now modular, maintainable, and extensible. I can confidently add new duck types or behaviors without touching existing code. Personally, I learned that UML isn’t just documentation, it’s a tool that guides better design and code structure.
Resources
While exploring this assignment, I also reviewed a great resource that breaks down the concepts from Head First Design Patterns in a clear and structured way. You can find it here on GitHub. It helped me connect UML representations with real-world code implementations, especially when applying the Strategy Pattern in my Duck Simulator project.
From the blog CS@Worcester – Rick’s Software Journal by RickDjouwe1 and used with permission of the author. All other rights reserved by the author.