Design Patterns

    Master software design patterns to write cleaner, more maintainable, and scalable code. Learn SOLID principles and common design patterns in Java and Microservices.

    Design patterns are reusable solutions to common problems in software design.

    23+
    GoF Patterns
    5
    SOLID Principles
    50+
    Code Examples
    3
    Pattern Categories

    What are Design Patterns?

    Design patterns are typical solutions to commonly occurring problems in software design. They are like pre-made blueprints that you can customize to solve a recurring design problem in your code.

    Unlike a library or framework, you can't just copy a pattern into your program. A pattern is not a specific piece of code, but a general concept for solving a particular problem. You can follow the pattern details and implement a solution that suits the realities of your own program.

    Patterns are often confused with algorithms, because both concepts describe typical solutions to some known problems. While an algorithm always defines a clear set of actions, a pattern is a more high-level description of a solution. The code of the same pattern applied to two different programs may be different.

    Why Use Design Patterns?

    Design patterns provide time-tested solutions that have been refined over years of real-world application. Instead of reinventing the wheel, developers can leverage these proven approaches to solve common software design challenges efficiently.

    By using design patterns, teams can communicate more effectively. When a developer mentions "Factory Pattern" or "Observer Pattern," other team members immediately understand the structure and intent of the code, reducing the need for lengthy explanations and documentation.

    Design patterns also promote code maintainability and scalability. They help create flexible architectures that can accommodate future changes with minimal modification to existing code, making your software more robust and easier to extend over time.

    Proven solutions to common problems
    Improved code maintainability
    Enhanced code reusability
    Better communication among developers
    Reduced development time
    Easier debugging and testing

    SOLID Principles

    In Java, SOLID principles are an object-oriented approach that are applied to software structure design. It is conceptualized by Robert C. Martin (also known as Uncle Bob).

    These five principles have changed the world of object-oriented programming, and also changed the way of writing software. They ensure that the software is modular, easy to understand, debug, and refactor.

    SOLID is an acronym for five design principles intended to make object-oriented designs more understandable, flexible, and maintainable. Each letter represents a fundamental principle that guides developers in creating robust and scalable software architectures.

    SOLID stands for:

    DRY Principle

    Don't Repeat Yourself

    The DRY (Don't Repeat Yourself) principle is a fundamental concept in software development that states: "Every piece of knowledge must have a single, unambiguous, authoritative representation within a system."

    This principle was formulated by Andy Hunt and Dave Thomas in their book "The Pragmatic Programmer" (1999). It aims to reduce repetition of code and logic, making software easier to maintain and less prone to bugs.

    The opposite of DRY is WET - which humorously stands for "Write Everything Twice" or "We Enjoy Typing" or "Waste Everyone's Time". WET code leads to maintenance nightmares and inconsistent behavior.

    DRY Principle Diagram showing WET vs DRY code

    WET code (duplicated) vs DRY code (reusable functions)

    WET Code Example (Bad)

    UserController.java - Duplicated Validation
    // ❌ BAD: Same email validation logic repeated in multiple placespublicclassUserController{publicvoidregisterUser(String email,String name){// Email validation - DUPLICATED!if(email ==null|| email.isEmpty()){thrownewValidationException("Email is required");}if(!email.contains("@")||!email.contains(".")){thrownewValidationException("Invalid email format");}// ... registration logic}}publicclassOrderController{publicvoidcreateOrder(String customerEmail,String product){// Same email validation - DUPLICATED!if(customerEmail ==null|| customerEmail.isEmpty()){thrownewValidationException("Email is required");}if(!customerEmail.contains("@")||!customerEmail.contains(".")){thrownewValidationException("Invalid email format");}// ... order logic}}publicclassNewsletterService{publicvoidsubscribe(String email){// Same email validation - DUPLICATED AGAIN!if(email ==null|| email.isEmpty()){thrownewValidationException("Email is required");}if(!email.contains("@")||!email.contains(".")){thrownewValidationException("Invalid email format");}// ... subscription logic}}

    Problems with WET code:

    • If validation rules change, you must update multiple places
    • Easy to miss one place during updates, causing inconsistent behavior
    • More code to test and maintain
    • Higher chance of bugs when copying and pasting

    DRY Code Example (Good)

    Refactored with DRY Principle
    // ✅ GOOD: Single source of truth for email validation// Step 1: Create a reusable utility classpublicclassValidationUtils{publicstaticvoidvalidateEmail(String email){if(email ==null|| email.isEmpty()){thrownewValidationException("Email is required");}if(!email.matches("^[A-Za-z0-9+_.-]+@(.+)$")){thrownewValidationException("Invalid email format");}}publicstaticvoidvalidateNotEmpty(String value,String fieldName){if(value ==null|| value.trim().isEmpty()){thrownewValidationException(fieldName +" is required");}}}// Step 2: Use the utility everywherepublicclassUserController{publicvoidregisterUser(String email,String name){ValidationUtils.validateEmail(email);// ✅ ReusedValidationUtils.validateNotEmpty(name,"Name");// ... registration logic}}publicclassOrderController{publicvoidcreateOrder(String customerEmail,String product){ValidationUtils.validateEmail(customerEmail);// ✅ ReusedValidationUtils.validateNotEmpty(product,"Product");// ... order logic}}publicclassNewsletterService{publicvoidsubscribe(String email){ValidationUtils.validateEmail(email);// ✅ Reused// ... subscription logic}}

    Benefits of DRY code:

    • Change validation logic in one place, updates everywhere
    • Consistent behavior across the entire application
    • Less code to write, test, and maintain
    • Improved code readability and reusability

    More Ways to Apply DRY

    Constants & Configuration

    Use constants for values that appear multiple times (API URLs, magic numbers, error messages).

    Utility Functions

    Extract common operations into utility classes or helper methods.

    Inheritance & Composition

    Share common behavior through base classes or composed objects.

    Templates & Generics

    Use generics and templates to write code that works with multiple types.

    Key Benefits of DRY

    Easier maintenance - fix bugs in one place
    Consistent behavior across application
    Reduced codebase size
    Better testability
    Improved code readability
    Faster development time

    Ready to Master Design Patterns?

    Start with the SOLID principles and build a strong foundation for understanding design patterns.