1. 什么是设计模式?为什么要用设计模式?
设计模式是一些被反复验证的、可复用的代码设计方案,它解决的是在软件开发中常见的架构性问题。设计模式不是代码片段,而是针对特定场景的设计思想,可以帮助我们提高代码的可维护性、复用性、扩展性。
使用设计模式可以降低系统的耦合度、提高开发效率,同时也让团队之间有统一的沟通语言,比如提到“工厂模式”“单例模式”大家都知道怎么写、为什么用。
2. 常见的设计模式分类及代表模式?
1.创建型模式:只关注创建的结果,而不关注是如何创建的,创建型设计模式大多情况下都对复杂的创建过程进行封装,调用者并不需要知道具体过程,只需要知道得到的对象是合法可用的。
代表模式有:单例模式,原型模式,工厂模式,抽象工厂,建造者模式;
2.结构型模式:结构型模式关注的不再是单一模块或者单一对象,而是关注如何设计才可以让多个模块,对象组合起来以达到目的;
代表模式有:适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式;
3.行为型模式:行为型模式专注于如何做,对象之间是如何协助,如何分配职责的,一言蔽之就是他解决的是谁来做,怎么做;
代表模式有:策略模式,模板方法,观察者模式,责任链模式,命令模式,状态模式,中介者模式,迭代器模式,备忘录模式,解释器模式,访问者模式;
3. 单例模式(Singleton)
定义:单例模式是指在一个类在 JVM 中有且仅有一个实例,并能在全局访问;
适合场景:配置管理类,因为只需要读取一次配置;数据库连接池:集中管理,全局复用;缓存/日志管理器:系统级组件需要统一并全局访问;Spring 容器中的默认 Bean;一言蔽之就是资源控制,配置管理,基础设施;
实现方式:
饿汉式:构建 Java 程序时就创建实例,线程安全:
publis class Singleton {
private static Singleton singleton = new Singleton();
public Singleton(){};
public static Singleton getSingleton() {
return this.singleton;
}
}
懒汉式:只在第一次调用时才构建实例,线程不安全;
public class Singleton {
private static Singleton singleton = null;
public Singleton(){};
public static Singleton getSingleton() {
if (singleton == null) {
this.singleton = new Singleton();
}
return this.singleton;
}
双检锁:和懒汉式一样懒加载,但是线程安全;
public class Singleton {
private static volatile Singleton singleton;
private Singleton() {};
public static Singleton getSingleton() {
if (singleton == null) {
synchronized(Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
静态内部类:懒加载,线程安全,实现简单,利用静态内部类只有在首次访问时才构建的特性以实现懒加载,并利用 JVM 加载类时是线程安全的特性实现单例线程安全;
public class Singleton {
private Singleton() {};
private static class Instance() {
public static final Singleton SINGLETON = new Singleton();
}
public static Singleton getSingleton() {
return Instance.SINGLETON;
}
}
4. 工厂模式(Factory)
定义:工厂模式实际是将构建对象的逻辑从使用者手中抽离出来,交由工厂统一处理,从而实现解耦;
适合场景:
1.创建逻辑复杂的对象,对象构建需要大量配置,参数,初始化流程,使用工厂统一封装,避免重复代码;
2.需要根据不同条件构建不同的对象,根据用户的输入,配置文件,上下文环境决定创建那种类的实例;
3.系统中存在多个不同类型的系统,但是系统内的对象大同小异;
4.希望解耦对象的创建与使用,使用方不想关注具体实现类,利用 Spring 的自动注入注解时,实际就是工厂模式,不需要关系具体的实现类,而只需要关心接口;
5. 代码中频繁使用 new,违反开闭原则,需要更改实现类时,调用者需要同步改动,而使用工厂模式,只需要更改工厂的生产产品即可,满足开闭原则;
实现方法:
1.简单工厂:多个类交给一个工厂管理,简单工厂类似一个百货超市什么都可以找到,优点是实现简单,缺点是没建一个类都需要更改工厂;实际应用可以是 SLF4J 提供了一个日志工厂类,他里面提供了多种不同日志实现,通过不同的配置选择不同的日志;
interface Product {
void use();
}
class AProduct implements Product {
public void use() { System.out.println("使用产品 A"); }
}
class BProduct implements Product {
public void use() { System.out.println("使用产品 B"); }
}
class ProductFactory {
public static Product create(String type) {
if ("A".equals(type)) return new AProduct();
else if ("B".equals(type)) return new BProduct();
throw new ("未知类型");
}
}
2.工厂方法:不同类交由不同的工厂单独生产,优点是新增工厂不需修改原工厂类,缺点是每个产品都需要新增一个工厂类;实际应用是 Spring 创建 Bean 使用的是“工厂方法模式”的思想:将 Bean 的创建交由容器中的“工厂方法”统一管理和调用,而不是直接通过 new 实例化。这样做解耦了对象的创建和使用,提高了灵活性与扩展性。
interface ProductFactory {
Product create();
}
class AFactory implements ProductFactory {
public Product create() { return new AProduct(); }
}
class BFactory implements ProductFactory {
public Product create() { return new BProduct(); }
}
3.抽象工厂模式:提供一个产品族的创建接口,多个相关产品由同一个工厂创建,就是每个工厂生产的产品种类是一样的,但是具体的产品却又不一样,比如 小米,华为,OV 都生产手机,但是都各有不同;具体的应用可以是数据库的连接,数据库创建连接都大同小异,但是实际却又因为不同数据库类型而有所不同,所以适合通过抽象工厂根据不同数据库类型调用不同的数据库连接创建工厂来构建连接对象;
interface Button {
void paint();
}
interface TextBox {
void draw();
}
class WinButton implements Button {
public void paint() { System.out.println("Win Button"); }
}
class MacButton implements Button {
public void paint() { System.out.println("Mac Button"); }
}
class WinTextBox implements TextBox {
public void draw() { System.out.println("Win TextBox"); }
}
class MacTextBox implements TextBox {
public void draw() { System.out.println("Mac TextBox"); }
}
interface GUIFactory {
Button createButton();
TextBox createTextBox();
}
class WinFactory implements GUIFactory {
public Button createButton() { return new WinButton(); }
public TextBox createTextBox() { return new WinTextBox(); }
}
class MacFactory implements GUIFactory {
public Button createButton() { return new MacButton(); }
public TextBox createTextBox() { return new MacTextBox(); }
}
5. 建造者模式
定义:建造者模式是一种创建型设计模式,通过将一个复杂对象的构造过程拆分,通过不同的链式构造而创建不同的对象;
适合场景:对象属性较多,构造过程复杂,某些属性是可选的;
public class User {
private String name;
private String address;
private String phone;
private String email;
private User(Builder builder) {
this.name = builder.name;
this.address = builder.address;
this.phone = builder.phone;
this.email = builder.email;
}
public static class Builder {
private String name;
private String address;
private String phone;
private String email;
public Builder name(String name) {
this.name = name;
return this;
}
public Builder address(String address) {
this.address = address;
return this;
}
public Builder phone(String phone) {
this.phone = phone;
return this;
}
public Builder email(String email) {
this.email = email;
return this;
}
public User build() {
return new User(this);
}
}
}
6. 代理模式(Proxy)
定义:代理模式实际是通过代理对象间接访问目标对象,在代理对象中可以对目标对象进行业务逻辑增强以实现像安全控制,日志记录,懒加载等功能。
适合场景:需要进行业务增强,懒加载,安全控制,隐藏实际业务逻辑;
public interface Subject {
public void display();
}
public class RealSubject implements subject {
private String path;
public void display() {
load(this.path);
}
public RealSubject(String path) {
this.path = path;
}
}
public class SubjectProxy implements subject {
private RealSubject realSubject;
private String path;
public SubjectProxy(String path) {
this.path = path;
}
public void display() {
// before 增强
if (realSubject == null) {
realSubject = new RealSubject(this.path);
}
this.realSubject.display();
// after 增强
}
}
7. 装饰器模式(Decorator)
定义:尽管继承也能增强类的功能,但它的扩展方式是静态的、单一继承,每增加一种增强功能都要写一个新的子类。当需要组合多个功能(如缓存 + 日志 + 权限)时,只能不断组合继承,导致子类爆炸和代码重复,不符合开闭原则。而装饰器模式则通过“对象组合”实现增强功能,每个装饰器类都实现相同的接口,并持有原对象引用,功能增强是动态的、可组合的,支持运行时灵活叠加多个功能,更加优雅且易扩展。
适用场景:在运行时为对象添加功能,而不是通过继承。不想创建大量子类(继承方式太死板)。想通过组合而非继承增强对象行为。UI 控件、日志、权限、数据校验、缓存增强等。
public interface Coffee {
String getDescription();
double cost();
}
public class SimpleCoffee implements Coffee {
public String getDescription() {
return "原味咖啡";
}
public double cost() {
return 10.0;
}
}
public abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
public String getDescription() {
return coffee.getDescription();
}
public double cost() {
return coffee.cost();
}
}
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
public String getDescription() {
return super.getDescription() + " + 糖";
}
public double cost() {
return super.cost() + 1.0;
}
}
public class MilkFoamDecorator extends CoffeeDecorator {
public MilkFoamDecorator(Coffee coffee) {
super(coffee);
}
public String getDescription() {
return super.getDescription() + " + 奶泡";
}
public double cost() {
return super.cost() + 2.0;
}
}
public class Client {
public static void main(String[] args) {
Coffee coffee = new SimpleCoffee(); // 原味
coffee = new SugarDecorator(coffee); // 加糖
coffee = new MilkFoamDecorator(coffee); // 加奶泡
System.out.println("描述: " + coffee.getDescription());
System.out.println("总价: " + coffee.cost() + " 元");
}
}
8. 适配器模式
定义:
适配器模式通过定义统一的适配器接口,为不兼容的类提供一致的调用方式。每个适配器实现该接口,只要能处理某个具体类,就可以将其“适配”为标准格式,实现统一的入参与出参,让调用方无需关心底层对象的具体实现。
适用场景:
统一接口
举例:
Spring MVC 中通过定义统一的 HandlerAdapter 接口,规范了适配器的入参和出参 —— 入参是HttpServletRequest、HttpServletResponse 和一个 handler 对象,出参是 ModelAndView。只要某个适配器实现类能 supports(handler),就表示它能处理这个 handler。此后,DispatcherServlet 就可以把请求对象、响应对象和 handler 一起传给适配器执行,而不用关心 handler 本身的类型和调用细节。适配器内部具体如何调用 handler 是它自己的实现细节,这种设计使得 DispatcherServlet 不依赖具体 Handler 类型,调用逻辑统一、灵活扩展,符合开闭原则。
所以整个流程就是:
-
-
- DispatcherServlet 接收请求;
- 遍历所有 HandlerMapping 找到 handler;
- 遍历所有 HandlerAdapter 找到能处理该 handler 的适配器;
- 调用 adapter.handle(request, response, handler) 执行处理;
- 返回 ModelAndView 或直接处理响应。
-
9. 外观模式
定义:
外观模式是将多个复杂子系统封装到一个统一的接口中,对外提供简化调用,隐藏实现细节,是复杂系统中模块解耦和接口聚合的典型设计模式。
适用场景:
子系统复杂、不希望外部耦合太深,如多个子系统协作(子模块、子服务),提供统一封装对外调用,如统一接口、SDK 门面、接口聚合,简化接口,便于客户端调用,降低外部理解成本,防止子系统暴露给外部。
public class VideoPlayerFacade {
private VideoDecoder decoder = new VideoDecoder();
private AudioSystem audio = new AudioSystem();
private Screen screen = new Screen();
public void play(String file) {
decoder.decode(file);
screen.show();
audio.playAudio();
}
}
10. 桥接模式
定义:
将抽象部分与实现部分分离,使它们可以独立变化,并通过组合关系将两者连接起来。
适用场景:
当一个类存在多个独立变化的维度时。
当需要在运行时切换不同的实现时。
当需要避免多层继承时。
举例说明:
// 实现部分接口 - PDF生成器(含语言维度)
interface PdfGenerator {
byte[] generateTable1(); // 表1生成逻辑
byte[] generateTable2(); // 表2生成逻辑
byte[] generateTable3(); // 表3生成逻辑
}
// 具体实现 - 中文版PDF生成
class ChinesePdfGenerator implements PdfGenerator {
@Override
public byte[] generateTable1() {
return "中文表1内容".getBytes();
}
// 其他表格实现...
}
// 具体实现 - 英文版PDF生成
class EnglishPdfGenerator implements PdfGenerator {
@Override
public byte[] generateTable1() {
return "English Table1 Content".getBytes();
}
// 其他表格实现...
}
// 抽象部分 - PDF操作
abstract class PdfOperation {
protected PdfGenerator generator;
public PdfOperation(PdfGenerator generator) {
this.generator = generator;
}
abstract void execute(int tableType);
}
// 精确抽象 - 预览操作
class PreviewOperation extends PdfOperation {
public PreviewOperation(PdfGenerator generator) {
super(generator);
}
@Override
void execute(int tableType) {
byte[] pdfData = switch (tableType) {
case 1 -> generator.generateTable1();
case 2 -> generator.generateTable2();
case 3 -> generator.generateTable3();
default -> throw new IllegalArgumentException();
};
System.out.println("预览PDF: " + new String(pdfData));
}
}
// 精确抽象 - 下载操作
class DownloadOperation extends PdfOperation {
public DownloadOperation(PdfGenerator generator) {
super(generator);
}
@Override
void execute(int tableType) {
byte[] pdfData = switch (tableType) {
case 1 -> generator.generateTable1();
case 2 -> generator.generateTable2();
case 3 -> generator.generateTable3();
default -> throw new IllegalArgumentException();
};
System.out.println("下载PDF文件: " + new String(pdfData));
}
}
public class PdfClient {
public static void main(String[] args) {
// 中文版操作
PdfGenerator chineseGen = new ChinesePdfGenerator();
PdfOperation previewChinese = new PreviewOperation(chineseGen);
previewChinese.execute(1); // 预览中文表1
// 英文版操作
PdfGenerator englishGen = new EnglishPdfGenerator();
PdfOperation downloadEnglish = new DownloadOperation(englishGen);
downloadEnglish.execute(2); // 下载英文表2
}
}
11. 组合模式
定义:
将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
使用场景:
部分-整体层次结构:当需要表示对象的"部分-整体"层次结构时,特别是希望客户端能够以统一的方式处理单个对象和组合对象。
统一处理需求:希望客户端忽略组合对象与单个对象之间的差异,统一地使用组合结构中的所有对象。
树形结构表示:需要表示或处理树形结构的数据时,如文件系统、组织架构、GUI组件等。
例子:
abstract class FileComponent {
protected String name;
public FileComponent(String name) { this.name = name; }
public abstract void display();
}
class FileLeaf extends FileComponent {
public FileLeaf(String name) { super(name); }
public void display() {
System.out.println("File: " + name);
}
}
class FolderComposite extends FileComponent {
private List<FileComponent> children = new ArrayList<>();
public FolderComposite(String name) { super(name); }
public void add(FileComponent fc) { children.add(fc); }
public void display() {
System.out.println("Folder: " + name);
for (FileComponent child : children) {
child.display();
}
}
}
FolderComposite root = new FolderComposite("root");
root.add(new FileLeaf("file1.txt"));
FolderComposite subFolder = new FolderComposite("sub");
subFolder.add(new FileLeaf("file2.txt"));
root.add(subFolder);
root.display();
12. 享元模式
定义:
享元模式的关键不在于减少对象数量,而在于分离共享状态和非共享状态,让多个对象可以复用相同的内部状态,从而减少内存消耗。
适用场景:
当系统中存在大量相似对象,且可以分离出共享状态时。
例子:
// 子弹享元
class BulletFlyweight {
private String model; // 3D模型(共享)
private String texture; // 贴图(共享)
public BulletFlyweight(String model, String texture) {
this.model = model;
this.texture = texture;
}
public void render(int x, int y) { // 外部状态:位置
System.out.println("渲染子弹: " + model + " 在 (" + x + ", " + y + ")");
}
}
// 享元工厂
class BulletFactory {
private static Map<String, BulletFlyweight> bullets = new HashMap<>();
public static BulletFlyweight getBullet(String type) {
if (!bullets.containsKey(type)) {
bullets.put(type, new BulletFlyweight(type + "_model", type + "_texture"));
}
return bullets.get(type);
}
}
// 游戏中的子弹
class Bullet {
private BulletFlyweight flyweight;
private int x, y; // 外部状态
public Bullet(String type, int x, int y) {
this.flyweight = BulletFactory.getBullet(type);
this.x = x;
this.y = y;
}
public void render() {
flyweight.render(x, y);
}
}
// 客户端
public class Game {
public static void main(String[] args) {
List<Bullet> bullets = new ArrayList<>();
bullets.add(new Bullet("red", 100, 200));
bullets.add(new Bullet("red", 150, 300)); // 共享同一个 red 子弹模型
bullets.add(new Bullet("blue", 200, 400));
bullets.forEach(Bullet::render);
}
}
13. 观察者模式(Observer)
定义:
观察者模式定义了对象之间一对多的依赖关系,当“被观察对象”状态发生变化时,会自动通知所有依赖它的“观察者对象”,实现自动联动、解耦通知。
使用场景:
事件通知机制,GUI按钮点击监听、窗口变化,消息发布订阅,订阅者收到消息推送(如MQ),数据变化感知,表单字段绑定、缓存刷新,分布式系统通知, 微服务中服务状态广播。
interface Subject {
void addObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
}
class WeatherStation implements Subject {
private List<Observer> observers = new ArrayList<>();
private int temperature;
public void setTemperature(int t) {
this.temperature = t;
notifyObservers();
}
@Override
public void addObserver(Observer o) { observers.add(o); }
@Override
public void removeObserver(Observer o) { observers.remove(o); }
@Override
public void notifyObservers() {
for (Observer o : observers) {
o.update(temperature);
}
}
}
interface Observer {
void update(int temp);
}
class PhoneDisplay implements Observer {
public void update(int temp) {
System.out.println("📱 手机温度显示: " + temp + "°C");
}
}
class DashboardDisplay implements Observer {
public void update(int temp) {
System.out.println("📊 仪表盘显示温度: " + temp + "°C");
}
}
public class Main {
public static void main(String[] args) {
WeatherStation station = new WeatherStation();
station.addObserver(new PhoneDisplay());
station.addObserver(new DashboardDisplay());
station.setTemperature(26); // 所有观察者自动收到通知
}
}
14. 发布订阅模式
定义:
发布-订阅模式本质上是在观察者模式的基础上引入了“消息中介”,就像在“被观察者”和“观察者”之间加了一个“中间人”作为消息分发中心,被观察者只需把事件投递给中间人,而观察者则提前注册到中间人上,中间人按照 topic 或 tag 等策略,将事件精准分发给感兴趣的观察者,从而实现了完全解耦的事件驱动通信模型,是消息中间件(如 RabbitMQ/Kafka)的核心设计思想。
使用场景:
一对多,多对一,多对多
public class Event {
private String topic;
private Object data;
public Event(String topic, Object data) {
this.topic = topic;
this.data = data;
}
// getters ...
}
public interface Subscriber {
void handleEvent(Event event);
}
public class EventBus {
private Map<String, List<Subscriber>> subscribers = new HashMap<>();
public void subscribe(String topic, Subscriber subscriber) {
subscribers.computeIfAbsent(topic, k -> new ArrayList<>()).add(subscriber);
}
public void publish(Event event) {
List<Subscriber> list = subscribers.get(event.getTopic());
if (list != null) {
for (Subscriber sub : list) {
sub.handleEvent(event); // 可以异步执行
}
}
}
}
public class UserCreatedListener implements Subscriber {
public void handleEvent(Event event) {
System.out.println("处理用户创建事件:" + event.getData());
}
}
public class Main {
public static void main(String[] args) {
EventBus bus = new EventBus();
// 订阅
bus.subscribe("user.created", new UserCreatedListener());
// 发布
bus.publish(new Event("user.created", "张三注册成功"));
}
}
15. 策略模式(Strategy)
定义:
策略模式就是“用接口规范算法族,按需注入策略实现。
适用场景:
订单促销 满减、打折、积分兑换策略
支付渠道选择 微信支付、支付宝、银联等切换
登录方式选择 账号密码、短信验证码、微信扫码等
算法族切换 排序算法、加密算法、推荐算法
业务规则灵活切换 营销活动、计费方式、优惠券发放等
public interface PromotionStrategy {
void execute();
}
@Component("fullReduction")
public class FullReductionStrategy implements PromotionStrategy {
public void execute() {
System.out.println("满减策略");
}
}
@Component("discount")
public class DiscountStrategy implements PromotionStrategy {
public void execute() {
System.out.println("打折策略");
}
}
@Component
public class PromotionStrategyFactory {
@Autowired
private Map<String, PromotionStrategy> strategyMap;
public PromotionStrategy getStrategy(String type) {
return strategyMap.get(type);
}
}
@Service
public class OrderService {
@Autowired
private PromotionStrategyFactory strategyFactory;
public void placeOrder(String promoType) {
PromotionStrategy strategy = strategyFactory.getStrategy(promoType);
if (strategy != null) {
strategy.execute();
} else {
System.out.println("无效的促销类型");
}
}
}
orderService.placeOrder("discount");
// 控制台输出:打折策略
orderService.placeOrder("fullReduction");
// 控制台输出:满减策略
16. 责任链模式
定义:
责任链模式是一种行为设计模式,允许你将请求沿着处理链传递,直到有一个处理者能够处理它为止。该模式通过让多个对象都有机会处理请求,从而避免请求发送者与接收者之间的耦合关系。
适用场景:
多级审批流程(如船员特免证明申请)
请求需要被多个对象处理(如异常处理、过滤器链)
处理者动态可变的场景(如中间件管道)
不确定具体处理者的场景(如事件冒泡机制)
举例说明:
public abstract class ExemptionApprovalHandler {
protected ExemptionApprovalHandler nextHandler;
public void setNextHandler(ExemptionApprovalHandler handler) {
this.nextHandler = handler;
}
public abstract ApprovalResult handleRequest(ExemptionApplication application);
}
public class ApprovalResult {
private boolean approved;
private String rejectReason;
// getters/setters...
}
// 1. 签发机构验证处理器
public class IssuingAuthorityValidator extends ExemptionApprovalHandler {
@Override
public ApprovalResult handleRequest(ExemptionApplication app) {
if(!MaritimeSystem.verifyIssuingAuthority(app.getIdCard(), app.getApplyPosition())) {
return new ApprovalResult(false, "必须向原证书签发机构申请");
}
return nextHandler != null ? nextHandler.handleRequest(app) : defaultSuccess();
}
}
// 2. 证书层级验证处理器
public class CertificateHierarchyValidator extends ExemptionApprovalHandler {
private static final Map<String, String> POSITION_REQUIREMENTS = Map.of(
"CAPTAIN", "CHIEF_MATE",
"CHIEF_ENGINEER", "FIRST_ENGINEER",
// ...其他职务映射
);
@Override
public ApprovalResult handleRequest(ExemptionApplication app) {
String requiredCert = POSITION_REQUIREMENTS.get(app.getApplyPosition());
if(!MaritimeSystem.hasCertificate(app.getIdCard(), requiredCert)) {
return new ApprovalResult(false, "缺少下一级适任证书:" + requiredCert);
}
return nextHandler != null ? nextHandler.handleRequest(app) : defaultSuccess();
}
}
// 3. 资历验证处理器
public class ExperienceValidator extends ExemptionApprovalHandler {
private static final Map<String, Integer> EXPERIENCE_REQUIREMENTS = Map.of(
"CAPTAIN", 12,
"CHIEF_MATE", 12,
"SECOND_OFFICER", 6,
// ...其他职务要求
);
@Override
public ApprovalResult handleRequest(ExemptionApplication app) {
int requiredMonths = EXPERIENCE_REQUIREMENTS.getOrDefault(app.getApplyPosition(), 0);
int actualMonths = MaritimeSystem.getExperienceMonths(app.getIdCard(), app.getApplyPosition());
if(actualMonths < requiredMonths) {
return new ApprovalResult(false,
String.format("资历不足(需%d个月,实际%d个月)", requiredMonths, actualMonths));
}
return nextHandler != null ? nextHandler.handleRequest(app) : defaultSuccess();
}
}
// 4. 特殊职位限制处理器
public class PositionRestrictionValidator extends ExemptionApprovalHandler {
private static final Set<String> ALLOWED_POSITIONS = Set.of(
"CAPTAIN", "CHIEF_ENGINEER", "CHIEF_MATE",
"FIRST_ENGINEER", "SECOND_OFFICER", "SECOND_ENGINEER",
"THIRD_OFFICER", "THIRD_ENGINEER"
);
@Override
public ApprovalResult handleRequest(ExemptionApplication app) {
if(!ALLOWED_POSITIONS.contains(app.getApplyPosition())) {
return new ApprovalResult(false, "该职务不允许办理特免证明");
}
return nextHandler != null ? nextHandler.handleRequest(app) : defaultSuccess();
}
}
public class ApprovalService {
private ExemptionApprovalHandler approvalChain;
public ApprovalService() {
buildApprovalChain();
}
private void buildApprovalChain() {
ExemptionApprovalHandler h1 = new PositionRestrictionValidator();
ExemptionApprovalHandler h2 = new IssuingAuthorityValidator();
ExemptionApprovalHandler h3 = new CertificateHierarchyValidator();
ExemptionApprovalHandler h4 = new ExperienceValidator();
h1.setNextHandler(h2);
h2.setNextHandler(h3);
h3.setNextHandler(h4);
this.approvalChain = h1;
}
public ApprovalResult submitApplication(ExemptionApplication app) {
return approvalChain.handleRequest(app);
}
}
// 使用示例
ApprovalService service = new ApprovalService();
ApprovalResult result = service.submitApplication(application);
if(!result.isApproved()) {
throw new BusinessException("申请被拒绝:" + result.getRejectReason());
}
17. 模板方法模式
定义:
模板方法模式是一种 行为型设计模式,它在父类中定义一个算法的骨架(模板方法),而将某些步骤的具体实现延迟到子类中。这样可以在不改变算法结构的情况下,允许子类重写某些特定步骤的逻辑。一言蔽之,流程固定,细节固定;
适用场景:
多个类有相似的流程,但某些步骤不同(如不同的数据存储方式、不同的计算逻辑)。
希望避免代码重复,抽取公共逻辑到父类。
需要控制子类的扩展方式,确保某些关键步骤必须执行(如初始化、资源清理)。
例子:
public abstract class Beverage {
// 模板方法(final 防止子类覆盖流程)
public final void prepareBeverage() {
boilWater();
brew();
pourInCup();
addCondiments();
}
// 公共步骤(直接实现)
private void boilWater() {
System.out.println("煮沸水");
}
private void pourInCup() {
System.out.println("倒入杯子");
}
// 抽象方法(由子类实现)
protected abstract void brew();
protected abstract void addCondiments();
}
// 咖啡
public class Coffee extends Beverage {
@Override
protected void brew() {
System.out.println("冲泡咖啡粉");
}
@Override
protected void addCondiments() {
System.out.println("加糖和牛奶");
}
}
// 茶
public class Tea extends Beverage {
@Override
protected void brew() {
System.out.println("浸泡茶叶");
}
@Override
protected void addCondiments() {
System.out.println("加柠檬");
}
}
public class Main {
public static void main(String[] args) {
Beverage coffee = new Coffee();
coffee.prepareBeverage();
// 输出:
// 煮沸水
// 冲泡咖啡粉
// 倒入杯子
// 加糖和牛奶
Beverage tea = new Tea();
tea.prepareBeverage();
// 输出:
// 煮沸水
// 浸泡茶叶
// 倒入杯子
// 加柠檬
}
}
18. 命令模式
定义:
命令模式是一种行为型设计模式,它将请求(操作)封装成一个独立的对象(Command)。一言蔽之,命令模式把操作变成“对象”,让控制更灵活!
适用场景:
需要将操作请求与执行解耦(如 GUI 按钮点击、任务队列)。
需要支持撤销(Undo)、重做(Redo)或事务(如文本编辑器、绘图软件)。
需要宏命令(批量执行多个命令)(如一键自动化任务)。
需要日志记录或延迟执行请求(如游戏回放、异步任务)。
public interface Command {
void execute(); // 执行操作
void undo(); // 撤销操作
}
public class InsertTextCommand implements Command {
private StringBuilder document; // 接收者(文档)
private String text; // 插入的内容
private int position; // 插入的位置
public InsertTextCommand(StringBuilder doc, String text, int pos) {
this.document = doc;
this.text = text;
this.position = pos;
}
@Override
public void execute() {
document.insert(position, text); // 执行插入
}
@Override
public void undo() {
document.delete(position, position + text.length()); // 撤销 = 删除
}
}
public class TextEditorController {
private Deque<Command> historyStack = new ArrayDeque<>(); // 历史记录栈
private Deque<Command> redoStack = new ArrayDeque<>(); // 重做栈
public void executeCommand(Command cmd) {
cmd.execute(); // 执行命令
historyStack.push(cmd); // 记录到历史
}
public void undo() {
if (!historyStack.isEmpty()) {
Command cmd = historyStack.pop();
cmd.undo();
redoStack.push(cmd); // 压入重做栈
}
}
public void redo() {
if (!redoStack.isEmpty()) {
Command cmd = redoStack.pop();
cmd.execute();
historyStack.push(cmd);
}
}
}
public static void main(String[] args) {
StringBuilder document = new StringBuilder("Hello");
TextEditorController controller = new TextEditorController();
// 插入文字
Command insertCmd = new InsertTextCommand(document, " World", 5);
controller.executeCommand(insertCmd); // 文档变为 "Hello World"
// 撤销操作
controller.undo(); // 文档恢复为 "Hello"
}
19. 状态模式
定义:
状态模式是一种 行为型设计模式,允许一个对象在 内部状态改变时改变它的行为,使对象看起来像是修改了自身的类。一言蔽之,“将状态封装成独立的对象,并将行为委托给当前状态对象”。
适用场景:
对象的行为依赖于它的状态,且状态数量较多(如订单状态、游戏角色状态)。
代码中存在大量与状态相关的条件语句,导致难以维护。
状态转换逻辑复杂,且未来可能需要新增状态。
举例说明:
public class OrderStateFactory {
public static OrderState create(String status) {
switch (status) {
case "UNPAID": return new UnpaidState();
case "PAID": return new PaidState();
case "SHIPPED":return new ShippedState();
default: throw new IllegalArgumentException("未知状态: " + status);
}
}
}
public class Order {
private Long id;
private OrderState currentState; // 当前状态对象
// 从数据库加载订单时初始化状态
public static Order loadFromDB(Long orderId) {
// 伪代码:查询数据库
String status = jdbcTemplate.queryForObject(
"SELECT status FROM orders WHERE id = ?",
String.class, orderId
);
Order order = new Order();
order.setId(orderId);
order.setState(OrderStateFactory.create(status)); // 工厂创建状态对象
return order;
}
// 委托给状态对象
public void pay() {
currentState.pay(this);
}
public void ship() {
currentState.ship(this);
}
// 更新数据库状态并切换内存状态
public void updateStatus(String newStatus) {
jdbcTemplate.update(
"UPDATE orders SET status = ? WHERE id = ?",
newStatus, this.id
);
this.currentState = OrderStateFactory.create(newStatus);
}
}
public class PaidState implements OrderState {
@Override
public void ship(Order order) {
System.out.println("执行发货逻辑...");
order.updateStatus("SHIPPED"); // 更新数据库并切换状态
sendShippingNotification(); // 触发其他业务逻辑
}
@Override
public void pay(Order order) {
throw new IllegalStateException("已支付订单不能重复支付");
}
}
public class Client {
public static void main(String[] args) {
// 从数据库加载订单(假设当前状态为PAID)
Order order = Order.loadFromDB(1L);
// 发货操作(实际调用PaidState.ship())
order.ship(); // 输出:执行发货逻辑...
// 数据库状态更新为SHIPPED,内存状态对象变为ShippedState
}
}
20. 中介者模式
定义:
中介者模式是一种行为设计模式,它通过定义一个中介对象来封装一组对象之间的交互,从而降低对象之间的直接耦合。
主要角色:
Mediator(中介者接口):定义各个同事对象通信的接口
ConcreteMediator(具体中介者):实现中介者接口,协调各同事对象
Colleague(同事类):每个同事对象都知道它的中介者对象,与其他同事通信时通过中介者进行
适用场景:
多个对象需要复杂交互,直接依赖会导致混乱。
需要一个中心控制点来管理交互逻辑(如UI、游戏、IoT)。
希望避免对象间的网状依赖。
举例说明:
// 中介者接口
interface ChatMediator {
void sendMessage(String msg, User user);
void addUser(User user);
}
// 具体中介者
class ChatMediatorImpl implements ChatMediator {
private List<User> users;
public ChatMediatorImpl() {
this.users = new ArrayList<>();
}
@Override
public void addUser(User user) {
this.users.add(user);
}
@Override
public void sendMessage(String msg, User user) {
for(User u : this.users) {
// 消息不应该发给发送者自己
if(u != user) {
u.receive(msg);
}
}
}
}
// 同事类
abstract class User {
protected ChatMediator mediator;
protected String name;
public User(ChatMediator med, String name) {
this.mediator = med;
this.name = name;
}
public abstract void send(String msg);
public abstract void receive(String msg);
}
// 具体同事类
class UserImpl extends User {
public UserImpl(ChatMediator med, String name) {
super(med, name);
}
@Override
public void send(String msg) {
System.out.println(this.name + ": 发送消息 = " + msg);
mediator.sendMessage(msg, this);
}
@Override
public void receive(String msg) {
System.out.println(this.name + ": 收到消息 = " + msg);
}
}
// 使用示例
public class MediatorPatternDemo {
public static void main(String[] args) {
ChatMediator mediator = new ChatMediatorImpl();
User user1 = new UserImpl(mediator, "张三");
User user2 = new UserImpl(mediator, "李四");
User user3 = new UserImpl(mediator, "王五");
mediator.addUser(user1);
mediator.addUser(user2);
mediator.addUser(user3);
user1.send("大家好!");
}
}
21. 迭代器模式
定义:
迭代器模式是一种行为设计模式,它提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
适合场景:
迭代器模式的核心价值在于它提供了一种标准化的访问机制,使得集合的实现和遍历逻辑能够独立变化,这是简单for-each语法无法替代的架构优势。
举例说明:
public class BookShelf implements Iterable<Book> {
private Book[] books;
private int last = 0;
public BookShelf(int maxsize) {
this.books = new Book[maxsize];
}
public Book getBookAt(int index) {
return books[index];
}
public void appendBook(Book book) {
this.books[last] = book;
last++;
}
public int getLength() {
return last;
}
@Override
public Iterator<Book> iterator() {
return new BookShelfIterator(this);
}
}
public class BookShelfIterator implements Iterator<Book> {
private BookShelf bookShelf;
private int index;
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
this.index = 0;
}
@Override
public boolean hasNext() {
return index < bookShelf.getLength();
}
@Override
public Book next() {
Book book = bookShelf.getBookAt(index);
index++;
return book;
}
}
22. 备忘录模式
定义:
备忘录模式是一种行为设计模式,它允许在不破坏封装性的前提下捕获并外部化一个对象的内部状态,以便以后可以将该对象恢复到原先保存的状态。
适用场景:
撤销/重做功能,文本编辑器、绘图软件等需要撤销操作的应用;
游戏存档,保存和恢复游戏状态;
事务回滚,数据库操作失败时回滚到之前的状态;
配置管理,保存和恢复系统配置;
工作流恢复点,长时间运行的工作流中可以设置恢复点;
举例说明:
// 备忘录类
class Memento {
private final String state;
public Memento(String stateToSave) {
state = stateToSave;
}
public String getSavedState() {
return state;
}
}
// 原发器类
class Originator {
private String state;
public void setState(String state) {
this.state = state;
}
public Memento saveToMemento() {
return new Memento(state);
}
public void restoreFromMemento(Memento memento) {
state = memento.getSavedState();
}
public void printState() {
System.out.println("Current state: " + state);
}
}
// 管理者类
class Caretaker {
private Memento memento;
public void saveState(Originator originator) {
memento = originator.saveToMemento();
}
public void restoreState(Originator originator) {
if (memento != null) {
originator.restoreFromMemento(memento);
}
}
}
// 使用示例
public class MementoDemo {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState("State #1");
caretaker.saveState(originator);
originator.printState();
originator.setState("State #2");
originator.printState();
caretaker.restoreState(originator);
originator.printState(); // 恢复为State #1
}
}
23. 解释器模式
定义:
解释器模式是一种行为设计模式,它定义了一个语言的文法,并建立一个解释器来解释该语言中的句子。这种模式主要应用于需要解释执行特定语言或规则的场景。
适用场景:
规则引擎,解释和执行业务规则,SQL解析,解析SQL查询语句,正则表达式,解释正则表达式模式,数学公式计算,解释和执行数学公式,领域特定语言(DSL),为特定领域创建的小型语言,配置文件解析,解释复杂的配置文件格式。
举例说明:
// 抽象表达式
interface Expression {
int interpret(Map<String, Integer> context);
}
// 终结符表达式 - 变量
class Variable implements Expression {
private String name;
public Variable(String name) {
this.name = name;
}
public int interpret(Map<String, Integer> context) {
return context.getOrDefault(name, 0);
}
}
// 终结符表达式 - 数字
class Number implements Expression {
private int value;
public Number(int value) {
this.value = value;
}
public int interpret(Map<String, Integer> context) {
return value;
}
}
// 非终结符表达式 - 加法
class Add implements Expression {
private Expression left;
private Expression right;
public Add(Expression left, Expression right) {
this.left = left;
this.right = right;
}
public int interpret(Map<String, Integer> context) {
return left.interpret(context) + right.interpret(context);
}
}
// 非终结符表达式 - 减法
class Subtract implements Expression {
private Expression left;
private Expression right;
public Subtract(Expression left, Expression right) {
this.left = left;
this.right = right;
}
public int interpret(Map<String, Integer> context) {
return left.interpret(context) - right.interpret(context);
}
}
// 使用示例
public class InterpreterDemo {
public static void main(String[] args) {
// 构建表达式:a + b - c
Expression a = new Variable("a");
Expression b = new Variable("b");
Expression c = new Variable("c");
Expression expr = new Subtract(new Add(a, b), c);
// 设置上下文
Map<String, Integer> context = new HashMap<>();
context.put("a", 10);
context.put("b", 5);
context.put("c", 3);
// 解释执行
int result = expr.interpret(context);
System.out.println("结果: " + result); // 输出12
}
}
24. 访问者模式
定义:
访问者模式是一种行为设计模式,它允许你将算法与对象结构分离,使得可以在不修改现有对象结构的情况下定义新的操作。
适用场景:
需要对复杂结构进行多种不相关的操作
操作可能经常变化或扩展
希望保持元素类的稳定性
例子:
// 文档元素接口
interface DocumentElement {
void accept(DocumentVisitor visitor);
}
// 具体元素 - 文本段落
class TextElement implements DocumentElement {
private String content;
private TextStyle style;
public TextElement(String content, TextStyle style) {
this.content = content;
this.style = style;
}
@Override
public void accept(DocumentVisitor visitor) {
visitor.visit(this);
}
// getters...
}
// 具体元素 - 图片
class ImageElement implements DocumentElement {
private String imagePath;
private float width;
private float height;
public ImageElement(String imagePath, float width, float height) {
this.imagePath = imagePath;
this.width = width;
this.height = height;
}
@Override
public void accept(DocumentVisitor visitor) {
visitor.visit(this);
}
// getters...
}
// 访问者接口
interface DocumentVisitor {
void visit(TextElement text);
void visit(ImageElement image);
void visit(TableElement table);
byte[] getResult();
}
// PDF访问者实现
class PdfVisitor implements DocumentVisitor {
private ByteArrayOutputStream outputStream;
private Document document;
private PdfWriter writer;
public PdfVisitor() {
this.outputStream = new ByteArrayOutputStream();
this.document = new Document();
try {
this.writer = PdfWriter.getInstance(document, outputStream);
document.open();
} catch (DocumentException e) {
throw new RuntimeException("PDF初始化失败", e);
}
}
@Override
public void visit(TextElement text) {
Font font = new Font(Font.FontFamily.HELVETICA,
text.getStyle().getFontSize(),
text.getStyle().isBold() ? Font.BOLD : Font.NORMAL);
Paragraph paragraph = new Paragraph(text.getContent(), font);
try {
document.add(paragraph);
} catch (DocumentException e) {
throw new RuntimeException("添加文本失败", e);
}
}
@Override
public void visit(ImageElement image) {
try {
Image pdfImage = Image.getInstance(image.getImagePath());
pdfImage.scaleToFit(image.getWidth(), image.getHeight());
document.add(pdfImage);
} catch (Exception e) {
throw new RuntimeException("添加图片失败", e);
}
}
@Override
public void visit(TableElement table) {
PdfPTable pdfTable = new PdfPTable(table.getColumns());
// 表格实现逻辑...
try {
document.add(pdfTable);
} catch (DocumentException e) {
throw new RuntimeException("添加表格失败", e);
}
}
@Override
public byte[] getResult() {
try {
document.close();
writer.close();
return outputStream.toByteArray();
} finally {
try {
outputStream.close();
} catch (IOException e) {
// 忽略关闭异常
}
}
}
}
// 文档对象
class Document {
private List<DocumentElement> elements = new ArrayList<>();
public void addElement(DocumentElement element) {
elements.add(element);
}
public byte[] generatePdf() {
PdfVisitor pdfVisitor = new PdfVisitor();
elements.forEach(e -> e.accept(pdfVisitor));
return pdfVisitor.getResult();
}
}
// 使用示例
public class PdfGenerationDemo {
public static void main(String[] args) throws IOException {
Document doc = new Document();
doc.addElement(new TextElement("标题", new TextStyle(16, true)));
doc.addElement(new TextElement("这是正文内容...", new TextStyle(12, false)));
doc.addElement(new ImageElement("logo.png", 100, 50));
byte[] pdfBytes = doc.generatePdf();
// 保存到文件
Files.write(Paths.get("output.pdf"), pdfBytes);
}
}
25. 解释简单工厂,工厂模式和抽象工厂模式的区别。
这三种模式都属于创建型模式,但解决的问题层次不同:
简单工厂是最基础的形式,通过一个工厂类集中处理所有对象的创建逻辑。它的优点是简单直接,但缺点明显——当需要新增产品类型时,必须修改工厂类代码,违反了开闭原则。
工厂方法通过引入抽象工厂接口,将具体产品的创建延迟到子类实现。这样新增产品类型时,只需增加新的工厂子类,无需修改现有代码,完美遵循开闭原则。代价是会产生较多的工厂类。
抽象工厂则更进一步,用于创建相关联的产品族。比如需要同时创建跨平台的Button和Menu组件。它强调产品之间的配套关系,适合系统需要切换整套产品的场景。扩展产品族很方便,但扩展产品种类(如新增Textbox)需要修改抽象接口。
26. 什么情况下应该选择工厂方法而不是抽象工厂?
当系统只需要创建单一类型产品,且该产品可能有多种实现时,应该使用工厂方法。例如日志系统需要支持FileLogger、DatabaseLogger等,这时工厂方法更合适。而如果需要创建配套的多个相关产品(如UI组件套装),则应该使用抽象工厂。
27. 简单工厂违反开闭原则,为什么还要使用它?
简单工厂在以下场景仍有价值:产品类型非常固定,几乎不会扩展,快速原型开发阶段,需要简单实现,与Spring等IoC容器配合使用时,容器本身相当于一个高度配置化的简单工厂,虽然它不够灵活,但在适当场景下,它的简单性反而成为优势。
28. 代理模式和装饰器模式的区别?
虽然代理模式和装饰器模式在结构上非常相似(都实现相同接口并持有目标对象的引用),但它们的设计目的有本质不同:
代理模式的核心是控制访问:代理类通常自行创建和管理目标对象,重点在于对目标对象的访问过程进行管控。
典型应用包括:虚拟代理(延迟加载大对象);保护代理(权限控制);远程代理(网络通信)。
装饰器模式的核心是功能增强:装饰器通过构造函数接收被装饰对象,可以多层嵌套,动态添加新功能
典型特征是:保持对象接口不变;支持多次装饰(如Java I/O流:BufferedInputStream(new FileInputStream()))。
关键区别在于:
代理是对象的替身,控制原始对象的访问
装饰是功能的包装,增强原始对象的能力"
29. 观察者模式的核心角色?
观察者模式包含四个核心角色:
Subject(主题):作为被观察的对象,需要维护所有观察者的引用列表,并提供注册(attach)和注销(detach)的方法。当自身状态变化时,通过notifyObservers()方法通知所有注册的观察者。
Observer(观察者):定义统一的更新接口(update),所有具体观察者通过实现这个接口来接收主题的状态变更通知。这个接口支持两种模式:
推模式:主题将变化数据直接通过update参数传递
拉模式:观察者从主题对象主动获取所需数据
ConcreteSubject(具体主题):继承Subject的具体实现,负责维护具体状态,并在状态改变时触发通知流程。例如微信公众号平台中的具体公众号。
ConcreteObserver(具体观察者):实现Observer接口,定义收到通知后的具体响应行为。比如用户收到文章推送后执行阅读操作。
这种设计实现了主题与观察者的松耦合,主题不需要知道观察者的具体实现,只需维护观察者接口列表即可。"
30. 推模式和拉模式有什么区别?各有什么优缺点?
"推模式(Push)和拉模式(Pull)是观察者通知的两种实现方式:
推模式:
主题将变更数据直接通过update参数传递
优点:观察者立即获得所需数据,减少后续交互
缺点:可能传递不必要的数据(如Java中的EventObject)
拉模式:
主题只通知变化,观察者主动调用主题方法获取数据
优点:观察者可以按需获取数据
缺点:增加耦合(观察者需要知道主题的数据结构)
实际开发中,推模式更常用,但在观察者需要不同数据子集时,拉模式更灵活。"
31. 简单工厂和策略模式的区别?
简单工厂模式是创建型设计模式,核心解决对象创建问题,它通过一个工厂类集中管理对象的实例化过程,根据输入参数返回具体产品对象,但新增产品类型需要修改工厂代码。策略模式是行为型设计模式,核心解决算法选择问题,它定义一系列可互换的算法族,通过上下文类动态切换策略实现,新增策略只需扩展新类无需修改已有代码。两者虽然类图相似,但根本区别在于:工厂模式关注"如何创建对象"(对象生命周期起点),策略模式关注"如何执行行为"(对象生命周期中的功能实现)。实际开发中,二者常组合使用——用工厂创建策略对象,再用策略模式执行算法。
32. 说说你如何用设计模式优化项目中的某个功能。
// 1. 策略接口
public interface FuelSupplyStrategy<T, R> {
void saveInfo(T vo, String recordId, String creatorId);
void saveFiles(T vo, String guid) throws ActionException;
}
// 2. 抽象模板类(可选,进一步提取公共逻辑)
public abstract class AbstractFuelSupplyTemplate<T, R extends BaseShipOilSupplyEntity>
implements FuelSupplyStrategy<T, R> {
@Override
public void saveInfo(T vo, String recordId, String creatorId) {
R entity = createEntity();
BeanUtils.copyProperties(vo, entity);
fillEntity(entity, recordId, creatorId);
getDao().save(entity);
saveFiles(vo, entity.getGuid());
}
protected abstract R createEntity();
protected abstract JpaRepository<R, String> getDao();
protected void fillEntity(R entity, String recordId, String creatorId) {
entity.setRecordId(recordId);
entity.setCreatorId(creatorId);
}
}
// 3. 具体策略类(示例:岸基供油)
@Service
public class ShoreBasedStrategy extends AbstractFuelSupplyTemplate<ShoreBasedFuelStationInfoVo, ShoreBasedFuelStationInfoEntity> {
@Autowired private ShoreBasedFuelStationInfoDao dao;
@Override
protected ShoreBasedFuelStationInfoEntity createEntity() {
return new ShoreBasedFuelStationInfoEntity();
}
@Override
protected JpaRepository<ShoreBasedFuelStationInfoEntity, String> getDao() {
return dao;
}
@Override
public void saveFiles(ShoreBasedFuelStationInfoVo vo, String guid) throws ActionException {
// 具体文件保存逻辑
}
}
// 4. 上下文类(统一调用策略)
@Service
public class FuelSupplyContext {
@Autowired private Map<String, FuelSupplyStrategy<?, ?>> strategies;
public void save(String type, Object vo, String recordId, String creatorId) {
FuelSupplyStrategy strategy = strategies.get(type + "Strategy");
if (strategy != null) {
strategy.saveInfo(vo, recordId, creatorId);
}
}
}
// 5. 原方法重构后
private void createAndSaveOperationInfo(ShipOilSupplyRecordVo vo, ShipOilSupplyRecordEntity entity) {
logger.info("进入方法,recordId: {}", entity.getRecordId());
FuelSupplyContext context = new FuelSupplyContext();
saveIfNotEmpty(vo.getShoreBasedFuelStationInfoVoList(), "shoreBased", context, entity);
saveIfNotEmpty(vo.getWaterBasedFuelStationInfoVoList(), "waterBased", context, entity);
// ...其他类型
logger.info("退出方法,recordId: {}", entity.getRecordId());
}
private <T> void saveIfNotEmpty(List<T> list, String type, FuelSupplyContext context, ShipOilSupplyRecordEntity entity) {
if (list != null && !list.isEmpty()) {
list.forEach(vo -> context.save(type, vo, entity.getRecordId(), vo.getCreatorId()));
}
}