新增消费者时如何避免RequestData类膨胀?
系统建模优化方案:解决RequestData膨胀与数据冗余问题
问题背景
现有系统包含多个请求处理器(如定价计算器),每个处理器仅依赖请求数据的子集,但所有处理器共享一个RequestData类。新增处理器时需修改RequestData添加字段,导致:
- 类持续膨胀,复杂度升高
- 多数处理器携带冗余数据,效率低下
原代码中的IRequestProcessor接口及各消费者实现、RequestData类存在上述耦合问题。
优化方案
1. 接口隔离原则(ISP):拆分数据契约
针对不同处理器的依赖,将RequestData拆分为多个粒度更细的接口,让每个处理器仅依赖自己需要的数据契约。
示例实现:
// 基础数据契约,所有处理器共享 public interface IBaseRequestData { double BaseCost { get; } } // ConsumerA、B依赖的扩展数据契约 public interface IValueRequestData : IBaseRequestData { double SomeValue { get; } } // ConsumerB额外依赖的条件数据契约 public interface IConditionRequestData : IValueRequestData { bool SomeCondition { get; } } // ConsumerC、D依赖的详情数据契约 public interface IDetailRequestData : IBaseRequestData { bool HasDetail { get; } } // ConsumerD额外依赖的期限数据契约 public interface IDueDateRequestData : IDetailRequestData { DateTime DueDate { get; } } // 处理器仅依赖自身需要的接口 public class ConsumerA : IRequestProcessor { public double ComputeResult(IValueRequestData request) => 10 * request.SomeValue + request.BaseCost; } public class ConsumerB : IRequestProcessor { public double ComputeResult(IConditionRequestData request) => (request.SomeCondition ? 10 : 0) * request.SomeValue + request.BaseCost; } public class ConsumerC : IRequestProcessor { public double ComputeResult(IDetailRequestData request) => request.HasDetail ? 1.5 * request.BaseCost : request.BaseCost; } public class ConsumerD : IRequestProcessor { public double ComputeResult(IDueDateRequestData request) => request.DueDate < DateTime.Now.AddMonths(6) ? -1 : request.HasDetail ? 1.5 * request.BaseCost : request.BaseCost; } // 完整请求类按需实现多个接口 public class FullRequestData : IConditionRequestData, IDueDateRequestData { public double BaseCost { get; } public double SomeValue { get; } public bool SomeCondition { get; } public bool HasDetail { get; } public DateTime DueDate { get; } public FullRequestData(double baseCost, double someValue, bool someCondition, bool hasDetail, DateTime dueDate) { BaseCost = baseCost; SomeValue = someValue; SomeCondition = someCondition; HasDetail = hasDetail; DueDate = dueDate; } }
优势:每个处理器仅依赖必要数据,新增处理器时只需定义新的数据接口,无需修改现有数据类,符合开闭原则。
2. 组合模式:分层数据模型
将请求数据拆分为多个独立的子数据类,通过组合方式构建完整请求,每个处理器按需获取子数据。
示例实现:
// 基础数据类 public class BaseRequestData { public double BaseCost { get; } public BaseRequestData(double baseCost) => BaseCost = baseCost; } // 价值相关子数据 public class ValueData { public double SomeValue { get; } public ValueData(double someValue) => SomeValue = someValue; } // 条件相关子数据 public class ConditionData { public bool SomeCondition { get; } public ConditionData(bool someCondition) => SomeCondition = someCondition; } // 详情相关子数据 public class DetailData { public bool HasDetail { get; } public DetailData(bool hasDetail) => HasDetail = hasDetail; } // 期限相关子数据 public class DueDateData { public DateTime DueDate { get; } public DueDateData(DateTime dueDate) => DueDate = dueDate; } // 完整请求类通过组合子数据构建 public class RequestData { public BaseRequestData BaseData { get; } public ValueData ValueData { get; } public ConditionData ConditionData { get; } public DetailData DetailData { get; } public DueDateData DueDateData { get; } // 构造器支持按需注入子数据,避免冗余 public RequestData(BaseRequestData baseData, ValueData valueData = null, ConditionData conditionData = null, DetailData detailData = null, DueDateData dueDateData = null) { BaseData = baseData; ValueData = valueData; ConditionData = conditionData; DetailData = detailData; DueDateData = dueDateData; } } // 处理器按需访问子数据 public class ConsumerA : IRequestProcessor { public double ComputeResult(RequestData request) => 10 * request.ValueData.SomeValue + request.BaseData.BaseCost; } public class ConsumerD : IRequestProcessor { public double ComputeResult(RequestData request) => request.DueDateData.DueDate < DateTime.Now.AddMonths(6) ? -1 : request.DetailData.HasDetail ? 1.5 * request.BaseData.BaseCost : request.BaseData.BaseCost; }
优势:数据结构模块化,新增子数据类不影响现有代码,处理器仅访问需要的子数据,减少冗余。
3. 策略模式+数据适配
为每个处理器定义专属的DTO(数据传输对象),通过适配器将原始请求数据转换为处理器所需的DTO,避免直接依赖大而全的RequestData。
示例实现:
// 泛型处理器接口,接受专属DTO public interface IRequestProcessor<T> { double ComputeResult(T dto); } // ConsumerA专属DTO public class ConsumerADto { public double BaseCost { get; } public double SomeValue { get; } public ConsumerADto(double baseCost, double someValue) { BaseCost = baseCost; SomeValue = someValue; } } // ConsumerA实现 public class ConsumerA : IRequestProcessor<ConsumerADto> { public double ComputeResult(ConsumerADto dto) => 10 * dto.SomeValue + dto.BaseCost; } // ConsumerD专属DTO public class ConsumerDDto { public double BaseCost { get; } public bool HasDetail { get; } public DateTime DueDate { get; } public ConsumerDDto(double baseCost, bool hasDetail, DateTime dueDate) { BaseCost = baseCost; HasDetail = hasDetail; DueDate = dueDate; } } // ConsumerD实现 public class ConsumerD : IRequestProcessor<ConsumerDDto> { public double ComputeResult(ConsumerDDto dto) => dto.DueDate < DateTime.Now.AddMonths(6) ? -1 : dto.HasDetail ? 1.5 * dto.BaseCost : dto.BaseCost; } // 适配器:转换原始数据为各处理器的DTO public class RequestDataAdapter { public ConsumerADto ToConsumerADto(RequestData request) => new ConsumerADto(request.BaseCost, request.SomeValue); public ConsumerDDto ToConsumerDDto(RequestData request) => new ConsumerDDto(request.BaseCost, request.HasDetail, request.DueDate); } // 调用示例 var originalRequest = new RequestData(...); var adapter = new RequestDataAdapter(); var consumerA = new ConsumerA(); var resultA = consumerA.ComputeResult(adapter.ToConsumerADto(originalRequest));
优势:处理器与原始数据完全解耦,新增处理器只需添加对应的DTO和适配器方法,对现有代码无侵入性。
总结
以上方案均围绕单一职责原则和开闭原则设计,核心思路是将大而全的RequestData拆分为粒度更细的契约/模块,让每个组件仅依赖自身所需的数据。实际选型可根据系统复杂度、扩展性需求决定:
- 若需要严格的契约隔离,优先选择接口隔离原则方案;
- 若数据结构天然可拆分,组合模式更直观;
- 若需完全解耦处理器与原始数据,策略+适配方案最灵活。
内容的提问来源于stack exchange,提问作者Toto
相关产品推荐
相关产品推荐

