Java常见设计模式

Java
71
0
0
2024-09-26
hi,我是程序员王也,一个资深Java开发工程师,平时十分热衷于技术副业变现和各种搞钱项目的程序员~,如果你也是,可以一起交流交流。

今天我们继续来聊聊Java中的设计模式~

第一部分:创建型模式

创建型模式主要用于对象的创建过程,以解耦对象的创建和使用。以下是几种常见的创建型设计模式。

1. 单例模式(Singleton)

单例模式确保一个类只有一个实例,并提供一个全局访问点。

案例源码:

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

单例模式在控制对象数量时非常有用,如配置管理器或线程池。然而,它也可能导致代码耦合和测试困难。在多线程环境中,确保线程安全是必要的,如上述代码中的synchronized关键字。

2. 工厂方法模式(Factory Method)

工厂方法模式定义了一个创建对象的接口,但让子类决定要创建的对象类型。

案例源码:

public interface Product {
    void operation();
}

public class ConcreteProduct implements Product {
    public void operation() {
        System.out.println("Concrete Product operation");
    }
}

public abstract class Creator {
    public abstract Product factoryMethod();
}

public class ConcreteCreator extends Creator {
    public Product factoryMethod() {
        return new ConcreteProduct();
    }
}

// 使用工厂方法模式
Creator creator = new ConcreteCreator();
Product product = creator.factoryMethod();
product.operation();

工厂方法模式封装了对象的创建过程,允许系统的扩展而无需修改已有的客户端代码。它提高了代码的可维护性和可扩展性。

3. 抽象工厂模式(Abstract Factory)

抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要指定它们的具体类。

案例源码:

// 假设有多个产品族和产品等级
public interface ProductA { /* ... */ }
public interface ProductB { /* ... */ }

public class ConcreteProductA implements ProductA { /* ... */ }
public class ConcreteProductB implements ProductB { /* ... */ }

public abstract class AbstractFactory {
    public abstract ProductA createProductA();
    public abstract ProductB createProductB();
}

public class ConcreteFactory extends AbstractFactory {
    public ProductA createProductA() {
        return new ConcreteProductA();
    }
    
    public ProductB createProductB() {
        return new ConcreteProductB();
    }
}

// 使用抽象工厂模式
AbstractFactory factory = new ConcreteFactory();
ProductA productA = factory.createProductA();
ProductB productB = factory.createProductB();

抽象工厂模式适用于系统需要提供多个产品族,并且这些产品族需要协调使用的场景。它能够确保客户端始终使用相同的产品族,从而保证了系统的一致性。

4. 建造者模式(Builder)

建造者模式将复杂对象的构建过程分离出来,使用不同的表示来构建对象。

案例源码:

public class Product {
    private String part1;
    private String part2;
    
    private Product(Builder builder) {
        this.part1 = builder.part1;
        this.part2 = builder.part2;
    }
    
    public static class Builder {
        private String part1;
        private String part2;
        
        public Builder part1(String part1) {
            this.part1 = part1;
            return this;
        }
        
        public Builder part2(String part2) {
            this.part2 = part2;
            return this;
        }
        
        public Product build() {
            return new Product(this);
        }
    }
}

// 使用建造者模式
Product product = new Product.Builder()
    .part1("Part1")
    .part2("Part2")
    .build();

建造者模式适用于创建复杂对象,它能够提供更多的灵活性,使得创建过程中的内聚性更强。它还有助于避免对象的不完整状态,因为所有必要的组件都可以在构建之前设置好。

创建型模式在软件开发中非常关键,它们提供了一系列的解决方案来处理对象创建过程中的复杂性。每种模式都有其特定的使用场景,合理地使用它们可以提高代码的可读性、可维护性和灵活性。

第二部分:结构型模式

结构型模式主要关注如何组合类或对象以形成更大的结构。以下是几种常见的结构型设计模式。

1. 适配器模式(Adapter)

适配器模式允许不兼容的接口协同工作,它通常用于将一个类的接口转换成客户端期望的另一个接口。

案例源码:

// 假设有一个旧的类,我们想要复用它
class OldClass {
    public void specificMethod() {
        System.out.println("Old class specific method");
    }
}

// 新的目标接口
interface NewInterface {
    void genericMethod();
}

// 适配器类
class Adapter implements NewInterface {
    private OldClass oldClass = new OldClass();

    @Override
    public void genericMethod() {
        oldClass.specificMethod();
    }
}

// 使用适配器
NewInterface newInterface = new Adapter();
newInterface.genericMethod();

适配器模式是实现接口兼容性的一种非常实用的方式。它允许开发者复用现有的类,即使这些类的接口不符合需求。适配器模式也可以用来让不同的类协同工作,即使它们之间没有直接的联系。

2. 装饰器模式(Decorator)

装饰器模式允许在不修改对象的基础上,动态地添加功能。

案例源码:

interface Shape {
    String draw();
}

class Circle implements Shape {
    @Override
    public String draw() {
        return "Drawing a circle";
    }
}

abstract class ShapeDecorator implements Shape {
    protected Shape decoratedShape;

    public ShapeDecorator(Shape decoratedShape) {
        this.decoratedShape = decoratedShape;
    }

    @Override
    public String draw() {
        return decoratedShape.draw();
    }
}

class RedShapeDecorator extends ShapeDecorator {
    public RedShapeDecorator(Shape decoratedShape) {
        super(decoratedShape);
    }

    @Override
    public String draw() {
        return "Drawing a red " + decoratedShape.draw();
    }
}

// 使用装饰器
Shape circle = new Circle();
System.out.println(circle.draw()); // 正常绘制圆形
Shape redCircle = new RedShapeDecorator(new Circle());
System.out.println(redCircle.draw()); // 绘制红色的圆形

装饰器模式提供了一种灵活的、可扩展的方式来扩展对象的功能。它通过组合而非继承来实现扩展,这有助于避免继承层次结构的复杂化。

3. 代理模式(Proxy)

代理模式提供了对另一个对象的代理以控制对此对象的访问。

案例源码:

interface Subject {
    void request();
}

class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("Handling real request");
    }
}

class Proxy implements Subject {
    private RealSubject realSubject;

    public Proxy() {
        this.realSubject = null;
    }

    public void request() {
        if (realSubject == null) {
            realSubject = new RealSubject();
        }
        realSubject.request();
    }
}

// 使用代理
Subject subject = new Proxy();
subject.request();

代理模式在控制对资源的访问方面非常有用,尤其是在资源的初始化很昂贵或需要安全控制的情况下。它也可以用来实现延迟初始化,从而提高程序的性能。

4. 组合模式(Composite)

组合模式允许将对象组合成树形结构以表示“部分-整体”的层次结构。

案例源码:

interface Component {
    void operation();
}

class Leaf implements Component {
    @Override
    public void operation() {
        System.out.println("Leaf operation");
    }
}

class Composite implements Component {
    private List<Component> children = new ArrayList<>();

    public void add(Component component) {
        children.add(component);
    }

    public void remove(Component component) {
        children.remove(component);
    }

    @Override
    public void operation() {
        for (Component child : children) {
            child.operation();
        }
    }
}

// 使用组合模式
Composite root = new Composite();
Component leaf1 = new Leaf();
Component leaf2 = new Leaf();
root.add(leaf1);
root.add(leaf2);
root.operation();

组合模式非常适合用来表示层次结构的数据,它使得单个对象和组合对象的使用具有一致性。这有助于简化客户端代码,因为客户端可以统一地对待单个对象和组合对象。

结构型模式通过不同的方式提供了对象组合和层次结构的设计方法,它们使得软件设计更加灵活和模块化。合理地使用结构型模式可以提高系统的可维护性和可扩展性。

第三部分:行为型模式

行为型模式主要关注对象之间的相互作用以及它们怎样相互协作来完成单个对象无法独立完成的任务。

1. 策略模式(Strategy)

策略模式定义了一系列算法,并将每个算法封装起来让它们可以互换使用,算法的变化不会影响使用算法的用户。

案例源码:

// 策略接口
interface Strategy {
    int doOperation(int num1, int num2);
}

// 实现策略接口的具体策略
class OperationAdd implements Strategy {
    public int doOperation(int num1, int num2) {
        return num1 + num2;
    }
}

class OperationSubtract implements Strategy {
    public int doOperation(int num1, int num2) {
        return num1 - num2;
    }
}

// 环境类
class Context {
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public int executeStrategy(int num1, int num2) {
        return strategy.doOperation(num1, num2);
    }
}

// 客户端使用策略模式
public class StrategyClient {
    public static void main(String[] args) {
        Context context = new Context(new OperationAdd());
        System.out.println("10 + 5 = " + context.executeStrategy(10, 5));

        context = new Context(new OperationSubtract());
        System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
    }
}

策略模式允许在运行时根据不同的情况选择不同的行为。它将行为的变化封装在不同的策略实现中,使得策略的变化不会影响到使用策略的客户端代码。策略模式非常适合用于需要在运行时切换行为的场景。

2. 观察者模式(Observer)

观察者模式允许对象在其状态发生变化时通知其他对象。

案例源码:

// 主题接口
interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

// 观察者接口
interface Observer {
    void update(String message);
}

// 具体主题
class ConcreteSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String state;

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
        notifyObservers();
    }

    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    @Override
    public void removeObserver(Observer o) {
        observers.remove(o);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(getState());
        }
    }
}

// 具体观察者
class ConcreteObserver implements Observer {
    private ConcreteSubject subject;

    public ConcreteObserver(ConcreteSubject subject) {
        this.subject = subject;
        this.subject.registerObserver(this);
    }

    @Override
    public void update(String message) {
        System.out.println("Observer: " + message);
    }
}

// 客户端使用观察者模式
public class ObserverClient {
    public static void main(String[] args) {
        ConcreteSubject subject = new ConcreteSubject();
        new ConcreteObserver(subject);

        subject.setState("New State 1");
        subject.setState("New State 2");
    }
}

观察者模式是一种发布-订阅模式,非常适合于需要解耦对象之间的交互的场景。它允许对象在不引用对方的情况下进行通信,使得系统更加灵活和可维护。

3. 命令模式(Command)

命令模式将一个请求封装为一个对象,从而允许用户使用不同的请求、队列或日志请求来参数化其他对象。

案例源码:

// 命令接口
interface Command {
    void execute();
}

// 具体命令
class ConcreteCommand implements Command {
    private Receiver receiver;

    public ConcreteCommand(Receiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        receiver.action();
    }
}

// 接收者,知道如何实施与执行一个请求相关的操作
class Receiver {
    public void action() {
        System.out.println("Action is performed");
    }
}

// 发射器,使用命令对象来执行请求
class Invoker {
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void executeCommand() {
        if (command != null) {
            command.execute();
        }
    }
}

// 客户端使用命令模式
public class CommandClient {
    public static void main(String[] args) {
        Receiver receiver = new Receiver();
        Command command = new ConcreteCommand(receiver);
        Invoker invoker = new Invoker();

        invoker.setCommand(command);
        invoker.executeCommand();
    }
}

命令模式提供了一种将操作封装为对象的方法,这使得你可以将操作作为参数传递给方法,或者将它们存储在数据结构中。这种模式非常适合于需要支持撤销/重做操作、事务等场景。

4. 迭代器模式(Iterator)

迭代器模式提供了一种按顺序访问对象元素的方法,而不暴露其底层表示。

案例源码:

// 抽象集合
interface Aggregate {
    Iterator createIterator();
}

// 抽象迭代器
interface Iterator {
    boolean hasNext();
    Object next();
}

// 具体迭代器
class ConcreteIterator implements Iterator {
    private List items;
    private int position = 0;

    public ConcreteIterator(List items) {
        this.items = items;
    }

    @Override
    public boolean hasNext() {
        return position < items.size();
    }

    @Override
    public Object next() {
        return items.get(position++);
    }
}

// 具体集合
class ConcreteAggregate implements Aggregate {
    private List items = new ArrayList();

    public void addItem(Object item) {
        items.add(item);
    }

    @Override
    public Iterator createIterator() {
        return new ConcreteIterator(items);
    }
}

// 客户端使用迭代器模式
public class IteratorClient {
    public static void main(String[] args) {
        ConcreteAggregate aggregate = new ConcreteAggregate();
        aggregate.addItem("Item1");
        aggregate.addItem("Item2");
        aggregate.addItem("Item3");

        Iterator iterator = aggregate.createIterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

迭代器模式是一种非常实用的设计模式,它使得你可以在不知道集合底层实现的情况下遍历集合。它为遍历不同的集合结构提供了统一的接口,这有助于编写通用的客户端代码。

行为型模式关注与对象交互的灵活性和可维护性,它们提供了多种处理对象交互和行为变化的解决方案。通过使用行为型模式,可以提高代码的灵活性和可重用性,同时也使得代码更容易扩展和维护。

第四部分:并发编程模式

在现代软件开发中,多线程和并发编程变得越来越重要。Java提供了多种并发编程模式来处理线程之间的交互和数据共享。

1. 生产者-消费者模式

生产者-消费者模式是描述两类线程(生产者和消费者)如何协同工作的经典模式。生产者生成数据,消费者消费(处理)数据。

案例源码:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class ProducerConsumer {
    private BlockingQueue<String> queue = new ArrayBlockingQueue<>(5);

    // 生产者线程
    class Producer implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    String product = "Product" + i;
                    queue.put(product);
                    System.out.println(product + " produced");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    // 消费者线程
    class Consumer implements Runnable {
        @Override
        public void run() {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    String product = queue.take();
                    System.out.println(product + " consumed");
                } catch (InterruptedException e) {
                    break;
                }
            }
        }
    }

    public static void main(String[] args) {
        ProducerConsumer demo = new ProducerConsumer();
        Thread producer = new Thread(demo.new Producer());
        Thread consumer = new Thread(demo.new Consumer());
        producer.start();
        consumer.start();
    }
}

生产者-消费者模式通过使用阻塞队列来安全地在生产者和消费者之间传递消息。这种模式非常适合处理线程间的数据共享,而且Java提供的BlockingQueue接口极大地简化了该模式的实现。

2. 反应式模式

反应式编程是一种关注数据流和变化传播的编程范式。在Java中,反应式编程通常与事件驱动的系统结合使用。

案例源码:

import reactor.core.publisher.Flux;

public class ReactiveDemo {
    public static void main(String[] args) {
        Flux.just("A", "B", "C", "D", "E")
            .map(String::toUpperCase)
            .filter(c -> c.equals("A"))
            .subscribe(System.out::println);
    }
}

反应式模式通过使用事件流和背压机制来处理异步数据流。它适用于构建可伸缩、非阻塞的事件驱动应用。使用反应式编程库(如Project Reactor)可以帮助开发者以声明式的方式处理复杂的异步逻辑。

3. 线程池模式

线程池模式用于有效地管理线程资源,通过重用已存在的线程来执行新的任务,而不是每次都创建和销毁线程。

案例源码:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 5; i++) {
            final int taskNumber = i;
            executor.submit(() -> {
                System.out.println("Task " + taskNumber + " is running");
            });
        }
        executor.shutdown(); // 关闭线程池
    }
}

线程池模式通过减少在处理任务时创建和销毁线程的开销来提高性能。它还允许开发者控制最大并发数,从而更好地管理资源。Java的java.util.concurrent包提供了强大的线程池实现,使得并发编程变得更加容易。

4. 信号量模式

信号量是一种用于控制同时访问共享资源数量的同步机制。

案例源码:

import java.util.concurrent.Semaphore;

public class SemaphoreDemo {
    private static final int THREAD_COUNT = 5;
    private static Semaphore semaphore = new Semaphore(2);

    public static void main(String[] args) {
        for (int i = 0; i < THREAD_COUNT; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire(); // 获取一个许可
                    System.out.println(Thread.currentThread().getName() + " entered the protected section");
                    // 模拟执行任务
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + " left the protected section");
                    semaphore.release(); // 释放一个许可
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

信号量模式通过控制许可的数量来限制对共享资源的访问。它非常适合用于解决并发编程中的竞态条件问题。Java的Semaphore类提供了信号量机制的实现,使得同步控制变得更加灵活。

第五部分:实际应用案例

在本节中,我们将通过一些实际的Java代码示例来展示设计模式的应用,并分析每种设计模式的优缺点和适用场景。

1. 单例模式的实际应用

单例模式常用于全局配置或全局访问点。

案例源码:

public class GlobalConfig {
    private static GlobalConfig instance;
    private String configData;

    private GlobalConfig() {
        // 初始化配置数据
    }

    public static synchronized GlobalConfig getInstance() {
        if (instance == null) {
            instance = new GlobalConfig();
        }
        return instance;
    }

    public String getConfigData() {
        return configData;
    }

    public void setConfigData(String configData) {
        this.configData = configData;
    }
}

// 使用单例模式
GlobalConfig config = GlobalConfig.getInstance();
config.setConfigData("New Configuration");
String data = config.getConfigData();

单例模式确保全局只有一个配置实例,适合于资源共享的情况。然而,它也可能导致代码耦合,并且在多线程环境中需要特别注意线程安全问题。

2. 工厂方法模式的实际应用

工厂方法模式用于创建对象,将对象创建逻辑封装在子类中。

案例源码:

public interface Animal {
    void makeSound();
}

public class Dog implements Animal {
    public void makeSound() {
        System.out.println("Woof!");
    }
}

public class Cat implements Animal {
    public void makeSound() {
        System.out.println("Meow!");
    }
}

public abstract class AnimalFactory {
    abstract Animal createAnimal();
}

public class DogFactory extends AnimalFactory {
    public Animal createAnimal() {
        return new Dog();
    }
}

public class CatFactory extends AnimalFactory {
    public Animal createAnimal() {
        return new Cat();
    }
}

// 使用工厂方法模式
AnimalFactory factory = new DogFactory();
Animal animal = factory.createAnimal();
animal.makeSound();

工厂方法模式允许扩展而无需修改客户端代码,提高了代码的可维护性和可扩展性。它在需要根据不同条件创建不同实例时非常有用。

3. 策略模式的实际应用

策略模式允许在运行时选择不同的行为。

案例源码:

public interface Strategy {
    int doOperation(int num1, int num2);
}

public class OperationAdd implements Strategy {
    public int doOperation(int num1, int num2) {
        return num1 + num2;
    }
}

public class Context {
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public int executeStrategy(int num1, int num2) {
        return strategy.doOperation(num1, num2);
    }
}

// 使用策略模式
Context context = new Context(new OperationAdd());
int result = context.executeStrategy(3, 4);
System.out.println("Result: " + result);

策略模式通过将算法封装起来,使得算法可以独立于客户端,并且可以轻松切换。它适用于算法有多种实现,且这些实现可以互换的场景。

4. 观察者模式的实际应用

观察者模式用于实现主题和观察者之间的一对多关系。

案例源码:

import java.util.ArrayList;
import java.util.List;

public interface Observer {
    void update(String message);
}

public interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

public class ConcreteSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String state;

    public void setState(String state) {
        this.state = state;
        notifyObservers();
    }

    public String getState() {
        return state;
    }

    @Override
    public void registerObserver(Observer o) {
        if (!observers.contains(o)) {
            observers.add(o);
        }
    }

    @Override
    public void removeObserver(Observer o) {
        observers.remove(o);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(state);
        }
    }
}

public class ConcreteObserver implements Observer {
    private ConcreteSubject subject;

    public ConcreteObserver(ConcreteSubject subject) {
        this.subject = subject;
        subject.registerObserver(this);
    }

    @Override
    public void update(String message) {
        System.out.println("Received message: " + message);
    }
}

// 使用观察者模式
ConcreteSubject subject = new ConcreteSubject();
new ConcreteObserver(subject);
subject.setState("New State");

观察者模式是一种强大的消息发布/订阅模式,它允许对象在状态变化时自动通知其他对象。这有助于解耦组件,使得代码更加灵活和易于维护。