Java设计模式

Java设计模式

一、观察者设计模式

1.1 概述

观察者模式(Observer Pattern),又称发布-订阅模式,是一种行为设计模式。它定义了一种一对多的依赖关系,允许多个观察者对象同时监听某个主题对象(Subject)。当主题对象的状态发生变化时,它会通知所有注册的观察者,以便这些观察者能够自动更新自身的状态。

1.2 结构

观察者模式通常包含以下角色:

  1. Subject(主题)
    • 抽象主题角色,负责维护观察者的列表。
    • 提供接口以添加和删除观察者。
    • 通常包括方法如 attach(observer)detach(observer)
  2. ConcreteSubject(具体主题)
    • 实现了 Subject 接口的具体类,维护有关状态。
    • 当状态变化时,调用通知方法以更新所有观察者。
  3. Observer(观察者)
    • 抽象观察者角色,定义更新接口,通常是 update() 方法。
    • 观察者在收到主题变化的通知时调用该方法更新自身状态。
  4. ConcreteObserver(具体观察者)
    • 实现 Observer 接口的具体类。
    • 在更新方法中实现如何响应主题变化。

例子:

在使用微信公众号时,大家都会有这样的体验,当你关注的公众号中有新内容更新的话,它就会推送给关注公众号的微信用户端。我们使用观察者模式来模拟这样的场景

  • 微信用户就是观察者
  • 微信公众号是被观察者
    有多个的微信用户关注了这个公众号。
java 复制代码
// 观察者接口
public interface Observer {
    void update(String message); // 接收更新消息
}

// 主题接口
public interface Subject {
    void attach(Observer observer); // 添加观察者
    void detach(Observer observer); // 删除观察者
    void notifyObservers(String message); // 通知观察者更新消息
}

// 具体主题类
import java.util.ArrayList;
import java.util.List;

public class WeChatOfficialAccount implements Subject {
    private List<Observer> observers = new ArrayList<>(); // 观察者列表

    @Override
    public void attach(Observer observer) {
        observers.add(observer);
        System.out.println(observer + " 关注了公众号。");
    }

    @Override
    public void detach(Observer observer) {
        observers.remove(observer);
        System.out.println(observer + " 取消关注了公众号。");
    }

    @Override
    public void notifyObservers(String message) {
        System.out.println("公众号发布新内容: " + message);
        for (Observer observer : observers) {
            observer.update(message);
        }
    }
}

// 具体观察者类
public class WeChatUser implements Observer {
    private String name;

    public WeChatUser(String name) {
        this.name = name;
    }

    @Override
    public void update(String message) {
        System.out.println(name + " 收到了新推送: " + message);
    }

    @Override
    public String toString() {
        return name; // 方便打印
    }
}

// 示例用法
public class WeChatObserverPattern {
    public static void main(String[] args) {
        // 创建公众号
        WeChatOfficialAccount wechatAccount = new WeChatOfficialAccount();

        // 创建微信用户
        WeChatUser user1 = new WeChatUser("用户A");
        WeChatUser user2 = new WeChatUser("用户B");
        WeChatUser user3 = new WeChatUser("用户C");

        // 用户关注公众号
        wechatAccount.attach(user1);
        wechatAccount.attach(user2);
        wechatAccount.attach(user3);

        // 发布新内容
        wechatAccount.notifyObservers("今天的天气真不错!");

        // 用户取消关注
        wechatAccount.detach(user2);

        // 再次发布新内容
        wechatAccount.notifyObservers("明天有一场特别活动!");
    }
}

1.3 特点

1. 优点

  • 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
  • 被观察者发送通知,所有注册的观察者都会收到信息(可以实现广播机制)。

2. 缺点

  • 如果观察者非常多的话,那么所有的观察者收到被观察者发送的通知会耗时。
  • 如果被观察者有循环依赖的话,那么被观察者发送通知会使观察者循环调用,会导致系统崩溃。

3. 使用场景

  • 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
  • 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面时。

1.4 JDK中的实现

在Java中,通过iava.util. Observable类和java.util. observer接口定义了观察者模式,只要实现它们的子类就可以编写 观察者模式实例。

1. Observable 类

Observable 类是抽象目标类(被观察者),它有一个 Vector 集合成员变量,用于保存所有要通知的观察者对象。它的三个重要方法如下:

  • void addObserver(Observer o)

    用于将新的观察者对象添加到集合中。

  • void notifyObservers(Object arg)

    调用集合中的所有观察者对象的 update 方法,通知它们数据发生改变。通常,越晚加入集合的观察者越先得到通知。

  • void setChanged()

    用来设置一个 boolean 类型的内部标志,注明目标对象发生了变化。当它为 true 时,notifyObservers() 才会通知观察者。

2. Observer 接口

Observer 接口是抽象观察者,它监视目标对象的变化。当目标对象发生变化时,观察者会得到通知,并调用 update 方法进行相应的工作。

3. 例子

java 复制代码
import java.util.Observable;
import java.util.Observer;

// 具体的被观察者类
class WeatherData extends Observable {
    private float temperature;

    public void setTemperature(float temperature) {
        this.temperature = temperature;
        setChanged(); // 标记状态已更改
        notifyObservers(temperature); // 通知观察者
    }
}

// 具体的观察者类
class CurrentConditionsDisplay implements Observer {
    private float temperature;

    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherData) {
            this.temperature = (float) arg; // 更新温度
            display(); // 显示当前条件
        }
    }

    public void display() {
        System.out.println("Current temperature: " + temperature + " degrees.");
    }
}

// 主程序
public class WeatherStation {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay();

        weatherData.addObserver(currentDisplay); // 注册观察者
        weatherData.setTemperature(25.5f); // 更新温度,触发通知
        weatherData.setTemperature(30.0f); // 再次更新温度
    }
}

二、模板设计模式

模板方法设计模式( Template Method Design Pattern ),在一个方法中定义一个算法骨架(即模板),并将某些步骤推迟到子类中实现。

该模式在父类中定义一个方法的骨架(或算法的框架),并允许子类在不改变算法结构的情况下重新定义该算法的某些步骤。

举例:

  • 创建一个抽象类,定义算法骨架:
java 复制代码
// 抽象类,定义制作饮料的模板方法
abstract class Beverage {

    // 模板方法,定义了制作饮料的通用步骤
    public final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        if (customerWantsCondiments()) { // 钩子方法,子类可以选择重写
            addCondiments();
        }
    }

    // 具体步骤:烧水
    private void boilWater() {
        System.out.println("Boiling water");
    }

    // 具体步骤:倒入杯中
    private void pourInCup() {
        System.out.println("Pouring into cup");
    }

    // 抽象步骤:冲泡饮料,具体实现由子类提供
    protected abstract void brew();

    // 抽象步骤:添加调料,具体实现由子类提供
    protected abstract void addCondiments();

    // 钩子方法:决定是否添加调料,子类可以选择重写
    protected boolean customerWantsCondiments() {
        return true; // 默认添加调料
    }
}
  • 创建具体的子类,实现抽象类中定义的抽象方法:
java 复制代码
// 具体类:茶
class Tea extends Beverage {

    @Override
    protected void brew() {
        System.out.println("Steeping the tea");
    }

    @Override
    protected void addCondiments() {
        System.out.println("Adding lemon");
    }

    // 重写钩子方法,茶默认不添加调料
    @Override
    protected boolean customerWantsCondiments() {
        return false;
    }
}
java 复制代码
// 具体类:咖啡
class Coffee extends Beverage {

    @Override
    protected void brew() {
        System.out.println("Dripping coffee through filter");
    }

    @Override
    protected void addCondiments() {
        System.out.println("Adding sugar and milk");
    }
}
  • 客户端代码:
java 复制代码
// 客户端代码
public class TemplateMethodPatternExample {
    public static void main(String[] args) {
        System.out.println("Preparing tea:");
        Beverage tea = new Tea();
        tea.prepareRecipe();

        System.out.println("\nPreparing coffee:");
        Beverage coffee = new Coffee();
        coffee.prepareRecipe();
    }
}
  • 输出:
plaintext 复制代码
Preparing tea:
Boiling water
Steeping the tea
Pouring into cup

Preparing coffee:
Boiling water
Dripping coffee through filter
Pouring into cup
Adding sugar and milk
  • Beverage 抽象类定义了模板方法prepareRecipe,并封装了制作饮料的固定步骤
  • brew()addCondiments() 是由子类实现的抽象方法,各自实现了茶和咖啡的制作过程
  • 钩子方法 customerWantsCondiments()允许子类控制是否执行某个步骤

三、单例设计模式

单例模式有以下特点:

  1. 单例类只能有一个实例。
  2. 单例类必须自己创建自己的唯一实例。
  3. 单例类必须给所有其他对象提供这一实例。

一、懒汉式单例

在第一次访问时实例化自己

java 复制代码
//懒汉式单例类.在第一次调用的时候实例化自己 
public class Singleton {
    private Singleton() {}
    private static Singleton single=null;
    //静态工厂方法 
    public static Singleton getInstance() {
         if (single == null) {  
             single = new Singleton();
         }  
        return single;
    }
}

Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。

如果走到single == null时发生线程切换,会存在线程不安全的问题。

有三种方式确保懒汉式单例的线程安全问题:

  1. 在getInstance上加上同步:
java 复制代码
public static synchronized Singleton getInstance() {
         if (single == null) {  
             single = new Singleton();
         }  
        return single;
}
  1. 双重检查锁定
java 复制代码
public static Singleton getInstance() {
        if (singleton == null) {  
            synchronized (Singleton.class) {  
               if (singleton == null) {  
                  singleton = new Singleton(); 
               }  
            }  
        }  
        return singleton; 
}
  1. 静态内部类
java 复制代码
public class Singleton {  
    private static class LazyHolder {  
       private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
       return LazyHolder.INSTANCE;  
    }  
}  
  • 静态内部类的特点
    • LazyHolder 是一个静态内部类,它只有在第一次被访问时才会被加载和初始化。
    • 类加载是线程安全的,JVM 在加载类时会确保类加载的线程安全性。
  • INSTANCE 的初始化
    • LazyHolder 内的 INSTANCE 是一个 static final 的变量,它在 LazyHolder 被加载时完成初始化。
    • static final 保证了 INSTANCE 的初始化只会发生一次,并且由 JVM 保证线程安全。
  • 特点:延迟加载,线程安全,利用类加载机制实现,推荐使用。

第三种比1、2好,即实现了线程安全,又避免了同步带来的性能影响

二、饿汉式单例

饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。

java 复制代码
//饿汉式单例类.在类初始化时,已经自行实例化 
public class Singleton1 {
    private Singleton1() {}
    private static final Singleton1 single = new Singleton1();
    //静态工厂方法 
    public static Singleton1 getInstance() {
        return single;
    }
}
  • 特点:线程安全,类加载时初始化,简单实现,但可能会造成资源浪费。

四、Builder模式

4.1 概述

Builder 模式是一种创建型设计模式,通过逐步构建复杂对象,将对象的构建过程与表示分离,使得同样的构建过程可以创建不同的对象。

特点

  1. 将对象构建与其表示分离:
    • 构建复杂对象时,不直接实例化对象,而是使用 Builder 逐步设置属性,最后通过 build() 方法生成对象。
  2. 灵活性和可扩展性:
    • 新增属性或功能只需修改 Builder 类,而无需更改已有的构造代码。
  3. 链式调用:
    • Builder 模式允许链式设置属性,代码可读性更强。

4.2 结构

  1. 产品类(Product)
    • 要创建的复杂对象。
    • 包含多个属性,通常是不可变的。
  2. 构建器类(Builder)
    • 定义构建对象的步骤,每个步骤设置对象的一个属性。
    • 提供 build() 方法返回完整的产品对象。
  3. 具体构建器(ConcreteBuilder)
    • 实现构建器类,包含构建对象的具体逻辑。
  4. 指导者类(Director,可选)
    • 用于控制构建过程的顺序和逻辑。
    • 实际应用中常常省略,由客户端直接调用 Builder。

4.3 具体实现

比如我们现在要自定义一个http发送请求连接:

传统的方式(构造函数)

http对象代码为:

java 复制代码
public class HttpRequest {
    private String url;
    private String method;
    private Map<String, String> headers;
    private String body;

    public HttpRequest(String url, String method, Map<String, String> headers, String body) {
        this.url = url;
        this.method = method;
        this.headers = headers != null ? headers : new HashMap<>();
        this.body = body;
    }

    @Override
    public String toString() {
        return "HttpRequest { " +
               "url='" + url + '\'' +
               ", method='" + method + '\'' +
               ", headers=" + headers +
               ", body='" + body + '\'' +
               " }";
    }
}

构建方式:

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 构建请求
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        headers.put("Authorization", "Bearer token123");

        HttpRequest request = new HttpRequest(
                "https://api.example.com/data", // URL
                "POST",                         // 方法
                headers,                        // Headers
                "{\"key\":\"value\"}"           // Body
        );

        System.out.println(request);
    }
}

问题分析

  • 构造器参数过多时,易混淆或出错,特别是可选参数场景。
  • 代码可读性差

传统方式(Set函数)

http对象代码:

java 复制代码
public class HttpRequest {
    private String url;
    private String method;
    private Map<String, String> headers = new HashMap<>();
    private String body;

    public void setUrl(String url) {
        this.url = url;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public void setHeader(String key, String value) {
        this.headers.put(key, value);
    }

    public void setBody(String body) {
        this.body = body;
    }

    @Override
    public String toString() {
        return "HttpRequest { " +
               "url='" + url + '\'' +
               ", method='" + method + '\'' +
               ", headers=" + headers +
               ", body='" + body + '\'' +
               " }";
    }
}

构建方式:

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 构建请求
        HttpRequest request = new HttpRequest();
        request.setUrl("https://api.example.com/data");
        request.setMethod("POST");
        request.setHeader("Content-Type", "application/json");
        request.setHeader("Authorization", "Bearer token123");
        request.setBody("{\"key\":\"value\"}");

        System.out.println(request);
    }
}

问题分析

  • 缺点: 无法保证对象的完整性(可能遗漏某些关键参数),容易导致不一致的状态

为什么改用 Builder 模式

  • 参数过多且部分可选时,传统方式可读性差。
  • Builder 模式可以链式调用,代码更清晰。
  • Builder 可封装复杂的初始化逻辑,确保对象的一致性和不可变性。

改进后的builder:

  1. PRODUCT
java 复制代码
public class HttpRequest {
    private String url;        // 必填
    private  String method;     // 必填
    private Map<String, String> headers; // 可选
    private String body;       // 可选

    // 私有构造器,确保只能通过 Builder 创建
    private HttpRequest(String url, String method, Map<String, String> headers, String body) {
        this.url = url;
        this.method = method;
        this.headers = headers;
        this.body = body;
    }

    // Getters
    public String getUrl() { return url; }
    public String getMethod() { return method; }
    public Map<String, String> getHeaders() { return headers; }
    public String getBody() { return body; }
}
  1. 构建器类(Builder)
java 复制代码
public interface Builder {
    Builder setUrl(String url);
    Builder setMethod(String method);
    Builder addHeader(String key, String value);
    Builder setBody(String body);
    HttpRequest build();
}
  1. 具体构建器(ConcreteBuilder)
java 复制代码
import java.util.HashMap;
import java.util.Map;

public class HttpRequestBuilder implements Builder {
    private String url;
    private String method;
    private Map<String, String> headers = new HashMap<>();
    private String body;

    @Override
    public Builder setUrl(String url) {
        this.url = url;
        return this;
    }

    @Override
    public Builder setMethod(String method) {
        this.method = method;
        return this;
    }

    @Override
    public Builder addHeader(String key, String value) {
        this.headers.put(key, value);
        return this;
    }

    @Override
    public Builder setBody(String body) {
        this.body = body;
        return this;
    }

    @Override
    public HttpRequest build() {
        // 校验必填参数
        if (url == null || method == null) {
            throw new IllegalStateException("URL and Method are required!");
        }
        return new HttpRequest(url, method, headers, body);
    }
}
  1. 指导者类(Director,可选)
java 复制代码
public class HttpRequestDirector {
    private final Builder builder;

    public HttpRequestDirector(Builder builder) {
        this.builder = builder;
    }

    public HttpRequest constructGetRequest(String url) {
        return builder.setUrl(url)
                      .setMethod("GET")
                      .build();
    }

    public HttpRequest constructPostRequest(String url, String body) {
        return builder.setUrl(url)
                      .setMethod("POST")
                      .setBody(body)
                      .addHeader("Content-Type", "application/json")
                      .build();
    }
}
  1. 客户端代码
java 复制代码
public class Main {
    public static void main(String[] args) {
        // 使用 Builder 直接构建
        HttpRequest request1 = new HttpRequestBuilder()
                .setUrl("https://api.example.com/resource")
                .setMethod("GET")
                .addHeader("Authorization", "Bearer token123")
                .build();

        System.out.println("Request1: " + request1.getUrl());

        // 使用 Director 构建
        HttpRequestDirector director = new HttpRequestDirector(new HttpRequestBuilder());
        HttpRequest request2 = director.constructPostRequest("https://api.example.com/resource", "{\"key\":\"value\"}");

        System.out.println("Request2: " + request2.getBody());
    }
}

Builder 的优势

  1. 强制性:
    • 必须设置关键参数(如 URL、method),否则无法创建对象。
  2. 校验性:
    • build() 方法集中校验,确保对象状态一致。
  3. 可读性:
    • 链式调用使代码更直观,避免参数顺序错误。

通过这种方式,Builder 模式能够显著提升代码的安全性和可靠性,避免遗漏关键参数的问题。

4.4 使用场景

  1. 参数多且部分可选的对象构建
    • 如配置类、网络请求对象(如 oKHttp)、数据库连接对象等。
  2. 不可变对象的构建
    • 需要在对象创建后禁止修改时,如 StringBuilderImmutable 对象。
  3. 复杂初始化逻辑的对象
    • 如需要组合多个子对象或处理复杂逻辑的初始化。
  4. 跨平台对象创建
    • 如需要动态生成 JSON、XML 等结构。
  5. 避免构造器参数过多
    • 如果构造器参数多且顺序容易搞混,Builder 模式可以显著改善代码可读性和安全性。
相关推荐
考虑考虑20 分钟前
Jpa使用union all
java·spring boot·后端
用户37215742613542 分钟前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊2 小时前
Java学习第22天 - 云原生与容器化
java
渣哥4 小时前
原来 Java 里线程安全集合有这么多种
java
间彧4 小时前
Spring Boot集成Spring Security完整指南
java
间彧4 小时前
Spring Secutiy基本原理及工作流程
java
数据智能老司机5 小时前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
Java水解5 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
数据智能老司机6 小时前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
洛小豆7 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试