模板方法模式(Template Method Pattern)是一种在软件工程中常用的设计模式,属于行为型模式。它的核心思想是在一个抽象类中定义一个操作中的算法的骨架(即一个模板),而将一些步骤的实现延迟到子类中。这样,可以在不改变算法结构的情况下,重新定义算法中的某些特定步骤。
关键特点
结构组成
使用场景
优点
缺点
接下来我们仿照JDBCTemplate编写一个模板。
要创建一个类似JdbcTemplate的简化版本,我们首先需要理解JdbcTemplate在Spring框架中的主要功能。JdbcTemplate主要用于简化JDBC操作,管理资源(如连接、语句和结果集)的打开和关闭,以及提供一个模板方法来执行SQL查询和更新操作。
以下是一个基础的模仿JdbcTemplate的实现。这个示例将包括几个关键组件:
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public abstract class SimpleJdbcTemplate {
// 假设有一个方法来获取数据库连接
protected abstract Connection getConnection() throws SQLException;
// 执行查询的模板方法
public T query(String sql, RowMapper rowMapper, Object... args) throws SQLException {
try (Connection conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
int paramIndex = 1;
for (Object arg : args) {
ps.setObject(paramIndex++, arg);
}
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
return rowMapper.mapRow(rs);
}
return null;
}
}
}
// 定义RowMapper接口
public interface RowMapper {
T mapRow(ResultSet rs) throws SQLException;
}
}
SimpleJdbcTemplate提供了一个query方法,这是一个模板方法,它接收SQL语句、RowMapper和参数,执行查询,并返回映射的结果。RowMapper是一个接口,需要用户实现,用于将ResultSet映射到想要的对象类型。
"观察者模式"是一种设计模式,用于软件开发中。在这种模式下,一个对象(称为“主题”)维护一个依赖对象列表(称为“观察者”),并在状态变化时自动通知它们。这种模式通常用于实现分布式事件处理系统,用于在对象之间传递消息或信息,而无需对象之间具有紧密的耦合关系。
观察者模式包括以下主要组件:
最常见的观察者模式的使用就是:配置中心。
配置中心的配置变更导致本地配置自动更新是观察者模式的一个典型应用场景。在这个场景中:
当配置中心的配置发生变化时,它会通知所有注册的观察者(即本地系统或服务),使它们能够相应地更新自己的配置。这样的机制确保了配置的一致性和实时性,同时降低了系统组件之间的耦合度。
例如,在微服务架构中,各个微服务可能依赖于一个共享的配置中心来管理其配置。当配置中心的配置更新时,所有依赖的微服务会自动获取最新配置,从而保证了系统的灵活性和可维护性。
这里简单描写一个配置中心的代码:
package blossom.project.designmode.observe;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
// 观察者接口
interface Observer {
void update(Map config);
}
// 配置中心类(主题)
class ConfigCenter {
private List observers = new ArrayList<>();
private Map config = new HashMap<>();
// 注册观察者
public void registerObserver(Observer observer) {
observers.add(observer);
}
// 移除观察者
public void removeObserver(Observer observer) {
observers.remove(observer);
}
// 通知所有观察者配置已更新
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(new HashMap<>(config));
}
}
// 设置配置项并通知观察者
public void setConfig(String key, String value) {
config.put(key, value);
notifyObservers();
}
}
// 具体观察者实现
class Service implements Observer {
private String name;
public Service(String name) {
this.name = name;
}
@Override
public void update(Map config) {
System.out.println("Service " + name + " received config update: " + config);
}
}
package blossom.project.designmode.observe;
import java.util.Random;
class ConfigChangeSimulator implements Runnable {
private ConfigCenter configCenter;
private Random random = new Random();
public ConfigChangeSimulator(ConfigCenter configCenter) {
this.configCenter = configCenter;
}
@Override
public void run() {
try {
while (true) {
// 模拟配置变更
String key = "key" + random.nextInt(10);
String value = "value" + random.nextInt(100);
configCenter.setConfig(key, value);
// 休眠一段时间以模拟配置更新间隔
Thread.sleep(3000);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public class ObserverPatternExample {
public static void main(String[] args) {
ConfigCenter configCenter = new ConfigCenter();
Service serviceA = new Service("A");
Service serviceB = new Service("B");
configCenter.registerObserver(serviceA);
configCenter.registerObserver(serviceB);
// 创建并启动配置变更模拟器
ConfigChangeSimulator simulator = new ConfigChangeSimulator(configCenter);
new Thread(simulator).start();
}
}
当配置中心配置发生变更的时候就会让本地的配置进行更新。
策略模式是一种行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。策略模式让算法独立于使用它的客户端而变化,即使客户端可以在运行时改变其算法。这种模式涉及到三个角色:
优点
缺点
使用场景
// 策略接口
interface SortingStrategy {
void sort(List list);
}
// 具体策略实现之一
class BubbleSortStrategy implements SortingStrategy {
@Override
public void sort(List list) {
System.out.println("Sorting using bubble sort");
// 实现冒泡排序
}
}
// 具体策略实现之二
class QuickSortStrategy implements SortingStrategy {
@Override
public void sort(List list) {
System.out.println("Sorting using quick sort");
// 实现快速排序
}
}
// 上下文角色
class SortedList {
private SortingStrategy strategy;
public void setSortingStrategy(SortingStrategy strategy) {
this.strategy = strategy;
}
public void sort(List list) {
strategy.sort(list);
}
}
// 使用策略模式
public class StrategyPatternDemo {
public static void main(String[] args) {
SortedList list = new SortedList();
list.setSortingStrategy(new BubbleSortStrategy());
list.sort(new ArrayList<>(Arrays.asList(1, 3, 2)));
list.setSortingStrategy(new QuickSortStrategy());
list.sort(new ArrayList<>(Arrays.asList(1, 3, 2)));
}
}
在这个例子中,SortedList充当上下文,可以设置不同的排序策略(BubbleSortStrategy或QuickSortStrategy),并使用这些策略对列表进行排序。这样,排序算法可以在运行时动态改变,且SortedList与具体的排序算法解耦。
但是更加合理的实现方式是使用单例模式和策略模式。
在工厂中为每种登录策略创建单一实例,并在请求时返回这些实例:
class LoginStrategyFactory {
private static final LoginStrategyFactory instance = new LoginStrategyFactory();
private Map strategies;
private LoginStrategyFactory() {
strategies = new HashMap<>();
strategies.put("usernamePassword", new UsernamePasswordLoginStrategy());
strategies.put("wechat", new WeChatLoginStrategy());
// 初始化其他策略
}
public static LoginStrategyFactory getInstance() {
return instance;
}
public LoginStrategy getLoginStrategy(String type) {
if (!strategies.containsKey(type)) {
throw new IllegalArgumentException("Unknown login type: " + type);
}
return strategies.get(type);
}
}
在这个工厂中,我们使用一个Map来存储每种类型的登录策略。工厂在初始化时就创建了所有类型的策略实例。当请求一个特定类型的策略时,工厂返回预先创建的实例,而不是每次都创建新的实例。
登录控制器(Controller)登录操作:
@RestController
@RequestMapping("/auth")
public class LoginController {
@PostMapping("/login")
public ResponseEntity> login(@RequestParam String type, @RequestBody Map credentials) {
LoginStrategyFactory factory = LoginStrategyFactory.getInstance();
LoginStrategy strategy = factory.getLoginStrategy(type);
strategy.login(credentials.get("username"), credentials.get("password"));
return ResponseEntity.ok().body("Login successful using " + type);
}
}
策略模式在源码中的使用还是比较常见的,比如我们的Comparator,Arrays的sort,TreeMap的排序底层都用到了策略模式的思想。
同时,在我们的spring中,我们知道spring可以读取多个位置的配置文件,比如resource,jvm,environment等。同时,如果我们配置了nacos配置中心,也可以从配置中心进行配置读取。
策略模式是一个非常常用的模式,我们的实际编码过程可能没有意识到我们用到了这种模式。
责任链模式是一种行为型设计模式,它使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。在这个模式中,这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
工作机制
角色
优点
缺点
使用场景
我们最创建的责任链的使用场景就是网关。
在Spring Cloud Gateway中,使用责任链模式使得处理HTTP请求的过程变得非常灵活和强大。开发者可以根据具体需求选择合适的过滤器,并定义它们在请求处理过程中的顺序。这种模式也使得添加新的过滤器或修改现有过滤器链变得简单,从而可以轻松地扩展和维护整个网关的功能。
我们知道,如果当前的过滤器需要处理当前请求,就会执行实际的处理方法,如果不需要进行处理,就直接return chain.doFilter()方法直接将当前请求传送到下一个过滤器去进行处理,这就是责任链模式,我需要对你进行处理我就对你进行处理,如果我不需要,那么我就不管你直接放你过去。
并且我们也知道,我们可以手动的添加和减少过滤器,也就是可插拔。
我的网关项目中的过滤器链就是用到责任链模式。
点击访问【BlossomGateway网关项目中的责任链】
这里还是简单的写一个责任链模式的代码:
要实现一个类似网关的责任链模式,其中过滤器可以从头到尾处理请求,然后再从尾到头逆向处理,我们需要定义一个过滤器接口,每个具体的过滤器实现这个接口。此外,我们还需要一个责任链类来管理这些过滤器。在这个实现中,我们将使用两个指针(或索引),一个用于正向遍历,一个用于反向遍历。
以下是实现的步骤和代码:
interface Filter {
void doFilter(Request request, Response response, FilterChain chain);
}
class Request {
// 请求的数据和方法
}
class Response {
// 响应的数据和方法
}
class FilterChain {
private List filters = new ArrayList<>();
private int index = 0; // 正向过滤器索引
private int reverseIndex; // 反向过滤器索引
public FilterChain addFilter(Filter filter) {
filters.add(filter);
reverseIndex = filters.size() - 1; // 初始化反向索引
return this;
}
public void doFilter(Request request, Response response) {
if (index < filters.size()) {
Filter filter = filters.get(index++);
filter.doFilter(request, response, this);
} else if (reverseIndex >= 0) {
Filter filter = filters.get(reverseIndex--);
filter.doFilter(request, response, this);
}
}
}
class AuthenticationFilter implements Filter {
@Override
public void doFilter(Request request, Response response, FilterChain chain) {
System.out.println("Authentication check");
chain.doFilter(request, response); // 继续调用链中的下一个过滤器
}
}
class LoggingFilter implements Filter {
@Override
public void doFilter(Request request, Response response, FilterChain chain) {
System.out.println("Logging request");
chain.doFilter(request, response); // 继续调用链中的下一个过滤器
}
}
public class Gateway {
public static void main(String[] args) {
FilterChain chain = new FilterChain();
chain.addFilter(new AuthenticationFilter())
.addFilter(new LoggingFilter());
Request request = new Request();
Response response = new Response();
chain.doFilter(request, response);
}
}
在这个示例中,FilterChain类维护了一个过滤器列表,并实现了doFilter方法,该方法负责依次调用过滤器。正向遍历时,index用于跟踪当前过滤器的位置;当所有过滤器处理完毕后,reverseIndex用于从尾部开始反向遍历过滤器。每个具体的过滤器(如AuthenticationFilter和LoggingFilter)在执行完自己的逻辑后,通过调用**chain.doFilter(request, response)**将控制权传递给链中的下一个过滤器。
这种设计使得请求可以按顺序经过所有的过滤器,并在处理完所有过滤器后,按相反的顺序返回。这在一些需要在请求处理前后执行不同逻辑的场景(如安全检查、日志记录、资源清理等)中非常有用。
状态模式是一种行为设计模式,它允许一个对象在其内部状态改变时改变它的行为。这种模式将每个状态的行为封装到对应的状态类中,使得对象在运行时可以看似改变其类。
核心思想:
组件:
假设有一个文档处理应用,文档可以处于不同的状态(草稿、审阅、发布):
javaCopy code
// 状态接口
interface DocumentState {
void publish();
void render();
}
// 具体状态:草稿
class Draft implements DocumentState {
@Override
public void publish() {
System.out.println("Draft published, now in review state.");
}
@Override
public void render() {
System.out.println("Render draft document.");
}
}
// 具体状态:审阅
class Review implements DocumentState {
@Override
public void publish() {
System.out.println("Review completed, now document is published.");
}
@Override
public void render() {
System.out.println("Render document for review.");
}
}
// 上下文类
class Document {
private DocumentState state;
public Document() {
this.state = new Draft(); // 初始状态为草稿
}
public void setState(DocumentState state) {
this.state = state;
}
public void publish() {
state.publish();
if (state instanceof Draft) {
setState(new Review());
} else if (state instanceof Review) {
setState(new Published());
}
}
public void render() {
state.render();
}
}
实现思路说明:
优点:
缺点:
使用场景:
状态模式(State Pattern)与策略模式(Strategy Pattern)在结构上非常相似,这可能是我们感觉它们很像的原因。尽管这两种模式在结构上相似,但它们的应用场景和目的存在显著差异。
相似性
差异性
实例对比
考虑一个文本编辑器的例子:
总的来说,尽管状态模式和策略模式在结构上相似,它们的应用场景和目标却有所不同。状态模式侧重于对象状态的管理和基于状态的行为变化,而策略模式侧重于定义一系列可互换的算法或策略。