设计模式-服务定位器模式
- 一、背景
- 1.1 服务定位模式
- 1.2 策略模式
- 二、代码实战
- 2.1 服务定位器
- 2.2 配置ServiceLocatorFactoryBean
- 2.3 定义一个支付的接口
- 2.4 根据不同类型处理Bean
- 2.5 controller层
- 三、项目结构及测试结果
- 3.1 测试结果
- 3.2 项目结构及源码(欢迎star)
- 四、参考资料
一、背景
项目开发中如果让你设计支付模块,目前有支付宝、微信、各大银行的部分,你会如何设计支付这块的代码?在调用的客户端一般都是用if else去做判断,比如类型等于ALIPAY,我就用支付宝的实现逻辑处理。那如果新加一种支付方式,是不是调用的客户端还要修改呢?这显然太耦合了,更高级一点,可以用策略模式去实现。这篇博文使用“策略模式的升级版本”,服务定位模式Service Locator Pattern来解决,它帮助我们消除紧耦合实现及其依赖性,并提出将服务与其具体类解耦。
1.1 服务定位模式
服务定位器模式(Service Locator Pattern)属于J2EE 模式。用在我们想使用 JNDI 查询定位各种服务的时候,考虑到为某个服务查找 JNDI 的代价很高,服务定位器模式充分利用了缓存技术。在首次请求某个服务时,服务定位器在 JNDI 中查找服务,并缓存该服务对象。当再次请求相同的服务时,服务定位器会在它的缓存中查找,这样可以在很大程度上提高应用程序的性能。以下是这种设计模式的实体。
(1)服务(Service) - 实际处理请求的服务。对这种服务的引用可以在 JNDI 服务器中查找到。
(2)Context / 初始的Context - JNDI Context 带有对要查找的服务的引用。
(3)服务定位器(Service Locator) - 服务定位器是通过JNDI 查找和缓存服务来获取服务的单点接触。
(4)缓存(Cache) - 缓存存储服务的引用,以便复用它们。
(5)客户端(Client) -Client 是通过 ServiceLocator 调用服务的对象。
1.2 策略模式
在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
主要解决: 在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。
何时使用: 一个系统有许多许多类,而区分它们的只是他们直接的行为。
应用实例:
诸葛亮的锦囊妙计,每一个锦囊就是一个策略。
旅行的出游方式,选择骑自行车. 坐汽车,每一种旅行方式都是一个策略。
JAVA AWT 中的 LayoutManager。
优点:
算法可以自由切换。
避免使用多重条件判断。
扩展性良好。
缺点:
策略类会增多。
所有策略类都需要对外暴露。
使用场景:
如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。一个系统需要动态地在几种算法中选择一种。
如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
二、代码实战
2.1 服务定位器
服务定位器接口PayParserFactory, 它有一个接受内容类型参数并返回PayService的方法。
package com.it.service;
import org.springframework.stereotype.Component;
/**
* @description: 支付服务定位器工厂接口
*/
@Component
public interface PayParserFactory {
/**
* 服务定位器
* @param payType 支付类型
* @return 返回具体的支付处理实现类
*/
PayService getPayParser(String payType);
}
2.2 配置ServiceLocatorFactoryBean
通过创建配置类,配置ServiceLocatorFactoryBean使用PayParserFactory作为服务定位器接口,PayParserFactory这个接口不需要写实现类。
package com.it.config;
import com.it.service.PayParserFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.ServiceLocatorFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @description: 支付解析器配置
*/
@Configuration
public class PayParserConfig {
/**
* 初始化 payParserFactory bean
* @return
*/
@Bean("payParserFactory")
public FactoryBean serviceLocatorFactoryBean() {
ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();
// 设置服务定位接口
factoryBean.setServiceLocatorInterface(PayParserFactory.class);
return factoryBean;
}
}
2.3 定义一个支付的接口
package com.it.service;
/**
* @description: 支付服务接口
*/
public interface PayService {
//支付接口
boolean pay(String orderId);
}
2.4 根据不同类型处理Bean
设置解析器Bean的名称为类型名称,方便服务定位。
支付宝支付
package com.it.service.impl;
import com.it.service.PayService;
import org.springframework.stereotype.Component;
/**
* @description: 支付宝支付实现层
*/
@Component("AliPay")
public class AliPayServiceImpl implements PayService {
@Override
public boolean pay(String orderId) {
System.out.println("支付宝支付-----------------------");
return true;
}
}
微信支付
package com.it.service.impl;
import com.it.service.PayService;
import org.springframework.stereotype.Component;
/**
* @description: 微信支付实现层
*/
@Component("WxPay")
public class WxPayServiceImpl implements PayService {
@Override
public boolean pay(String orderId) {
System.out.println("微信支付-----------------------");
return true;
}
}
2.5 controller层
package com.it.controller;
import com.it.service.PayParserFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @description: 支付模块控制层
*/
@RestController
@RequestMapping("/pay")
public class PayController {
@Autowired
private PayParserFactory payParserFactory;
@PostMapping("/orderPay")
public boolean orderPay(@RequestParam String payType) {
// 关键点,直接根据类型获取
boolean result = payParserFactory.getPayParser(payType).pay("P202303013475492");
return result;
}
}
总结:
Spring 的ServiceLocatorFactoryBean实现了 FactoryBean接口,创建了Service Factory服务工厂Bean。我们通过使用服务定位器模式实现了一种扩展 Spring 控制反转的方法。它帮助我们解决了依赖注入未提供最佳解决方案的用例。也就是说,依赖注入仍然是首选,并且在大多数情况下不应使用服务定位器来替代依赖注入。
三、项目结构及测试结果
3.1 测试结果
输入localhost:9003/pay/orderPay?payType=WxPay
3.2 项目结构及源码(欢迎star)
源码下载springboo-cacheable,欢迎star!
四、参考资料
设计模式
设计模式简要介绍
服务定位器模式
设计模式-策略模式(服务定位器模式)
设计模式-服务定位器模式