Java设计模式
- 一、观察者设计模式
-
- [1.1 概述](#1.1 概述)
- [1.2 结构](#1.2 结构)
- [1.3 特点](#1.3 特点)
-
- [1. 优点](#1. 优点)
- [2. 缺点](#2. 缺点)
- [3. 使用场景](#3. 使用场景)
- [1.4 JDK中的实现](#1.4 JDK中的实现)
-
- [1. Observable 类](#1. Observable 类)
- [2. Observer 接口](#2. Observer 接口)
- [3. 例子](#3. 例子)
- 二、模板设计模式
- 三、单例设计模式
- 四、Builder模式
-
- [4.1 概述](#4.1 概述)
- [4.2 结构](#4.2 结构)
- [4.3 具体实现](#4.3 具体实现)
- [4.4 使用场景](#4.4 使用场景)
一、观察者设计模式
1.1 概述
观察者模式(Observer Pattern),又称发布-订阅模式,是一种行为设计模式。它定义了一种一对多的依赖关系,允许多个观察者对象同时监听某个主题对象(Subject)。当主题对象的状态发生变化时,它会通知所有注册的观察者,以便这些观察者能够自动更新自身的状态。
1.2 结构
观察者模式通常包含以下角色:
- Subject(主题) :
- 抽象主题角色,负责维护观察者的列表。
- 提供接口以添加和删除观察者。
- 通常包括方法如
attach(observer)
和detach(observer)
。
- ConcreteSubject(具体主题) :
- 实现了 Subject 接口的具体类,维护有关状态。
- 当状态变化时,调用通知方法以更新所有观察者。
- Observer(观察者) :
- 抽象观察者角色,定义更新接口,通常是
update()
方法。 - 观察者在收到主题变化的通知时调用该方法更新自身状态。
- 抽象观察者角色,定义更新接口,通常是
- 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()
允许子类控制是否执行某个步骤
三、单例设计模式
单例模式有以下特点:
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
一、懒汉式单例
在第一次访问时实例化自己
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
时发生线程切换,会存在线程不安全的问题。
有三种方式确保懒汉式单例的线程安全问题:
- 在getInstance上加上同步:
java
public static synchronized Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
- 双重检查锁定
java
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
- 静态内部类
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 模式是一种创建型设计模式,通过逐步构建复杂对象,将对象的构建过程与表示分离,使得同样的构建过程可以创建不同的对象。
特点:
- 将对象构建与其表示分离:
- 构建复杂对象时,不直接实例化对象,而是使用 Builder 逐步设置属性,最后通过 build() 方法生成对象。
- 灵活性和可扩展性:
- 新增属性或功能只需修改 Builder 类,而无需更改已有的构造代码。
- 链式调用:
- Builder 模式允许链式设置属性,代码可读性更强。
4.2 结构
- 产品类(Product) :
- 要创建的复杂对象。
- 包含多个属性,通常是不可变的。
- 构建器类(Builder) :
- 定义构建对象的步骤,每个步骤设置对象的一个属性。
- 提供
build()
方法返回完整的产品对象。
- 具体构建器(ConcreteBuilder) :
- 实现构建器类,包含构建对象的具体逻辑。
- 指导者类(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:
- 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; }
}
- 构建器类(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();
}
- 具体构建器(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);
}
}
- 指导者类(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();
}
}
- 客户端代码
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 的优势
- 强制性:
- 必须设置关键参数(如 URL、method),否则无法创建对象。
- 校验性:
build()
方法集中校验,确保对象状态一致。
- 可读性:
- 链式调用使代码更直观,避免参数顺序错误。
通过这种方式,Builder 模式能够显著提升代码的安全性和可靠性,避免遗漏关键参数的问题。
4.4 使用场景
- 参数多且部分可选的对象构建 :
- 如配置类、网络请求对象(如
oKHttp
)、数据库连接对象等。
- 如配置类、网络请求对象(如
- 不可变对象的构建 :
- 需要在对象创建后禁止修改时,如
StringBuilder
或Immutable
对象。
- 需要在对象创建后禁止修改时,如
- 复杂初始化逻辑的对象 :
- 如需要组合多个子对象或处理复杂逻辑的初始化。
- 跨平台对象创建 :
- 如需要动态生成 JSON、XML 等结构。
- 避免构造器参数过多 :
- 如果构造器参数多且顺序容易搞混,Builder 模式可以显著改善代码可读性和安全性。