
Explore all 24 essential design patterns with clear diagrams and Java code, and learn when to use them through real open-source frameworks.
Design Pattern Summary Note is downloadable
The single responsibility principle requires a class to have one reason to change, keeping product service focused on product tasks. This reduces complexity and improves maintainability.
Implement the open/closed principle by defining an abstract draw method in shapes and letting each subclass handle drawing, while the graphic generator remains unchanged.
Understand the Liskov substitution principle, where a superclass remains replaceable by its subclass without breaking correctness, and recognize how superclass changes cascade to subclasses.
illustrates how overriding the calculate method in a subclass can produce incorrect results, violating the liskov substitution principle, and discusses avoiding superclass changes in the extension hierarchy.
Illustrates how overriding a method in B can breach the Liskov substitution principle, and moving unchanged methods from A and B to a common class with dependency fixes the design.
Demonstrates resolving a violation of the interface segregation principle by splitting a large interface into two smaller interfaces, ensuring all methods in each interface are used.
Demonstrate a dependency inversion principle violation by wiring a person to concrete email and phone text classes, and illustrate open/closed principle by extending messaging options like Instagram and WhatsApp.
Observe the dependency inversion principle in action by decoupling a person from message types through a message interface, with concrete implementations like email, phone text, and WhatsApp.
Explore the composite reuse principle by comparing inheritance and composition, showing how composing objects reduces coupling, increases flexibility and reusability, and guiding when to favor composition over inheritance.
Apply the law of demeter to limit a class to its immediate friends, reducing dependencies; friends are objects closely related, including those passed as arguments or created by the object.
Demonstrates that calling b.getC().performTask() violates the Law of Demeter, explains why c is not a friend in this context, and previews an improved approach in demo two.
This demo fixes a law of demeter violation by delegating c's task through b, removing direct c access from a, and making aa and bb LoD compliant.
We reveal why synchronizing the entire singleton method harms performance and how a double-check approach lazily initializes the singleton with minimal locking.
Explore the inner static class approach to implementing a singleton in Java, using a private constructor and a static inner class as the holder, with lazy loading via the JVM.
Explore how the JDK runtime class implements a singleton with a private constructor and a public static getRuntime method, illustrating eager loading.
Use the singleton to manage access to a shared resource and provide global access to shared data, ensuring only one instance and improving efficiency.
Explore the creational design pattern with its types: simple factory, factory method, and abstract factory, focusing on instantiation through a single factory class based on input used by client.
Implement a factory method in Java by defining an abstract order pizza and concrete New York and California orders, then a pizza store that creates pizzas based on type.
Define and implement an abstract factory interface with concrete factories for New York and California pizzas, enabling the client to order pizzas via abstract creation methods.
Explore the abstract factory pattern by building New York and California pizza factories, creating cheese and classic pizzas, and wiring them through a decoupled order client to illustrate open-closed principles.
Explore how the JDK source code uses a static getInstance method to create calendar instances via a switch on input types, illustrating the factory design pattern.
The Prototype Design Pattern is a creational design pattern that allows an object to create a copy (or clone) of itself. This pattern is useful when creating a new object is costly, or when you want to keep the new object’s state the same as an existing one. Instead of instantiating a new object from a class directly, the prototype pattern creates a new object by copying an existing object.
The clone() method (or equivalent) is defined in the prototype and implemented in the concrete prototype classes. The client can then create new objects by copying prototypes without knowing their specific types.
Use Cases:
When object creation is expensive (e.g., involves complex initialization, I/O operations, or heavy computational processes).
When an object has many configurations that can be reused with small modifications.
When you want to avoid subclasses or having a system be dependent on concrete classes.
Explore the prototype pattern to efficiently create ten identical objects, such as Zuzu with black fur and age, and why cloning helps when a class has many fields.
Demonstrate deep copy in the prototype pattern by implementing cloneable and serializable configuration classes, cloning nested objects, and validating copies with distinct hash codes.
The Builder Design Pattern is a creational design pattern that allows for the construction of complex objects step by step. Unlike other creational patterns that create objects in a single step, the Builder pattern focuses on the construction process. It separates the construction of an object from its representation, allowing the same construction process to create different representations.
Key components:
Builder: This is an interface or abstract class defining the steps to build the object. Each method in the interface typically corresponds to a part of the object being constructed.
Concrete Builder: Implements the builder interface, providing implementations for the building steps and keeping track of the representation it builds. It defines how the individual parts are created.
Director: This class is responsible for managing the correct sequence of object construction. It knows which concrete builder to use and in what order to execute the build steps, though this is optional in some implementations.
Product: The complex object that is being constructed.
Apply the builder pattern to construct a car using a car builder abstract class with body, wheels, and engine methods, and a director coordinating distinct truck or sedan builds.
Explore how Spring Boot uses the bean definition builder to configure primary, scope, and autowire, and how a generic bean definition yields a bean via getBeanDefinition.
The Adapter Pattern is a structural design pattern that allows objects with incompatible interfaces to work together. It acts as a bridge between two interfaces, converting the interface of a class into another interface that a client expects.
Key components:
Client: The entity that wants to use some service but cannot because of incompatible interfaces.
Adaptee: The class or object that has an interface that the client cannot directly use.
Adapter: The intermediary that translates the client's request into a format that the Adaptee can understand.
Types of Adapter Patterns:
Object Adapter: Uses composition to adapt an object (as shown above).
Class Adapter: Uses inheritance, where the adapter inherits from both the target interface and the adaptee class. This is more restrictive since it only works in languages that support multiple inheritance.
Use Case:
You would use the Adapter Pattern when:
You have existing code that you cannot change (like a library) but need to integrate with new systems.
You need to convert data or interface formats to allow systems to work together.
You want to reuse a class that doesn't have the expected interface.
SpringMVC:
Handler: Servlet, Controller interface, Controller method with annotation @RequestMapping, HttpRequest
Bridge incompatible interfaces with the adapter pattern by wrapping an image generator in an image creator adapter using composition. Learn object and class adapter approaches, wiring through constructors, and testing.
demonstrates the image creator adapter as an adapter that extends the image generator and implements the image creator interface, calling draw through the adapter to bridge incompatible interfaces.
Demonstrate how the adapter pattern underpins Spring MVC's dispatch flow, mapping handlers to controller methods via a handler adapter to invoke methods and return a model and view.
The Bridging Pattern is a structural design pattern in object-oriented software design that decouples an abstraction from its implementation, allowing both to vary independently. This pattern is useful when you want to avoid a permanent binding between an abstraction and its implementation. It helps in the case where you want to be able to change the abstraction as well as the implementation dynamically.
Key components:
Abstraction: This is the core interface or abstract class that defines the main functionality, but it doesn’t implement it directly. Instead, it maintains a reference to an implementation object.
Implementor: This is an interface that defines the method(s) to be implemented by the concrete implementation classes. The implementor is independent of the abstraction and can vary without affecting the abstraction.
Concrete Abstraction: This is a concrete class that extends the abstraction and uses the methods of the implementor to achieve its functionality.
Concrete Implementor: These are the actual classes that implement the Implementor interface and provide concrete implementations for the methods defined in it.
Benefits of the Bridge Pattern:
Decoupling: The abstraction and implementation are decoupled, allowing both to evolve separately without affecting each other.
Flexibility: You can switch between different implementations without modifying the abstraction.
Scalability: New abstractions and implementations can be added easily without impacting existing code.
Demonstrates the bridge design pattern by defining a brand implementer interface and concrete brands (Apple, Samsung, Sony), and an abstraction that decouples brands from phone styles (upright, fold, slide).
explain how the JDBC driver manager bridges MySQL or Oracle drivers to a common connection interface via getConnection, illustrating a bridging pattern that is not traditional.
The Decorator Pattern is a structural design pattern used to extend the functionality of an object dynamically without altering its structure. It allows behavior to be added to individual objects, either statically or dynamically, without affecting the behavior of other objects from the same class.
Key components:
Component Interface: This defines the common interface for both decorators and the original object that will be wrapped.
Concrete Component: The class that we are decorating. This is the object whose behavior is being dynamically extended.
Decorator Class: This implements the same interface as the concrete component. It contains a reference to a component object and delegates all the work to the referenced object while adding some additional behavior.
Concrete Decorators: These are the actual decorators that extend the functionality of the component by adding their own behavior.
Discover the decorator design pattern for coffee with add-ons like chocolate, milk, and soy, wrapping the drink and calculating cost while respecting the open end principle.
Explore how to implement the decorator pattern in Java by building a drink component, concrete coffees, and add-on decorators, with recursive cost and description calculations.
Explore how the Java IO decorator pattern works with filter input stream, wrapping an input stream and empowering it through concrete decorators like data input stream and file input stream.
Composite Pattern is a structural design pattern used to treat individual objects and compositions of objects uniformly. It allows you to compose objects into tree-like structures to represent part-whole hierarchies, where individual objects and composite objects are handled in the same way.
Key components:
Component: An interface that defines the operations that can be performed on both individual objects and their compositions.
Leaf: Represents the individual objects that do not have any children. Implements the Component interface.
Composite: Contains child components and implements the Component interface. A composite object can contain multiple leaf or composite objects.
Benefits of using the Composite Pattern:
Simplifies Client Code:
Clients can treat both individual objects and composite structures uniformly. You don't need to worry whether you're dealing with a single object or a whole hierarchy, which reduces conditional logic and code complexity.
Easily Extendable:
It's easy to add new kinds of components without changing existing code. By adhering to a common interface, you can introduce new leaf or composite types with minimal impact on the client code.
Supports Recursive Structures:
The Composite Pattern is ideal for implementing recursive structures like trees, where components can contain children that are themselves either individual objects or further composites.
Promotes Flexibility:
You can dynamically create complex tree structures at runtime and manipulate them flexibly (e.g., adding or removing components without affecting the rest of the system).
Facilitates Polymorphism:
By relying on a common interface for leaf and composite nodes, the pattern promotes the use of polymorphism, allowing objects to be used interchangeably without knowing their specific type.
Reduces Duplication:
Since leaf and composite nodes share the same interface, duplicate code for handling individual and composite elements is minimized.
Demonstrates the composite pattern by modeling a hierarchical education structure with a top education, science department, and leaf subjects, using a shared interface, add/remove, and print name and description.
Are you a beginner Java developer who wants to write cleaner, more professional code?
Do you want to finally understand how design patterns work and how professional developers use them in real-world applications?
This course is your complete, beginner-friendly guide to all 24 GoF design patterns in Java.
We start from the very basics, explaining each pattern step-by-step with clear visual diagrams so you can easily grasp the concepts. Then, we move to hands-on coding examples where you’ll implement each pattern yourself. Finally, we explore real-world open-source Java frameworks and analyze how these patterns are applied in practice, giving you the skills to read and understand professional-level code.
By the end of this course, you will:
Understand all 24 GoF design patterns and their use cases.
Apply design patterns to real-world Java projects.
Improve your code’s quality, scalability, and maintainability.
Read open-source framework source code with confidence.
Why this course is different:
Beginner-friendly: No prior experience with design patterns required.
Visual learning: Every pattern explained with clear diagrams.
Practical approach: Hands-on coding for every pattern.
Real-world insight: Learn from open-source framework examples.
Whether you are a junior developer, a student, or someone preparing for technical interviews, this course will give you the knowledge and confidence to write better Java code and think like a software architect.