获取图表数据序列时,最佳设计模式选型及代码架构优化咨询
后端图表数据API的代码组织方案
1. 明确分层职责,拆解现有逻辑
把当前Controller直接调用Repository+计算函数的结构拆为四层,保证职责单一:
- Controller层:仅负责接收请求、参数校验、调用Service返回响应,不处理任何业务逻辑或数据计算。
- Service层:作为业务逻辑核心,负责编排数据流程——调用Repository获取原始数据,根据需求选择是否触发计算逻辑,最终返回可直接用于图表的序列。
- Repository层:只做数据库交互,执行SQL查询返回原始数据模型,不做任何数据转换或计算。
- Calculator层:将所有数据计算逻辑抽为独立的类/函数集合,每个计算逻辑对应一个独立方法(比如
calculateGrowthRate、computeMovingAverage),专注纯数据运算,不依赖数据库或业务上下文。
2. 用设计模式简化逻辑扩展
策略模式(Strategy Pattern)
如果存在多种数据计算规则(比如不同图表需要不同的增长率、平均值计算),把每种计算逻辑封装成独立策略类,统一实现一个通用接口:
// 通用计算策略接口 public interface DataCalculationStrategy { List<Double> calculate(List<Double> rawData); } // 具体策略:计算移动平均值 public class MovingAverageStrategy implements DataCalculationStrategy { private int windowSize; public MovingAverageStrategy(int windowSize) { this.windowSize = windowSize; } @Override public List<Double> calculate(List<Double> rawData) { // 移动平均计算逻辑实现 } } // 具体策略:计算同比增长率 public class YoYGrowthStrategy implements DataCalculationStrategy { @Override public List<Double> calculate(List<Double> rawData) { // 同比增长率计算逻辑实现 } }
Service层根据请求参数(比如图表类型)选择对应策略,避免大量if-else判断。
工厂模式(Factory Pattern)
配合策略模式,用工厂类创建具体的计算策略实例,Service层只需调用工厂获取策略,无需关心具体实现:
public class CalculationStrategyFactory { public static DataCalculationStrategy getStrategy(String chartType) { return switch(chartType) { case "MOVING_AVERAGE" -> new MovingAverageStrategy(7); case "YOY_GROWTH" -> new YoYGrowthStrategy(); default -> throw new IllegalArgumentException("不支持的图表类型"); }; } }
装饰器模式(Decorator Pattern)
如果需要对同一组原始数据执行多步连续计算(比如先算增长率再做数据平滑),用装饰器模式叠加计算逻辑,避免硬编码步骤:
public abstract class DataCalculationDecorator implements DataCalculationStrategy { protected DataCalculationStrategy wrappedStrategy; public DataCalculationDecorator(DataCalculationStrategy wrappedStrategy) { this.wrappedStrategy = wrappedStrategy; } } // 装饰器:对计算结果做平滑处理 public class SmoothingDecorator extends DataCalculationDecorator { public SmoothingDecorator(DataCalculationStrategy wrappedStrategy) { super(wrappedStrategy); } @Override public List<Double> calculate(List<Double> rawData) { List<Double> calculatedData = wrappedStrategy.calculate(rawData); // 对计算结果做平滑处理 return smoothedData; } }
使用时可自由组合:new SmoothingDecorator(new YoYGrowthStrategy()),实现先算增长率再平滑的链式操作。
3. 代码拆分的具体实践
- 按业务领域拆分Repository:比如
UserBehaviorRepository、SalesDataRepository,每个类仅负责对应领域的SQL查询。 - 按计算类型分组Calculator:比如
TrendCalculators、StatisticCalculators类,每个类下存放同类型的计算方法。 - 按图表场景拆分Service:比如
SalesChartService、UserEngagementChartService,每个Service仅负责一类图表的数据组装逻辑。
4. 额外优化点
- 将常用计算参数(比如移动平均窗口大小)抽入配置文件(如
application.yml),避免硬编码。 - 为计算逻辑添加单元测试,纯数据运算易写测试用例,能有效保证计算结果准确性。
- 若计算逻辑耗时较长,对重复计算的结果做缓存(如用Redis),避免每次请求重复计算。
内容的提问来源于stack exchange,提问作者saikano
相关产品推荐
相关产品推荐

