目录
一、外观模式
1、外观模式特点
- 为系统对外提供一个统一的入口,可以对客户端隐藏子系统内部实现的细节,也降低了客户端与子系统类之间的耦合度;
- 例如Spring MVC中的 DispaterServlet,所有的Controller都是通过DispaterServlet统一暴露。
2、应用举例
场景一:
比如,一个项目团队有需求人员,开发人员,测试人员等组成,从需求,前后端开发,测试,部署上线的每一步,他们按照各自负责的内容进行产品开发就行。但是项目越来越大,问题越来越多的时候,需要一个技术组长去协调问题,解决遇到的问题,控制项目进度,组长在外观模式中起到了统一整个团队的作用,什么问题都可以通过他来协调解决。
// 开发人员
public class Developer {
public void develop(){
System.out.println("有一个开发的问题??");
}
}
// 测试人员
public class Tester {
public void test(){
System.out.println("有一个测试的问题.....");
}
}
// 技术组长:收集开发与测试的问题,统一解决
public class Leader {
Developer developer = new Developer();
Tester tester = new Tester();
public void processProblem() {
developer.develop();
tester.test();
}
}
场景二:
又比如Java Web项目开发中常常用到Controller、Service、Dao(或者Mapper)这种开发模式,它的本质是一种外观模式。
- Controller层相当于一个统一的入口,不同的业务处理类XxxService的实例通过注解@Autowired注入到Spring IOC容器中,并进行封装操作。
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private WorkSenderService workSenderService;
}
-
同理Service层也相当于一个统一的入口,不同的处理数据库操作的XxxDao或者XxxMapper的实例通过注解@Autowired注入到Spring IOC容器中,并进行业务处理。
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private GoodsMapper goodsMapper;
}
二、策略模式
1、策略模式特点
- 针对一组算法,将每一种算法都封装到具有共同接口的独立的类中,从而使它们可以相互替换,算法可以在不影响客户端的情况下发生变化,从而改变不同的功能;
- 客户端增加行为不用修改原有的代码,只要添加一种策略(或者行为)即可,体现了开闭原则;
- 策略模式改变了传统的封装继承方式,更多的体现了面向接口编程。
策略模式中有三种角色:
- 抽象策略角色:定义的是公共算法的接口。
- 具体策略角色:定义的是不同的算法策略的具体内容。
- 真实角色:持有公共算法的接口对象的引用,便于客户端使用不同的算法策略。
2、应用举例
比如加减乘除等数学运算,计算是一个共同的算法,而采取加法,减法等方式是不同的策略,如图所示:
-
Demo实现:
/** 定义抽象策略角色(公共算法的部分)*/
public interface CalcStrategy {
int calc(int i1,int i2); // 进行int类型的两数之间的运算
}
/**定义具体策略角色(实现不同算法的部分) */
public class AddStrategy implements CalcStrategy {
@Override
public int calc(int i1, int i2) {
return i1 + i2; // 加法运算策略
}
}
public class SubStrategy implements CalcStrategy{
@Override
public int calc(int i1, int i2) {
return i1 - i2; // 减法运算策略
}
}
public class MulStrategy implements CalcStrategy {
@Override
public int calc(int i1, int i2) {
return i1 * i2; // 乘法运算策略
}
}
/** 定义真实角色(持有抽象角色对象的引用,只有运行时才知道运用哪个运算策略,便于实现各自计算策略)*/
public class CalcClass {
// 通过构造器方式注入CalcStrategy接口对象
private CalcStrategy calcStrategy;
public CalcClass(CalcStrategy calcStrategy) {
this.calcStrategy = calcStrategy;
}
public int calculate(int a, int b) {
return calcStrategy.calc(a, b);
}
}
- 测试:
@Test
void testStrategy() {
CalcClass addCalc = new CalcClass(new AddStrategy());
logger.info("6+7=" + addCalc.calculate(6, 7));
CalcClass subCalc = new CalcClass(new SubStrategy());
logger.info("10-2=" + subCalc.calculate(10, 2));
CalcClass mulCalc = new CalcClass(new MulStrategy());
logger.info("10*2=" + mulCalc.calculate(10, 2));
}
// 运行结果:
6+7=13
10-2=8
10*2=20
- 如果需要增加除法,求余等运算,相应的增加DivStrategy、ModStrategy等运算策略即可,这样的策略设计模式符合开闭原则。
3、Java项目中使用策略模式场景
第一种:与Spring框架结合时,通过修改XML配置文件的配置,选择切换不同的策略。
<bean id="context" class = "com.it.xxwei.service.impl.Context">
<property name="stg" ref="stgB"/>
</bean>
<bean id="stgA" class = "com.it.xxwei.service.impl.StrategyA"/>
<bean id="stgB" class = "com.it.xxwei.service.impl.StrategyB"/>
第二种:Spring项目中,不同环境快速切换中间件,如Redis,开发环境使用的是单机版配置,而部署到生产环境则会使用集群配置,利用策略模式可以在部署时切换到集群版本。
思路:
- 定义一个JedisClient接口,将操作Redis的方法都放在接口里;
- 再分别定义单机类JedisClientPool、集群类JedisClientCluster,实现JedisClient接口并重写接口的操作方法;
- 而配置文件的话,将单机版Redis连接配置注释掉,保留集群配置即可。
注意:如SpringBoot项目,可以通过配置文件的spring.profiles.active配置快速切换不同的环境(如开发、测试、生产环境等)
第三种:容错恢复机制。有一个日志操作的场景:要求数据库正常连接时,将日志消息记录在数据库的日志表中,当数据库连接异常时,日志先记录在磁盘文件中,等待数据库连接正常时,将日志文件同步到数据库(可以通过定时任务实现)。
public interface LogStrategy {
public void log(String msg);
}
public class DbLog implements LogStrategy{
@Override
public void log(String msg){
// 日志记录到数据库
}
}
public class FileLog implements LogStrategy{
@Override
public void log(String msg){
// 日志记录到磁盘文件
}
}
public class LogContext {
public void recordLog(String msg) {
LogStrategy strategy = null;
try{
strategy = new DbLog();
strategy .log(msg);
}catch(Exception e){
// 数据库连接异常时,暂时记录在磁盘文件
strategy = new FileLog();
strategy .log(msg);
}
}
}
第四种:Java中使用Comparable和Comparator对引用类型对象排序比较的场景。
可以参考我的另一篇博客:https://blog.csdn.net/qq_29119581/article/details/111826702