设计模式
设计模式,学了要会用,从知名开源项目中学习设计模式更是事半功倍,提高我们的代码鉴赏能力,比如:
虽有二十三种设计模式,但日常编码常用的就构造器、模板设计方法、策略模式,外加责任链
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一, 这种类型的设计模式属于创建型模式,它的思想是提供一个工厂方法返回对象!可以理解为【代工厂】
联想到实际生活,公司会把具体产品类的信息和数据告诉工厂,让工厂去替他们生产出具体的产品而不是自己去生产具体的产品。以电脑产品为例:
Computer的抽象类public abstract class Computer {
private String name;
private double price;
public abstract void start();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
Mac和Huaiwei品牌的电脑继承和发展了Computer类public class Mac extends Computer{
@Override
public void start() {
System.out.println(getName() + "以非常优雅的方法启动了,展示了一个苹果logo");
}
}
------------------------------------------------------------------------
public class Huawei extends Computer{
@Override
public void start() {
System.out.println(getName() + "开机了,展示了华为的菊花图标~~~~");
}
}
Factory工厂专门为公司生产品牌电脑public class FactoryPattern {
/**
定义一个方法,创建对象返回
*/
public static Computer createComputer(String info){
switch (info){
case "huawei":
Computer c = new Huawei();
c.setName("huawei pro 16");
c.setPrice(5999);
return c;
case "mac":
Computer c2 = new Mac();
c2.setName("MacBook pro");
c2.setPrice(11999);
return c2;
default:
return null;
}
}
}
Mac和Huawei公司都来委托Factory工厂帮他们生产电脑public class FactoryDemo {
public static void main(String[] args) {
Computer c1 = FactoryPattern.createComputer("huawei");
c1.start();
Computer c2 = FactoryPattern.createComputer("mac");
c2.start();
}
}
可以看到多个类之间不在耦合,公司如果对工厂生产的产品不满意只要换更好的工厂即可【只需要修改FactoryPattern类即可】
创建一个新类,包装原始类,从而在新类中提升原来类的功能。
简单来说就是:在加强类中定义抽象父类的实现类,然后调用实现类,并增强功能
作用: 装饰模式指的是在不改变原类的基础上, 动态地扩展一个类的功能。
以IO流为例,模拟其装饰过程
/**
共同父类
*/
public abstract class InputStream {
public abstract int read();
public abstract int read(byte[] buffer);
}
/**
原始类
*/
public class FileInputStream extends InputStream{
@Override
public int read() {
System.out.println("低性能的方式读取了一个字节a");
return 97;
}
@Override
public int read(byte[] buffer) {
buffer[0] = 97;
buffer[1] = 98;
buffer[2] = 99;
System.out.println("低性能的方式读取了一个字节数组:" + Arrays.toString(buffer));
return 3;
}
}
/**
装饰类:继承InputStream 拓展原始类的功能
*/
public class BufferedInputStream extends InputStream{
private InputStream is;
public BufferedInputStream(InputStream is){
this.is = is;
}
@Override
public int read() {
System.out.println("提供8KB的缓冲区,提高读数据性能~~~~");
return is.read();
}
@Override
public int read(byte[] buffer) {
System.out.println("提供8KB的缓冲区,提高读数据性能~~~~");
return is.read(buffer);
}
}
@Builder注解通常与Java语言中的Lombok库一起使用。它的作用是简化Java类的构造方法和生成Builder模式的代码。使用@Builder注解,你可以轻松地创建一个具有多个属性的对象,而无需手动编写冗长的构造方法或Builder模式的代码。
以下是@Builder注解的一些主要功能和用法:
自动生成Builder类:当你在类上使用@Builder注解时,Lombok会自动生成一个相应的Builder类,该Builder类包含了类中所有字段的设置方法,使你可以链式调用设置属性的方法。
省去构造方法:@Builder注解允许你创建一个对象,只需提供所需属性的值,而不需要编写构造方法。这可以大大简化代码。
默认值:你可以在属性上使用@Builder.Default注解来设置默认值,这样在使用Builder创建对象时,如果没有显式设置该属性的值,就会使用默认值。
忽略属性:如果你不希望某些属性被Builder设置,可以使用@Builder.Exclude注解来排除这些属性。
示例代码:
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class Person {
private String name;
private int age;
@Builder.Default
private String country = "China";
}
// 使用Builder创建对象
Person person = Person.builder()
.name("John")
.age(30)
.build();
// 使用默认值
Person defaultPerson = Person.builder()
.name("Alice")
.build();
总之,@Builder注解是用来简化Java对象的构建过程的工具,特别适用于具有多个属性的类。通过减少样板代码,它可以提高代码的可读性和维护性。
定义一组算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式使这些算法在客户端调用它们的时候能够互不影响地变化,客户端代指使用算法的代码。用来消除 if-else、switch 等多重判断的代码,消除 if-else、switch 多重判断 可以有效应对代码的复杂性。

这段代码是能够满足项目中业务需求的,而且很多已上线生产环境的代码也有这类代码。但是,这一段代码存在存在两个弊端:
将上述代码块改造为策略设计模式,大致需要三个步骤。

目前把抽象策略接口、具体的策略实现类以及策略工厂都已经创建了,现在可以看一下客户端需要如何调用,又是如何对客户端屏蔽具体实现细节的。

根据代码块图片得知,具体策略类是从策略工厂中获取,确实是取消了 if-else 设计,在工厂中使用 Map 存储策略实现。获取到策略类后执行具体的优惠策略方法就可以获取优惠后的金额。
通过分析大家得知,目前这种设计确实将应用代码的复杂性降低了。如果新增一个优惠策略,只需要新增一个策略算法实现类即可。但是,添加一个策略算法实现,意味着需要改动策略工厂中的代码,还是不符合开闭原则。
如何完整实现符合开闭原则的策略模式,需要借助 Spring 的帮助,详细案例请继续往下看。
最近项目中设计的一个功能用到了策略模式,分为两类角色,笔者负责定义抽象策略接口以及策略工厂,不同的策略算法需要各个业务方去实现,可以联想到上文中的优惠券功能。因为是 Spring 项目,所以都是按照 Spring 的方式进行处理,话不多说,上代码。

可以看到,比对上面的示例代码,有两处明显的变化:
小贴士:为了阅读方便,mark() 返回直接使用字符串替代,读者朋友在返回标示时最好使用枚举定义。
接下来继续查看抽象策略工厂如何改造,才能满足开闭原则。

和之前责任链模式相同 (TODO 添加链接),都是通过 InitializingBean 接口实现中调用 IOC 容器查找对应策略实现,随后将策略实现 mark() 方法返回值作为 key, 策略实现本身作为 value 添加到 Map 容器中等待客户端的调用。

这里使用的 SpringBoot 测试类,注入策略工厂 Bean,通过策略工厂选择出具体的策略算法类,继而通过算法获取到优惠后的价格。小插曲:如果不想把策略工厂注入 Spring 也可以,实现方法有很多。
总结下本小节,我们通过和 Spring 结合的方式,通过策略设计模式对文初的代码块进行了两块优化:应对代码的复杂性,让其满足开闭原则。更具体一些呢就是 通过抽象策略算法类减少代码的复杂性,继而通过 Spring 的一些特性同时满足了开闭原则,现在来了新需求只要添加新的策略类即可,健壮易扩展。
这段代码定义了一个名为 AbstractChainHandler 的接口,它扩展了 Ordered 接口。这个接口可能是用于实现责任链模式的一个基础框架,它提供了一个处理方法 handler 和一个用于标识组件的方法 mark。
Ordered 接口:
Ordered 接口,用于标识有序的元素。实现此接口的类可以提供一个比较方法,用于确定元素之间的顺序。在这个 AbstractChainHandler 接口中,Ordered 接口可能用于定义责任链中处理节点的顺序,以便在处理请求时可以按照特定的顺序执行。handler 方法:
requestParam 是对请求数据的封装,可以是任何类型的对象。requestParam 进行处理,并可以选择性地将处理结果或者进一步处理的需求传递给链中的下一个节点。mark 方法:
AbstractChainHandler 接口。在处理请求时,请求会从链的第一个节点开始,逐个经过每个节点,直到所有节点都处理完毕。public interface AbstractChainHandler<T> extends Ordered {
/**
* 执行责任链逻辑
*
* @param requestParam 责任链执行入参
*/
void handler(T requestParam);
/**
* @return 责任链组件标识
*/
String mark();
}
具体的过滤器实现:
/**
* 购票流程过滤器之验证参数必填
*
* @公众号:马丁玩编程,回复:加群,添加马哥微信(备注:12306)获取项目资料
*/
@Component
public class TrainPurchaseTicketParamNotNullChainHandler implements TrainPurchaseTicketChainFilter<PurchaseTicketReqDTO> {
@Override
public void handler(PurchaseTicketReqDTO requestParam) {
// ......
}
@Override
public int getOrder() {
return 0;
}
}
/**
* 购票流程过滤器之验证参数是否有效
* 验证参数有效这个流程会大量交互缓存,为了优化性能需要使用 Lua。为了方便大家理解流程,这里使用多次调用缓存
*
* @公众号:马丁玩编程,回复:加群,添加马哥微信(备注:12306)获取项目资料
*/
@Component
@RequiredArgsConstructor
public class TrainPurchaseTicketParamVerifyChainHandler implements TrainPurchaseTicketChainFilter<PurchaseTicketReqDTO> {
private final TrainMapper trainMapper;
private final TrainStationMapper trainStationMapper;
private final DistributedCache distributedCache;
@Override
public void handler(PurchaseTicketReqDTO requestParam) {
// ......
}
@Override
public int getOrder() {
return 10;
}
}
/**
* 购票流程过滤器之验证列车站点库存是否充足
*
* @公众号:马丁玩编程,回复:加群,添加马哥微信(备注:12306)获取项目资料
*/
@Component
@RequiredArgsConstructor
public class TrainPurchaseTicketParamStockChainHandler implements TrainPurchaseTicketChainFilter<PurchaseTicketReqDTO> {
private final SeatMarginCacheLoader seatMarginCacheLoader;
private final DistributedCache distributedCache;
@Override
public void handler(PurchaseTicketReqDTO requestParam) {
// ......
}
@Override
public int getOrder() {
return 20;
}
}
/**
* 购票流程过滤器之验证乘客是否重复购买
*
* @公众号:马丁玩编程,回复:加群,添加马哥微信(备注:12306)获取项目资料
*/
@Component
@RequiredArgsConstructor
public class TrainPurchaseTicketRepeatChainHandler implements TrainPurchaseTicketChainFilter<PurchaseTicketReqDTO> {
@Override
public void handler(PurchaseTicketReqDTO requestParam) {
// ......
}
@Override
public int getOrder() {
return 30;
}
}
购票流程使用过滤器:
private final AbstractChainContext<PurchaseTicketReqDTO> purchaseTicketAbstractChainContext;
@Override
@Transactional(rollbackFor = Throwable.class)
public TicketPurchaseRespDTO purchaseTickets(PurchaseTicketReqDTO requestParam) {
// 责任链模式,验证 0:参数必填 1:参数正确性 2:列车车次余量是否充足 3:乘客是否已买当前车次等
purchaseTicketAbstractChainContext.handler(TicketChainMarkEnum.TRAIN_PURCHASE_TICKET_FILTER.name(), requestParam);
// ......
}
购票责任链实现原理:
/**
* 抽象责任链上下文
*
* @公众号:马丁玩编程,回复:加群,添加马哥微信(备注:12306)获取项目资料
*/
public final class AbstractChainContext<T> implements CommandLineRunner {
private final Map<String, List<AbstractChainHandler>> abstractChainHandlerContainer = new HashMap<>();
/**
* 责任链组件执行
*
* @param mark 责任链组件标识
* @param requestParam 请求参数
*/
public void handler(String mark, T requestParam) {
List<AbstractChainHandler> abstractChainHandlers = abstractChainHandlerContainer.get(mark);
if (CollectionUtils.isEmpty(abstractChainHandlers)) {
throw new RuntimeException(String.format("[%s] Chain of Responsibility ID is undefined.", mark));
}
abstractChainHandlers.forEach(each -> each.handler(requestParam));
}
@Override
public void run(String... args) throws Exception {
Map<String, AbstractChainHandler> chainFilterMap = ApplicationContextHolder
.getBeansOfType(AbstractChainHandler.class);
chainFilterMap.forEach((beanName, bean) -> {
List<AbstractChainHandler> abstractChainHandlers = abstractChainHandlerContainer.get(bean.mark());
if (CollectionUtils.isEmpty(abstractChainHandlers)) {
abstractChainHandlers = new ArrayList();
}
abstractChainHandlers.add(bean);
List<AbstractChainHandler> actualAbstractChainHandlers = abstractChainHandlers.stream()
.sorted(Comparator.comparing(Ordered::getOrder))
.collect(Collectors.toList());
abstractChainHandlerContainer.put(bean.mark(), actualAbstractChainHandlers);
});
}
}