MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Java常用设计模式概述

2022-11-264.9k 阅读

一、创建型模式

1.1 单例模式

单例模式是一种常用的设计模式,它确保一个类只有一个实例,并提供一个全局访问点。在Java中,实现单例模式有多种方式。

饿汉式单例:在类加载时就创建实例。

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

这种方式简单直接,在类加载时就创建实例,所以线程安全。但如果该单例对象一直未使用,会造成内存浪费。

懒汉式单例(线程不安全):在第一次调用getInstance方法时创建实例。

public class LazySingleton {
    private static LazySingleton instance;
    private LazySingleton() {}
    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

这种方式延迟了实例的创建,但在多线程环境下会有问题。如果多个线程同时调用getInstance方法,可能会创建多个实例。

懒汉式单例(线程安全 - 同步方法)

public class LazySingletonThreadSafe {
    private static LazySingletonThreadSafe instance;
    private LazySingletonThreadSafe() {}
    public static synchronized LazySingletonThreadSafe getInstance() {
        if (instance == null) {
            instance = new LazySingletonThreadSafe();
        }
        return instance;
    }
}

通过synchronized关键字修饰getInstance方法,保证了线程安全。但每次调用该方法都要进行同步,性能较低。

双重检查锁(DCL)单例

public class DoubleCheckedLockingSingleton {
    private volatile static DoubleCheckedLockingSingleton instance;
    private DoubleCheckedLockingSingleton() {}
    public static DoubleCheckedLockingSingleton getInstance() {
        if (instance == null) {
            synchronized (DoubleCheckedLockingSingleton.class) {
                if (instance == null) {
                    instance = new DoubleCheckedLockingSingleton();
                }
            }
        }
        return instance;
    }
}

这里使用了双重检查,只有在实例为null时才进行同步,大大提高了性能。volatile关键字保证了instance变量的可见性和禁止指令重排序,防止在多线程环境下出现问题。

静态内部类单例

public class StaticInnerClassSingleton {
    private StaticInnerClassSingleton() {}
    private static class SingletonHolder {
        private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
    }
    public static StaticInnerClassSingleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

这种方式利用了类加载的机制,在外部类被加载时,静态内部类不会被加载。只有在调用getInstance方法时,静态内部类才会被加载并创建实例,既保证了线程安全,又实现了延迟加载。

1.2 工厂模式

工厂模式主要分为简单工厂、工厂方法和抽象工厂。

简单工厂模式:简单工厂模式定义了一个工厂类,用于创建产品对象。它将对象的创建和使用分离。

// 产品接口
interface Shape {
    void draw();
}
// 具体产品类
class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a rectangle.");
    }
}
class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a circle.");
    }
}
// 简单工厂类
class ShapeFactory {
    public Shape createShape(String shapeType) {
        if ("rectangle".equalsIgnoreCase(shapeType)) {
            return new Rectangle();
        } else if ("circle".equalsIgnoreCase(shapeType)) {
            return new Circle();
        }
        return null;
    }
}

使用时:

public class SimpleFactoryDemo {
    public static void main(String[] args) {
        ShapeFactory factory = new ShapeFactory();
        Shape rectangle = factory.createShape("rectangle");
        rectangle.draw();
        Shape circle = factory.createShape("circle");
        circle.draw();
    }
}

简单工厂模式的优点是实现简单,将对象创建逻辑封装在工厂类中。缺点是不符合开闭原则,如果要添加新的产品,需要修改工厂类代码。

工厂方法模式:工厂方法模式将对象的创建延迟到子类。

// 产品接口
interface Product {
    void operation();
}
// 具体产品类
class ConcreteProductA implements Product {
    @Override
    public void operation() {
        System.out.println("ConcreteProductA operation.");
    }
}
class ConcreteProductB implements Product {
    @Override
    public void operation() {
        System.out.println("ConcreteProductB operation.");
    }
}
// 抽象工厂类
abstract class Factory {
    public abstract Product createProduct();
}
// 具体工厂类
class ConcreteFactoryA extends Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProductA();
    }
}
class ConcreteFactoryB extends Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProductB();
    }
}

使用时:

public class FactoryMethodDemo {
    public static void main(String[] args) {
        Factory factoryA = new ConcreteFactoryA();
        Product productA = factoryA.createProduct();
        productA.operation();
        Factory factoryB = new ConcreteFactoryB();
        Product productB = factoryB.createProduct();
        productB.operation();
    }
}

工厂方法模式符合开闭原则,当添加新的产品时,只需创建新的具体工厂类和具体产品类,而不需要修改原有代码。但缺点是工厂类数量增多,系统复杂度增加。

抽象工厂模式:抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

// 产品族接口
interface AbstractProductA {
    void methodA();
}
interface AbstractProductB {
    void methodB();
}
// 具体产品类
class ProductA1 implements AbstractProductA {
    @Override
    public void methodA() {
        System.out.println("ProductA1 methodA.");
    }
}
class ProductA2 implements AbstractProductA {
    @Override
    public void methodA() {
        System.out.println("ProductA2 methodA.");
    }
}
class ProductB1 implements AbstractProductB {
    @Override
    public void methodB() {
        System.out.println("ProductB1 methodB.");
    }
}
class ProductB2 implements AbstractProductB {
    @Override
    public void methodB() {
        System.out.println("ProductB2 methodB.");
    }
}
// 抽象工厂类
abstract class AbstractFactory {
    public abstract AbstractProductA createProductA();
    public abstract AbstractProductB createProductB();
}
// 具体工厂类
class ConcreteFactory1 extends AbstractFactory {
    @Override
    public AbstractProductA createProductA() {
        return new ProductA1();
    }
    @Override
    public AbstractProductB createProductB() {
        return new ProductB1();
    }
}
class ConcreteFactory2 extends AbstractFactory {
    @Override
    public AbstractProductA createProductA() {
        return new ProductA2();
    }
    @Override
    public AbstractProductB createProductB() {
        return new ProductB2();
    }
}

使用时:

public class AbstractFactoryDemo {
    public static void main(String[] args) {
        AbstractFactory factory1 = new ConcreteFactory1();
        AbstractProductA productA1 = factory1.createProductA();
        AbstractProductB productB1 = factory1.createProductB();
        productA1.methodA();
        productB1.methodB();
        AbstractFactory factory2 = new ConcreteFactory2();
        AbstractProductA productA2 = factory2.createProductA();
        AbstractProductB productB2 = factory2.createProductB();
        productA2.methodA();
        productB2.methodB();
    }
}

抽象工厂模式的优点是隔离了具体产品类的创建,使得系统易于扩展和维护。缺点是实现复杂,当产品族增加时,代码量会大幅增加。

1.3 建造者模式

建造者模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

// 产品类
class Computer {
    private String cpu;
    private String ram;
    private String hardDisk;
    public void setCpu(String cpu) {
        this.cpu = cpu;
    }
    public void setRam(String ram) {
        this.ram = ram;
    }
    public void setHardDisk(String hardDisk) {
        this.hardDisk = hardDisk;
    }
    @Override
    public String toString() {
        return "Computer{" +
                "cpu='" + cpu + '\'' +
                ", ram='" + ram + '\'' +
                ", hardDisk='" + hardDisk + '\'' +
                '}';
    }
}
// 抽象建造者类
abstract class ComputerBuilder {
    protected Computer computer = new Computer();
    public abstract void buildCpu();
    public abstract void buildRam();
    public abstract void buildHardDisk();
    public Computer getComputer() {
        return computer;
    }
}
// 具体建造者类
class HighEndComputerBuilder extends ComputerBuilder {
    @Override
    public void buildCpu() {
        computer.setCpu("Intel i9");
    }
    @Override
    public void buildRam() {
        computer.setRam("32GB DDR4");
    }
    @Override
    public void buildHardDisk() {
        computer.setHardDisk("1TB SSD");
    }
}
class LowEndComputerBuilder extends ComputerBuilder {
    @Override
    public void buildCpu() {
        computer.setCpu("Intel i3");
    }
    @Override
    public void buildRam() {
        computer.setRam("8GB DDR4");
    }
    @Override
    public void buildHardDisk() {
        computer.setHardDisk("500GB HDD");
    }
}
// 指挥者类
class Director {
    private ComputerBuilder computerBuilder;
    public Director(ComputerBuilder computerBuilder) {
        this.computerBuilder = computerBuilder;
    }
    public Computer construct() {
        computerBuilder.buildCpu();
        computerBuilder.buildRam();
        computerBuilder.buildHardDisk();
        return computerBuilder.getComputer();
    }
}

使用时:

public class BuilderPatternDemo {
    public static void main(String[] args) {
        ComputerBuilder highEndBuilder = new HighEndComputerBuilder();
        Director director = new Director(highEndBuilder);
        Computer highEndComputer = director.construct();
        System.out.println(highEndComputer);
        ComputerBuilder lowEndBuilder = new LowEndComputerBuilder();
        director = new Director(lowEndBuilder);
        Computer lowEndComputer = director.construct();
        System.out.println(lowEndComputer);
    }
}

建造者模式的优点是将复杂对象的构建过程封装起来,易于理解和维护。同时可以创建不同表示的对象。缺点是如果产品的内部变化复杂,可能需要定义很多具体建造者类。

二、结构型模式

2.1 代理模式

代理模式为其他对象提供一种代理以控制对这个对象的访问。

// 真实主题接口
interface Subject {
    void request();
}
// 真实主题类
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject is handling request.");
    }
}
// 代理类
class Proxy implements Subject {
    private RealSubject realSubject;
    public Proxy() {
        realSubject = new RealSubject();
    }
    @Override
    public void request() {
        System.out.println("Proxy is pre - processing.");
        realSubject.request();
        System.out.println("Proxy is post - processing.");
    }
}

使用时:

public class ProxyPatternDemo {
    public static void main(String[] args) {
        Proxy proxy = new Proxy();
        proxy.request();
    }
}

代理模式可以在不改变真实主题的情况下,对其进行功能扩展,如日志记录、权限控制等。静态代理在编译时就确定代理类,而动态代理则是在运行时动态生成代理类。

动态代理示例(JDK动态代理)

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 真实主题接口
interface Subject {
    void request();
}
// 真实主题类
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject is handling request.");
    }
}
// 调用处理器
class DynamicProxyHandler implements InvocationHandler {
    private Object target;
    public DynamicProxyHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("DynamicProxy is pre - processing.");
        Object result = method.invoke(target, args);
        System.out.println("DynamicProxy is post - processing.");
        return result;
    }
}

使用时:

public class DynamicProxyPatternDemo {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        InvocationHandler handler = new DynamicProxyHandler(realSubject);
        Subject proxy = (Subject) Proxy.newProxyInstance(
                realSubject.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(),
                handler);
        proxy.request();
    }
}

JDK动态代理基于接口实现,而CGLIB动态代理则可以基于类实现,CGLIB通过继承被代理类来创建代理对象。

2.2 装饰器模式

装饰器模式动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

// 抽象组件
abstract class Beverage {
    String description = "Unknown Beverage";
    public String getDescription() {
        return description;
    }
    public abstract double cost();
}
// 具体组件
class Espresso extends Beverage {
    public Espresso() {
        description = "Espresso";
    }
    @Override
    public double cost() {
        return 1.99;
    }
}
class HouseBlend extends Beverage {
    public HouseBlend() {
        description = "House Blend Coffee";
    }
    @Override
    public double cost() {
        return 0.89;
    }
}
// 抽象装饰器
abstract class CondimentDecorator extends Beverage {
    public abstract String getDescription();
}
// 具体装饰器
class Mocha extends CondimentDecorator {
    Beverage beverage;
    public Mocha(Beverage beverage) {
        this.beverage = beverage;
    }
    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Mocha";
    }
    @Override
    public double cost() {
        return 0.2 + beverage.cost();
    }
}
class Whip extends CondimentDecorator {
    Beverage beverage;
    public Whip(Beverage beverage) {
        this.beverage = beverage;
    }
    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Whip";
    }
    @Override
    public double cost() {
        return 0.1 + beverage.cost();
    }
}

使用时:

public class DecoratorPatternDemo {
    public static void main(String[] args) {
        Beverage espresso = new Espresso();
        System.out.println(espresso.getDescription() + " $" + espresso.cost());
        Beverage houseBlend = new HouseBlend();
        houseBlend = new Mocha(houseBlend);
        houseBlend = new Whip(houseBlend);
        System.out.println(houseBlend.getDescription() + " $" + houseBlend.cost());
    }
}

装饰器模式通过组合的方式,在运行时动态地给对象添加功能,避免了子类数量的爆炸式增长。但多层装饰可能会使代码变得复杂,难以理解和维护。

2.3 适配器模式

适配器模式将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 类适配器(通过继承实现)

// 目标接口
interface Target {
    void request();
}
// 适配者类
class Adaptee {
    public void specificRequest() {
        System.out.println("Adaptee is handling specific request.");
    }
}
// 类适配器类
class ClassAdapter extends Adaptee implements Target {
    @Override
    public void request() {
        specificRequest();
    }
}

使用时:

public class ClassAdapterPatternDemo {
    public static void main(String[] args) {
        Target target = new ClassAdapter();
        target.request();
    }
}

对象适配器(通过组合实现)

// 目标接口
interface Target {
    void request();
}
// 适配者类
class Adaptee {
    public void specificRequest() {
        System.out.println("Adaptee is handling specific request.");
    }
}
// 对象适配器类
class ObjectAdapter implements Target {
    private Adaptee adaptee;
    public ObjectAdapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }
    @Override
    public void request() {
        adaptee.specificRequest();
    }
}

使用时:

public class ObjectAdapterPatternDemo {
    public static void main(String[] args) {
        Adaptee adaptee = new Adaptee();
        Target target = new ObjectAdapter(adaptee);
        target.request();
    }
}

适配器模式在软件维护和升级时非常有用,可以在不修改原有类的情况下,使不兼容的类协同工作。但如果过多使用适配器,会使系统整体结构变得混乱,可读性降低。

三、行为型模式

3.1 观察者模式

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

// 主题接口
interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}
// 具体主题类
class WeatherData implements Subject {
    private float temperature;
    private float humidity;
    private float pressure;
    private java.util.ArrayList<Observer> observers;
    public WeatherData() {
        observers = new java.util.ArrayList<>();
    }
    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }
    @Override
    public void removeObserver(Observer observer) {
        int i = observers.indexOf(observer);
        if (i >= 0) {
            observers.remove(i);
        }
    }
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(temperature, humidity, pressure);
        }
    }
    public void measurementsChanged() {
        notifyObservers();
    }
    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}
// 观察者接口
interface Observer {
    void update(float temperature, float humidity, float pressure);
}
// 具体观察者类
class CurrentConditionsDisplay implements Observer {
    private float temperature;
    private float humidity;
    private Subject weatherData;
    public CurrentConditionsDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }
    @Override
    public void update(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        display();
    }
    public void display() {
        System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");
    }
}

使用时:

public class ObserverPatternDemo {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData);
        weatherData.setMeasurements(80, 65, 30.4f);
    }
}

观察者模式在事件驱动系统、消息发布 - 订阅系统等场景中广泛应用。优点是实现了对象之间的解耦,提高了系统的可维护性和可扩展性。缺点是如果观察者数量过多,通知所有观察者可能会影响性能。

3.2 策略模式

策略模式定义了一系列算法,将每个算法封装起来,并且使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户。

// 抽象策略接口
interface Strategy {
    void execute();
}
// 具体策略类
class ConcreteStrategyA implements Strategy {
    @Override
    public void execute() {
        System.out.println("Executing strategy A.");
    }
}
class ConcreteStrategyB implements Strategy {
    @Override
    public void execute() {
        System.out.println("Executing strategy B.");
    }
}
// 上下文类
class Context {
    private Strategy strategy;
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }
    public void executeStrategy() {
        strategy.execute();
    }
}

使用时:

public class StrategyPatternDemo {
    public static void main(String[] args) {
        Strategy strategyA = new ConcreteStrategyA();
        Context context = new Context(strategyA);
        context.executeStrategy();
        Strategy strategyB = new ConcreteStrategyB();
        context = new Context(strategyB);
        context.executeStrategy();
    }
}

策略模式使得算法的选择和使用更加灵活,符合开闭原则。但如果算法过多,会导致策略类数量增加,增加系统的复杂性。

3.3 模板方法模式

模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

// 抽象类
abstract class AbstractClass {
    public final void templateMethod() {
        primitiveOperation1();
        primitiveOperation2();
        concreteOperation();
        hook();
    }
    protected abstract void primitiveOperation1();
    protected abstract void primitiveOperation2();
    protected void concreteOperation() {
        System.out.println("AbstractClass's concrete operation.");
    }
    protected void hook() {}
}
// 具体子类
class ConcreteClass extends AbstractClass {
    @Override
    protected void primitiveOperation1() {
        System.out.println("ConcreteClass's primitive operation 1.");
    }
    @Override
    protected void primitiveOperation2() {
        System.out.println("ConcreteClass's primitive operation 2.");
    }
    @Override
    protected void hook() {
        System.out.println("ConcreteClass's hook implementation.");
    }
}

使用时:

public class TemplateMethodPatternDemo {
    public static void main(String[] args) {
        AbstractClass concreteClass = new ConcreteClass();
        concreteClass.templateMethod();
    }
}

模板方法模式通过将公共部分封装在抽象类中,减少了代码重复,提高了代码的复用性。同时,子类可以根据需要重写部分步骤,增强了系统的灵活性。但如果模板方法过于复杂,子类可能难以理解和维护。