使用设计模式实现自然人/法人Person类的最优方案咨询
最佳实现方案
推荐设计模式:多态继承 + 工厂方法模式
现有Person类的原生写法只能靠业务代码人工保证自然人、法人关联关系互斥,没有强制校验机制,很容易出现非法的脏数据。采用多态继承配合工厂方法是最贴合需求的实现方案:
- 将
Person调整为抽象基类,提取自然人、法人共有的属性到基类中,PersonReal(自然人)和PersonLegal(法人)作为子类继承Person,天然保证一个Person实例只能属于其中一种类型,从面向对象特性层面就杜绝了同时属于两种类型的可能。 - 配合工厂方法模式封装实例创建逻辑,避免上层业务直接实例化子类,统一控制实例创建规则,降低业务代码出错概率。
代码实现示例
// 抽象Person基类 public abstract class Person { public Guid Id { get; set; } public string Title { get; set; } // 其他所有自然人、法人共有的属性可统一放到此处 } // 自然人类 public class PersonReal : Person { public Guid Sex { get; set; } public string FirstName { get; set; } } // 法人类 public class PersonLegal : Person { public string Name { get; set; } public Guid? TopManager { get; set; } public string NationalCode { get; set; } } // 工厂类,封装实例创建逻辑 public static class PersonFactory { public static Person CreateRealPerson(string title, Guid sex, string firstName) { return new PersonReal { Id = Guid.NewGuid(), Title = title, Sex = sex, FirstName = firstName }; } public static Person CreateLegalPerson(string title, string name, Guid? topManager, string nationalCode) { return new PersonLegal { Id = Guid.NewGuid(), Title = title, Name = name, TopManager = topManager, NationalCode = nationalCode }; } }
如果使用Entity Framework做数据持久化,可以直接用TPH(Table Per Hierarchy)映射策略,所有Person子类数据存在同一张表中,EF会自动生成Discriminator列标记实例类型,不需要额外处理关联关系。
兼容原有结构的备选方案:类型状态模式
如果因为历史代码/表结构约束,无法调整继承关系,可以用类型状态模式,增加身份类型枚举+属性赋值校验,强制保证关联关系互斥:
public enum PersonType { Unknown, Real, Legal } public class Person { public Guid Id { get; set; } private PersonType _type = PersonType.Unknown; private Guid? _personRealId; private Guid? _personLegalId; private PersonReal _personReal; private PersonLegal _personLegal; public PersonType Type => _type; public Guid? PersonRealId { get => _personRealId; set { if (value.HasValue && _personLegalId.HasValue) throw new InvalidOperationException("同一Person实例不能同时关联自然人和法人"); _personRealId = value; _type = value.HasValue ? PersonType.Real : PersonType.Unknown; } } public Guid? PersonLegalId { get => _personLegalId; set { if (value.HasValue && _personRealId.HasValue) throw new InvalidOperationException("同一Person实例不能同时关联自然人和法人"); _personLegalId = value; _type = value.HasValue ? PersonType.Legal : PersonType.Unknown; } } public virtual PersonReal PersonReal { get => _personReal; set { if (value != null && _personLegal != null) throw new InvalidOperationException("同一Person实例不能同时关联自然人和法人"); _personReal = value; _personRealId = value?.Id; _type = value != null ? PersonType.Real : PersonType.Unknown; } } public virtual PersonLegal PersonLegal { get => _personLegal; set { if (value != null && _personReal != null) throw new InvalidOperationException("同一Person实例不能同时关联自然人和法人"); _personLegal = value; _personLegalId = value?.Id; _type = value != null ? PersonType.Legal : PersonType.Unknown; } } }
该方案不需要改动现有表结构,所有互斥校验在属性赋值时自动触发,能有效避免非法数据写入。
内容的提问来源于stack exchange,提问作者Javad Azimi
相关产品推荐
相关产品推荐

