.NET对象层次结构-到事件还是不到事件

.NET对象层次结构-到事件还是不到事件,.net,oop,class-hierarchy,.net,Oop,Class Hierarchy,您的工作是设计一个支持任务跟踪的项目计划类库(类似于MS Project的工作方式)。此类库有一个任务对象(以及其他对象) Task对象具有EstimatedHours(Double)、StartDate(DateTime)和EndDate(DateTime)等属性。一个任务对象可以有一个父任务,和几个子任务对象。具有子项(即父项)的任务的估计时数、开始日期和结束日期属性取决于其直接子项的属性。父任务的StartDate是其子任务中最早的StartDate。父任务的EndDate是其子任务的最新

您的工作是设计一个支持任务跟踪的项目计划类库(类似于MS Project的工作方式)。此类库有一个
任务
对象(以及其他对象)

Task
对象具有
EstimatedHours
Double
)、
StartDate
DateTime
)和
EndDate
DateTime
)等属性。一个
任务
对象可以有一个父
任务
,和几个子
任务
对象。具有子项(即父项)的任务的
估计时数
开始日期
结束日期
属性取决于其直接子项的属性。父任务
StartDate
是其子任务中最早的
StartDate
。父任务
EndDate
是其子任务的最新
EndDate
。父任务
估计时数是其子任务估计时数的总和。因此,在具有子任务的
任务上更改这些属性是无效的

如果在具有父任务的任务上更改了EstimatedHours、StartDate或EndDate,您将如何处理该用例?(父任务的属性是其子任务的反映,因此对子任务的任何更改都可能需要调整父任务的属性以适当反映这些更改)

一个选项是在每个属性发生更改时都有一个事件。父
任务
将侦听其直接子
任务
对象上的这些事件,并在这些事件发生时对其自身属性进行适当更改。这是一个好方法,还是有更好的方法?你会怎么做

下面是关于
任务
对象的基本概念:

Public Class Task

  Private mChildren As List(Of Task)

  Private mEndDate As DateTime = DateTime.MinVlue
  Public Property EndDate() As DateTime
    Get
      Return mEndDate 
    End Get
    Set(ByVal value As DateTime)
      mEndDate = value
      'What to do here?
    End Set
  End Property

  Private mEstimatedHours As Double = 0.0
  Public Property EstimatedHours() As Double 
    Get
      Return mEstimatedHours 
    End Get
    Set(ByVal value As Double)
      mEstimatedHours = value
      'What to do here?
    End Set
  End Property

  Private mStartDate As DateTime = DateTime.MinVlue
  Public Property StartDate() As DateTime
    Get
      Return mStartDate 
    End Get
    Set(ByVal value As DateTime)
      mStartDate = value
      'What to do here?
    End Set
  End Property

End Class

解决这个问题的正确方法是使用观测器设计模式。对实现观察者模式的详细解释超出了本文讨论的范围。但这里有一些关于观察者模式的链接。一个环节是,另一个环节是


< P>我不认为这是模型责任的一部分,而是它上面的控制器。

向模型中添加事件或观察者模式会增加其他方面的复杂性,如序列化,这是您希望避免的


让它成为进行修改的类的责任,而不是模型本身。请记住:模型的职责是包含信息,而不是暗示业务规则。

请记住,当事件链中的一个事件引发异常时,将不会调用以下事件。因此,如果数据中注册了其他事件,则可能不会调用您的事件


如果基本任务与其子任务永不脱离接触对应用程序至关重要,则不要使用事件。

我将首先构建对象模型,以便它动态计算值。我将给你C#,因为我最熟悉它(我也使用字段而不是属性来保持样本的小):

公共类任务
{
public List Children=new List();
公共任务父级;
私有整数持续时间;
公共整数持续时间
{
得到
{
如果(子项计数>0)
{ 
返回SumChildrenDuration();
}
返回时间;
}
设置
{
如果(子项计数>0)
抛出新异常(“只能添加到叶子”);
_持续时间=值;
}
}
}

一旦你有了它,你现在就有了运行系统所需的所有代码。您可能会发现系统的性能足够好,并将其保持为这样。否则,您可以添加其他功能来缓存结果,然后在对象更改时重置缓存。无论您做什么,请务必仔细分析它,因为您希望确保缓存和过期不会比动态计算更昂贵。

我不确定我实际会这样做,但这里有一个不同的选择:不允许任务有子对象,而是使用两个对象,实现ITask接口的任务和任务集。任务将有自己的StartDate、EndDate和EstimatedHours,但任务集将从其子任务动态计算这些值。使用服务向ITask添加和删除子项。对于添加,它将在添加第一个子任务时将任务转换为任务集。对于删除,它将在删除最后一个子任务时将任务集转换回任务,并根据最后一个子任务上的值设置属性

我告诉我的ASP.NET开发人员,“事件监督。方法起作用。”

事件应该比调用方法的IFblocks多一点。无尝试/捕获等。
方法执行所有数据访问/操作/验证/计算等操作。
这也在我的开发人员中创建了“可重用代码”心态。

它将事物分开。
它还与MVC概念非常相似

控制器对事件作出反应。他们监督。他们称之为模型方法。
模特做这项工作

这不是一个完美的类比。
诚然,它过于简单化了,但它提供了相当好的指导方针

public class Task
{

    public List<Task> Children=new List<Task>();
    public Task Parent;   
    private int _duration;

    public int Duration
    {

       get
       {
          if (Children.Count>0)
          { 
              return SumChildrenDuration();
          }

          return _duration;
       }

       set 
       {
          if (children.Count>0)
              throw new Exception("Can only add to leaves");
          _duration=value;
       }
    }
}