背景
谈起设计模式,你一定会问?这玩意到底有啥用?我好像没用过也不影响做一个码农。也可能项目中实际用了,但是你不自知。虽然Java设计模式有23种,但是工作中常用的可能并没有那麽多。就像新华字典有多少字,你没必要都学一样。本章我们只谈常用的几种设计模式,通过设计模式的理念、规约、到应用,理解实战中如何正确使用设计模式,不论对面试还是实际工作中都有益处。
文章提纲
图片
设计理念
最为Java开发者,程序员基本修养名言绝句:
- 该露露,该藏藏
- 该封装的要封装
- 万事万物兼对象
- 程序代码要健壮
我们简单归纳为2个核心词:高内聚、低耦合。
很小的时候看过动画片,封神演义中哪吒:三头八臂显威力,千征百战斗魔法。(串台了。。。)
图片
我们根据这首歌词抽象一下,哪吒:三头八臂是静态特征,千征百战是动态技能。把这些特征归纳映射一下:类 = 属性 + 方法如下图:
图片
以上是高内聚的概念,什么是低耦合?
如果可能,我写一本神话《封神演戏》,说哪吒有:三头九臂。你肯定和我吵吵,要给它再配一把兵器。虽然还没想好是啥,但是有个总则:绝对不影响先前这八臂的演技。这就是低耦合!!!所谓程序健壮、拓展性强,也是这个道理。我们真诚地希望:
图片
上述例子不是特别恰当,但是对于设计模式,我们终级的理念是:封装变化的内容,保留不变的宗旨。
设计原则
设计原则可以归纳为2大类:
- 开闭原则(李氏替换,组合复用,依赖倒置)
规定:软件中的对象(类、模块、函数等等)应该对于扩展是开放的,但是对于修改是封闭的。换句话说,一个实体是允许在不改变它的源代码的前提下变更它的行为。
- 单一职责(接口隔离,迪米特法则)
规定:一个类只应该有一个职责,只有一个改变它的原因
Spring中的设计模式
在Spring框架中,各种设计模式被广泛应用以支持其强大的功能和灵活性。下面我将结合Spring的源码,鉴赏下Spring中常见的几种设计模式。
1. 单例模式
Spring框架中的Bean默认就是单例的。Spring IoC容器负责创建对象实例,并确保在整个应用中,针对同一个Bean的ID,只实例化一次对象。DefaultSingletonBeanRegistry类是Spring管理单例Bean的核心类。
// DefaultSingletonBeanRegistry类中的部分源码 public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { // ... private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); // ... @Override public Object getSingleton(String beanName) { return getSingleton(beanName, true); } protected Object getSingleton(String beanName, boolean allowEarlyReference) { // ... 省略部分代码 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return (singletonObject != NULL_OBJECT ? singletonObject : null); } // ... }
2. 工厂模式
Spring使用工厂模式通过BeanFactory、ApplicationContext等接口创建和管理Bean对象。DefaultListableBeanFactory是Spring中Bean工厂的实现类。
// DefaultListableBeanFactory类中的部分源码 public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable { // ... @Override public <T> T getBean(String name, Class<T> requiredType) throws BeansException { return doGetBean(name, requiredType, null, false); } @Override public <T> T getBean(Class<T> requiredType) throws BeansException { return doGetBean(null, requiredType, null, false); } // ... 省略部分代码 }
3. 代理模式
Spring AOP(面向切面编程)的实现就是基于代理模式。Spring创建目标对象的代理对象,并在代理对象中织入切面逻辑。JdkDynamicAopProxy和CglibAopProxy是Spring AOP中创建代理的两个核心类。
// JdkDynamicAopProxy类中的部分源码 public class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable { // ... @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // ... 省略部分代码 // 获取AdvisedSupport对象,包含了切面等AOP相关信息 final AdvisedSupport advised = this.advised; // ... 省略部分代码 // 获取拦截器链(切面链) List<Object> chain = advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // ... 省略部分代码 // 执行链式调用 return invokeJoinpointUsingReflection(target, method, args, targetClass, chain); } // ... }
4. 观察者模式(监听模式)
在Spring中,事件处理机制就是基于观察者模式实现的。当事件发生时,所有注册的观察者都会收到通知并作出响应。ApplicationEventMulticaster接口和SimpleApplicationEventMulticaster类是Spring事件处理机制的核心。
// SimpleApplicationEventMulticaster类中的部分源码 public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster { // ... @Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {```java // 调用监听器的方法处理事件 invokeListener(listener, event); } } private void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) { ErrorHandler errorHandler = getErrorHandler(); if (errorHandler != null) { try { doInvokeListener(listener, event); } catch (Throwable err) { errorHandler.handleError(err); } } else { doInvokeListener(listener, event); } } private void doInvokeListener(ApplicationListener<?> listener, ApplicationEvent event) { try { // 调用监听器的onApplicationEvent方法 listener.onApplicationEvent(event); } catch (ClassCastException ex) { // ... 省略部分代码,处理类型不匹配异常 } } // ... }
5. 责任链模式
在Spring中,HandlerInterceptor和HandlerInterceptorAdapter等类在处理请求拦截时,采用的就是责任链模式。一个请求会按照定义的拦截器顺序,逐个被处理,直到找到对应的处理器或者遍历完所有的拦截器。
// HandlerInterceptor接口定义 public interface HandlerInterceptor { boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception; void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception; } // 实现HandlerInterceptor接口的自定义拦截器 public class CustomInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 在这里执行前置处理逻辑 return true; // 返回true表示继续向下执行,返回false表示中断请求 } // ... 其他方法实现 }
6. 模版模式
Spring中的JdbcTemplate、HibernateTemplate等类就是模版模式的典型应用。它们定义了一个操作数据库或Hibernate的骨架方法,允许子类在不改变算法结构的情况下重定义某些步骤的具体内容。
// JdbcTemplate部分源码 public class JdbcTemplate extends JdbcAccessor implements JdbcOperations, BeanFactoryAware { // ... public <T> T query(String sql, RowMapper<T> rowMapper) { return query(sql, new Object[0], rowMapper); } public <T> T query(String sql, Object[] args, RowMapper<T> rowMapper) { return query(sql, args, rowMapper, true); } // ... 省略部分代码,这里是模版方法的实现 // 真正的SQL执行和结果集处理逻辑在这里,但是允许子类通过RowMapper来定制结果集的处理方式 // ... } // 自定义RowMapper实现 public class CustomRowMapper implements RowMapper<MyObject> { @Override public MyObject mapRow(ResultSet rs, int rowNum) throws SQLException { // 在这里定制如何从ResultSet中映射到MyObject对象 return new MyObject(/* 映射逻辑 */); } }
这些设计模式在Spring框架中被广泛应用,能够灵活地应对各种复杂场景,提供强大且可扩展的功能。
实战应用
假设有这样一个需求:
- 业务登录商城用户鉴权
- 购买产品下订单
- 校验订单填写是否合法
- 记录接口中的参数
- 订单确认后给买家发短信通知
根据业务场景,我们大致可拆分为:用户流程、订单流程
用户登录流程
在用户登录流程中,可能用到拦截器做鉴权校验,日志记录接口参数等,使用了一些常见的设计模式。
场景一、用户鉴权校验
责任链模式
拦截器通常按照定义的顺序执行,每个拦截器检查特定的条件或执行特定的任务。
// 拦截器接口 public interface Interceptor { boolean intercept(AuthenticationContext context); } // 用户校验拦截器 public class UserValidationInterceptor implements Interceptor { @Override public boolean intercept(AuthenticationContext context) { // 用户校验逻辑 if (isValidUser(context.getUser())) { return true; } return false; } private boolean isValidUser(User user) { // 校验用户是否有效 return true; // 示例,实际中应有具体校验逻辑 } } // 鉴权校验拦截器 public class AuthorizationInterceptor implements Interceptor { @Override public boolean intercept(AuthenticationContext context) { // 鉴权校验逻辑 if (isAuthorized(context.getUser(), context.getCredentials())) { return true; } return false; } private boolean isAuthorized(User user, Credentials credentials) { // 校验用户是否有权限 return true; // 示例,实际中应有具体校验逻辑 } } // 拦截器链 public class InterceptorChain { private List<Interceptor> interceptors = new ArrayList<>(); public void addInterceptor(Interceptor interceptor) { interceptors.add(interceptor); } public boolean execute(AuthenticationContext context) { for (Interceptor interceptor : interceptors) { if (!interceptor.intercept(context)) { // 如果拦截器返回false,则中断链的执行 return false; } } return true; } }
场景二、记录用户登录信息
单例模式
日志记录器通常设计为单例,确保全局只有一个实例。
Logger logger = LoggerFactory.getLoggerFactoryInstance().getLogger(); logger.log("This is a user log message");
在这行代码中,体现了单例模式的核心思想,确保了无论多少次调用LoggerFactory.getLoggerFactoryInstance(),都只会返回一个LoggerFactory实例。事实上,我们从源码中也可以看到。
订单流程
根据业务场景,核心订单流程如下:
场景三、订单校验
工厂模式
使用工厂模式实现的CheckOrderFactory,它用于创建不同类型的订单校验服务实例。
同时,我们定义一个校验接口ICheckOrderService,并创建了两个实现类:购买数量校验:CountCheckOrder和订单参数校验:ParamCheckOrder。
public interface ICheckOrderService { boolean checkOrder(Object order); String getErrorMessage(); }
购买数量校验的实现类CountCheckOrder:
public class CountCheckOrder implements ICheckOrderService { @Override public boolean checkOrder(Object order) { // 假设order是一个包含购买数量的对象 int quantity = ((Order) order).getQuantity(); return quantity > 0; // 只允许购买数量大于0 } @Override public String getErrorMessage() { return "购买数量必须大于0。"; } }
订单参数校验的实现类ParamCheckOrder:
public class ParamCheckOrder implements ICheckOrderService { @Override public boolean checkOrder(Object order) { // 假设order是一个包含各种订单参数的对象 // 这里可以添加具体的订单参数校验逻辑 return true; // 示例代码,默认返回true } @Override public String getErrorMessage() { return "订单参数校验失败。"; } }
工厂类CheckOrderFactory,用于创建不同类型的校验服务实例:
public class CheckOrderFactory { public static ICheckOrderService createCheckOrderService(String type) { switch (type) { case "count": return new CountCheckOrder(); case "param": return new ParamCheckOrder(); default: throw new IllegalArgumentException("不支持的校验类型: " + type); } } }
使用工厂模式有利于业务类的实现和拓展,但是有时候也存在过度设计,导致写了很多的业务类。
场景四、短信通知
观察者模式(监听模式)
在Spring框架中,我们可以使用ApplicationEvent和ApplicationListener来实现事件发布和监听的功能。
假设我们要下发一个购买成功的短信提醒,那么就可以发布一个自定义的PurchaseSuccessEvent事件。
import org.springframework.context.ApplicationEvent; public class PurchaseSuccessEvent extends ApplicationEvent { private final String buyerPhoneNumber; private final String orderId; public PurchaseSuccessEvent(Object source, String buyerPhoneNumber, String orderId) { super(source); this.buyerPhoneNumber = buyerPhoneNumber; this.orderId = orderId; } public String getBuyerPhoneNumber() { return buyerPhoneNumber; } public String getOrderId() { return orderId; } }
接着定义一个SmsNotificationListener类,它实现了ApplicationListener接口,用于监听PurchaseSuccessEvent事件:
import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; @Component public class SmsNotificationListener implements ApplicationListener<PurchaseSuccessEvent> { @Override public void onApplicationEvent(PurchaseSuccessEvent event) { String message = "亲爱的买家,您的订单 " + event.getOrderId() + " 购买成功!"; sendSms(event.getBuyerPhoneNumber(), message); } private void sendSms(String phoneNumber, String message) { // 在这里实现发送短信的逻辑 System.out.println("Sending SMS to " + phoneNumber + ": " + message); } }
然后,在Spring的配置中启用事件发布功能。配置一个ApplicationEventPublisher的bean:
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.GenericApplicationContext; @Configuration public class AppConfig { @Bean public ApplicationEventPublisher applicationEventPublisher() { return new GenericApplicationContext(); } }
最后,在业务逻辑中发布PurchaseSuccessEvent事件。
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; @Service public class PurchaseService { private final ApplicationEventPublisher applicationEventPublisher; @Autowired public PurchaseService(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } public void completePurchase(String buyerPhoneNumber, String orderId) { // 模拟购买完成的业务逻辑 // ... // 发布购买成功事件 applicationEventPublisher.publishEvent(new PurchaseSuccessEvent(this, buyerPhoneNumber, orderId)); } }
总结
- 使用设计模式的宗旨:封装变化的部分,维护不变的宗旨
- 好处:提高代码拓展性,程序更优雅更健壮
- 对开源框架设计模式的使用,要提高鉴赏能力
- 考虑可读性,不可为了设计模式而过度设计
原文地址:https://mp.weixin.qq.com/s/d85I6n0qbbEWI9H6JmlXlg