如何设计CarInfo类的客户成员,支持Customer对象或独立姓名?
问题描述
我有一个用于存储车辆及其车主信息的CarInfo类。车主分为两种情况:一是已知客户(可查询并填充Customer类实例),二是仅知晓姓名(无法填充Customer类)。当前的类设计如下:
public class CarInfo { public Guid Id { get; } // 其他属性... public Customer? Customer { get; set; } // 要么填充该成员 public string? FirstName { get; set; } // 要么填充姓和名字段 public string? LastName { get; set; } // 其他属性... }
请问处理这种情况的最佳设计方案是什么?是否应将客户相关信息拆分到新的CustomerInfo类中,通过多态来处理,例如:
public class CustomerInfo { public Customer? Customer { get; private set; } public string? FirstName { get; private set; } public string? LastName { get; private set; } public CustomerInfo(Customer customer) => Customer = customer; public CustomerInfo(string firstName, string lastName) { FirstName = firstName; LastName = lastName; } }
并在CarInfo中这样使用?
public class CarInfo { public Guid Id { get; } // 其他属性... public CustomerInfo OwnerInfo { get; set; } // 其他属性... }
我不确定应参考哪些最佳实践或设计模式。
解决方案
你的思路方向是对的——原设计的核心问题是无法保证车主信息的状态一致性:如果同时给Customer和FirstName/LastName赋值,会导致数据矛盾,后续业务逻辑处理时容易出错。下面给出两种符合最佳实践的优化方案,可根据场景选择:
方案一:封装互斥状态的CustomerInfo类(简单实用)
直接基于你的思路优化,通过封装确保两种车主状态互斥,同时提供统一的访问接口,避免外部处理时的判断逻辑:
public class CustomerInfo { public Customer? KnownCustomer { get; } public string? FirstName { get; } public string? LastName { get; } // 已知客户的构造函数,确保参数非空 public CustomerInfo(Customer customer) { KnownCustomer = customer ?? throw new ArgumentNullException(nameof(customer)); // 从Customer同步姓名,确保外部获取的姓名一致 FirstName = customer.FirstName; LastName = customer.LastName; } // 仅知姓名的构造函数,确保至少有一个姓名字段有效 public CustomerInfo(string firstName, string lastName) { if (string.IsNullOrWhiteSpace(firstName) && string.IsNullOrWhiteSpace(lastName)) throw new ArgumentException("至少需要提供姓名的一部分"); FirstName = firstName; LastName = lastName; } // 统一获取全名的方法,外部无需判断类型 public string GetFullName() { return KnownCustomer != null ? $"{KnownCustomer.FirstName} {KnownCustomer.LastName}".Trim() : $"{FirstName} {LastName}".Trim(); } }
然后修改CarInfo:
public class CarInfo { public Guid Id { get; } // 其他车辆属性... public CustomerInfo OwnerInfo { get; set; } // 其他属性... }
优势
- 彻底避免状态不一致:构造函数只能选择一种车主类型,不会出现同时赋值的情况
- 封装了车主信息的逻辑,
CarInfo只需关注车辆本身,符合单一职责原则 - 对外提供统一的访问方式(如
GetFullName),减少外部代码的判断逻辑
方案二:多态抽象设计(灵活扩展)
如果未来可能新增更多车主类型(比如公司车主、临时车主),可以用抽象基类+派生类的多态设计,符合开闭原则(新增类型无需修改现有代码):
// 抽象基类,定义车主信息的统一接口 public abstract class OwnerInfo { public abstract string GetFullName(); } // 已知客户的派生类 public class KnownCustomerOwner : OwnerInfo { public Customer Customer { get; } public KnownCustomerOwner(Customer customer) { Customer = customer ?? throw new ArgumentNullException(nameof(customer)); } public override string GetFullName() { return $"{Customer.FirstName} {Customer.LastName}".Trim(); } } // 仅知姓名的派生类 public class AnonymousOwner : OwnerInfo { public string FirstName { get; } public string LastName { get; } public AnonymousOwner(string firstName, string lastName) { if (string.IsNullOrWhiteSpace(firstName) && string.IsNullOrWhiteSpace(lastName)) throw new ArgumentException("至少需要提供姓名的一部分"); FirstName = firstName; LastName = lastName; } public override string GetFullName() { return $"{FirstName} {LastName}".Trim(); } }
修改CarInfo使用抽象基类:
public class CarInfo { public Guid Id { get; } // 其他车辆属性... public OwnerInfo OwnerInfo { get; set; } // 其他属性... }
优势
- 多态特性让外部代码无需判断车主类型,直接调用抽象方法即可
- 新增车主类型时只需添加新的派生类,无需修改现有业务逻辑
- 每个派生类只负责一种车主的逻辑,职责更清晰
方案选择建议
- 如果只是固定两种车主类型,优先选方案一,实现简单,维护成本低
- 如果未来有扩展需求,或者需要处理更复杂的车主逻辑,选方案二,灵活性更高
内容的提问来源于stack exchange,提问作者Peter Rundqvist
相关产品推荐
相关产品推荐

