我一直在考虑处理具有多态性孩子的聚合实体的最佳方法,并想知道是否有人可以告诉我下面的实现是否是正确的实现,或者我是否可能忽略了一些潜在的陷阱。
以下代码演示了一个用于在不同的时间表上发送某种通信的系统。
THE DOMAIN
class Delivery {
Dictionary<Type, Func<ScheduleDto, ScheduleEntity>> ScheduleFactories =
new Dictionary<Type, Func<ScheduleDto, ScheduleEntity>> {
{ typeof(DailyScheduleDto), GetDailySchedule},
{ typeof(HourlyScheduleDto), GetHourlySchedule}
};
public Guid Id { get; private set; }
public string Message { get; private set; }
public StatusEnum Status { get private set; }
public Schedule Schedule { get; private set; }
public Delivery(DeliveryDto dto){
Id = dto.Id;
Message = dto.Message;
Status = StatusEnum.New;
Schedule = ScheduleFactories[dto.Schedule.GetType()](dto.Schedule);
}
public void Update(DeliveryDto dto){
Message = dto.Message;
Schedule.Update(dto.Schedule);
}
public DateTime GetNextExecution(DateTime after){
return Schedule.GetNextExecution(after);
}
public enum StatusEnum { New, InProgress, Complete }
Schedule GetDailyScheduleEntity(ScheduleDto dto){
return new DailyScheduleEntity((DailyScheduleDto)dto);
}
Schedule GetHourlyScheduleEntity(ScheduleDto dto){
return new HourlyScheduleEntity((HourlyScheduleDto)dto);
}
}
abstract class Schedule {
public DateTime StartsOn { get; private set; }
public abstract DateTime GetNextExecution(DateTime after);
public virtual void Update(ScheduleDto dto){
StartsOn = dto.StartsOn;
}
public Schedule(ScheduleDto dto) {
StartsOn = dto.StartsOn;
}
}
class DailySchedule : Schedule {
public DaysEnum Days { get; private set; }
public override DateTime GetNextExecution(DateTime after){
return // logic for calculating the next day based on Days;
}
public DailySchedule(DailyScheduleDto dto)
: base(dto)
{
Days = dto.Days.ToEnum<DaysEnum>();
base.Update(dto);
}
public override void Update(ScheduleDto dto){
if(!(dto is DialyScheduleDto)) throw new InvalidOperationException();
Days = ((DailyScheduleDto)dto).Days.ToEnum<DaysEnum>();
}
[Flags]
public enum DaysEnum{
Monday, Tuesday, Wednesday, Thursday, Friday
}
}
class HourlySchedule : Schedule {
public int Interval { get; private set }
public override DateTime GetNextExecution(DateTime after){
return // logic for calculating the next hour based on interval;
}
public HourlySchedule(HourlyScheduleDto dto)
: base(dto)
{
Interval = dto.Interval;
}
public override void Update(ScheduleDto dto){
if(!(dto is HourlyScheduleDto)) throw new InvalidOperationException();
Interval = ((HoulryScheduleDto)dto).Interval;
base.Update(dto);
}
}
class DeliveryService : IDeliveryService {
public void Add(DeliveryDto dto){
var delivery = new Delivery(dto);
var scheduledTask = new ScheduledTask(
delivery.Id,
delivery.GetNextExecution(after: DateTime.Now)
);
unitOfWork.Add<ScheduledTask>(scheduledTask);
unitOfWork.Add<Delivery>(delivery);
unitOfWork.SaveChanges();
}
public void Update(DeliveryDto dto){
var delivery = unitOfWork.Get<Delivery>(dto.Id);
delivery.Update(dto);
unitOfWork.SaveChanges();
}
public void SendAll(){
var scheduledTasks = unitOfWork.GetAll<ScheduledTask>(st => st.ExecutionDateTime < DateTime.Now && st.Status == ScheduledTaskStatus.New);
scheduledTasks.ForEach(//Send to subsystem for execution);
}
}
合同
public class DeliveryDto{
public Guid Id {get; set;}
public string Message {get; set;}
public ScheduleDto Schedule {get; set;}
}
public class ScheduleDto{
public DateTime StartsOn {get; set;}
}
public class HourlyScheduleDto : ScheduleDto{
public int Interval {get; set;}
}
public class DailyScheduleDto : ScheduleDto{
public String Days {get; set;}
}
我通常会有特定的dt用于创建新实例和执行更新,但为简单起见,请在此处重复使用相同的类型。
虽然有一种观点认为直接将Dtos传递给实体会将客户端紧密地耦合到域中,但我的目的是提供一个薄的外观类型层来负责升级&#39;如果当前需要改变以适应不断变化的业务需求,dtos将保持与现有客户的兼容性
答案 0 :(得分:1)
一些想法:
设计似乎过于复杂。 DailySchedule
和HourlySchedule
都继承Schedule
但是在Update()
方法中,您必须手动将参数强制转换为不同的类型,这是一种在那里滥用继承的气味。
此外,包含Schedule of Schedule工厂的Delivery
似乎很可疑。我能看到的唯一原因是你必须生成多个不同类型的多个对象,但Delivery
似乎只需要一个Schedule
对象。
我在Schedule.Update(dto.Schedule)
中执行Delivery
时看到了一个潜在的错误,并且时间表不是Delivery
初始化的类型(每小时而不是每日,反之亦然) )。
总的来说,代码无法传达域意图IMO。 Update
含糊不清 - 用户需要更新什么以及出于什么原因? after
变量名称含糊不清。为什么GetNextDeliverySchedule()
会返回DateTime而不是名称?等等。