Fundamentals 41 min read

Comprehensive Guide to Design Patterns with Java Examples

This article provides a thorough introduction to classic software design patterns—including creational, structural, and behavioral patterns—explaining their principles, illustrating each with clear Java code examples, and offering practical insights on when and how to apply them effectively in development.

IT Architects Alliance
IT Architects Alliance
IT Architects Alliance
Comprehensive Guide to Design Patterns with Java Examples

Design patterns are essential for writing clean, maintainable, and extensible code. This article introduces the three major categories—creational, structural, and behavioral—through detailed explanations and complete Java implementations, showing when each pattern should be used.

Creational Patterns

Simple Factory encapsulates object creation in a single method.

public class FoodFactory {
    public static Food makeFood(String name) {
        if (name.equals("noodle")) {
            Food noodle = new LanZhouNoodle();
            noodle.addSpicy("more");
            return noodle;
        } else if (name.equals("chicken")) {
            Food chicken = new HuangMenChicken();
            chicken.addCondiment("potato");
            return chicken;
        } else {
            return null;
        }
    }
}

Factory Method defines an interface for creating an object, letting subclasses decide which class to instantiate.

public interface FoodFactory {
    Food makeFood(String name);
}
public class ChineseFoodFactory implements FoodFactory {
    @Override
    public Food makeFood(String name) {
        if (name.equals("A")) {
            return new ChineseFoodA();
        } else if (name.equals("B")) {
            return new ChineseFoodB();
        } else {
            return null;
        }
    }
}

Abstract Factory groups related factories to produce families of related objects.

public interface ComputerFactory {
    CPU makeCPU();
    MainBoard makeMainBoard();
    HardDisk makeHardDisk();
}
public class AmdFactory implements ComputerFactory {
    @Override
    public CPU makeCPU() { return new AmdCPU(); }
    @Override
    public MainBoard makeMainBoard() { return new AmdMainBoard(); }
    @Override
    public HardDisk makeHardDisk() { return new AmdHardDisk(); }
}

Singleton ensures a class has only one instance.

public class Singleton {
    private static final Singleton instance = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() { return instance; }
}

Builder separates the construction of a complex object from its representation.

public class User {
    private String name;
    private String password;
    private String nickName;
    private int age;
    private User(String name, String password, String nickName, int age) {
        this.name = name;
        this.password = password;
        this.nickName = nickName;
        this.age = age;
    }
    public static class UserBuilder {
        private String name;
        private String password;
        private String nickName;
        private int age;
        public UserBuilder name(String name) { this.name = name; return this; }
        public UserBuilder password(String password) { this.password = password; return this; }
        public UserBuilder nickName(String nickName) { this.nickName = nickName; return this; }
        public UserBuilder age(int age) { this.age = age; return this; }
        public User build() {
            if (name == null || password == null) {
                throw new RuntimeException("用户名和密码必填");
            }
            if (age <= 0 || age >= 150) {
                throw new RuntimeException("年龄不合法");
            }
            if (nickName == null) { nickName = name; }
            return new User(name, password, nickName, age);
        }
    }
}

Structural Patterns

Proxy adds a layer of control before delegating to the real subject.

public class FoodServiceProxy implements FoodService {
    private FoodService foodService = new FoodServiceImpl();
    @Override
    public Food makeChicken() {
        System.out.println("我们马上要开始制作鸡肉了");
        Food food = foodService.makeChicken();
        System.out.println("鸡肉制作完成啦,加点胡椒粉");
        food.addCondiment("pepper");
        return food;
    }
    @Override
    public Food makeNoodle() {
        System.out.println("准备制作拉面~");
        Food food = foodService.makeNoodle();
        System.out.println("制作完成啦");
        return food;
    }
}

Adapter converts one interface to another so that classes can work together.

public class CockAdapter implements Duck {
    private Cock cock;
    public CockAdapter(Cock cock) { this.cock = cock; }
    @Override
    public void quack() { cock.gobble(); }
    @Override
    public void fly() { cock.fly(); }
}

Bridge separates an abstraction from its implementation, allowing them to vary independently.

public interface DrawAPI {
    void draw(int radius, int x, int y);
}
public class RedPen implements DrawAPI {
    @Override
    public void draw(int radius, int x, int y) {
        System.out.println("用红色笔画图,radius:" + radius + ", x:" + x + ", y:" + y);
    }
}
public abstract class Shape {
    protected DrawAPI drawAPI;
    protected Shape(DrawAPI drawAPI) { this.drawAPI = drawAPI; }
    public abstract void draw();
}
public class Circle extends Shape {
    private int radius;
    public Circle(int radius, DrawAPI drawAPI) { super(drawAPI); this.radius = radius; }
    @Override
    public void draw() { drawAPI.draw(radius, 0, 0); }
}

Decorator adds responsibilities to objects dynamically.

public abstract class Condiment extends Beverage {}
public class Lemon extends Condiment {
    private Beverage beverage;
    public Lemon(Beverage beverage) { this.beverage = beverage; }
    @Override
    public String getDescription() { return beverage.getDescription() + ", 加柠檬"; }
    @Override
    public double cost() { return beverage.cost() + 2; }
}

Facade provides a simple interface to a complex subsystem.

public class ShapeMaker {
    private Shape circle = new Circle();
    private Shape rectangle = new Rectangle();
    private Shape square = new Square();
    public void drawCircle() { circle.draw(); }
    public void drawRectangle() { rectangle.draw(); }
    public void drawSquare() { square.draw(); }
}

Composite lets clients treat individual objects and compositions uniformly.

public class Employee {
    private String name;
    private String dept;
    private int salary;
    private List
subordinates = new ArrayList<>();
    public Employee(String name, String dept, int salary) {
        this.name = name; this.dept = dept; this.salary = salary;
    }
    public void add(Employee e) { subordinates.add(e); }
    public void remove(Employee e) { subordinates.remove(e); }
    public List
getSubordinates() { return subordinates; }
    @Override
    public String toString() {
        return "Employee :[ Name : " + name + ", dept : " + dept + ", salary :" + salary + " ]";
    }
}

Behavioral Patterns

Strategy defines a family of algorithms and makes them interchangeable.

public interface Strategy {
    void draw(int radius, int x, int y);
}
public class RedPen implements Strategy {
    @Override
    public void draw(int radius, int x, int y) {
        System.out.println("用红色笔画图,radius:" + radius + ", x:" + x + ", y:" + y);
    }
}
public class Context {
    private Strategy strategy;
    public Context(Strategy strategy) { this.strategy = strategy; }
    public void executeDraw(int r, int x, int y) { strategy.draw(r, x, y); }
}

Observer defines a one‑to‑many dependency so that when one object changes state, all its dependents are notified.

public class Subject {
    private List
observers = new ArrayList<>();
    private int state;
    public int getState() { return state; }
    public void setState(int state) { this.state = state; notifyAllObservers(); }
    public void attach(Observer observer) { observers.add(observer); }
    public void notifyAllObservers() { for (Observer o : observers) o.update(); }
}
public abstract class Observer {
    protected Subject subject;
    public abstract void update();
}
public class BinaryObserver extends Observer {
    public BinaryObserver(Subject subject) { this.subject = subject; subject.attach(this); }
    @Override
    public void update() {
        System.out.println("二进制值为:" + Integer.toBinaryString(subject.getState()));
    }
}

Chain of Responsibility passes a request along a chain of handlers.

public abstract class RuleHandler {
    protected RuleHandler successor;
    public abstract void apply(Context context);
    public void setSuccessor(RuleHandler successor) { this.successor = successor; }
    public RuleHandler getSuccessor() { return successor; }
}
public class NewUserRuleHandler extends RuleHandler {
    @Override
    public void apply(Context context) {
        if (context.isNewUser()) {
            if (getSuccessor() != null) getSuccessor().apply(context);
        } else {
            throw new RuntimeException("该活动仅限新用户参与");
        }
    }
}

Template Method defines the skeleton of an algorithm, deferring some steps to subclasses.

public abstract class AbstractTemplate {
    public final void templateMethod() {
        init();
        apply();
        end();
    }
    protected void init() { System.out.println("init 抽象层已经实现,子类也可以选择覆写"); }
    protected abstract void apply();
    protected void end() {}
}
public class ConcreteTemplate extends AbstractTemplate {
    @Override
    protected void apply() { System.out.println("子类实现抽象方法 apply"); }
    @Override
    protected void end() { System.out.println("我们可以把 method3 当做钩子方法来使用,需要的时候覆写就可以了"); }
}

State allows an object to alter its behavior when its internal state changes.

public interface State {
    void doAction(Context context);
}
public class DeductState implements State {
    @Override
    public void doAction(Context context) {
        System.out.println("商品卖出,准备减库存");
        context.setState(this);
        // ... execute deduct logic
    }
    public String toString() { return "Deduct State"; }
}
public class Context {
    private State state;
    private String name;
    public Context(String name) { this.name = name; }
    public void setState(State state) { this.state = state; }
    public State getState() { return state; }
}

These patterns collectively help developers write code that is easier to understand, extend, and maintain across a wide range of software projects.

design patternsjavasoftware architecturebehavioralcreationalstructural
IT Architects Alliance
Written by

IT Architects Alliance

Discussion and exchange on system, internet, large‑scale distributed, high‑availability, and high‑performance architectures, as well as big data, machine learning, AI, and architecture adjustments with internet technologies. Includes real‑world large‑scale architecture case studies. Open to architects who have ideas and enjoy sharing.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.