属性初始化反模式

时间:2013-01-15 00:27:09

标签: c# design-patterns initialization anti-patterns

我一次又一次地沿着这些行编写代码,在那里我创建了一些对象,然后循环遍历它们以使用另一个类初始化一些属性......

ThingRepository thingRepos      = new ThingRepository();
GizmoProcessor  gizmoProcessor  = new GizmoProcessor();
WidgetProcessor widgetProcessor = new WidgetProcessor();

public List<Thing> GetThings(DateTime date)
{
    List<Thing> allThings = thingRepos.FetchThings();

    // Loops through setting thing.Gizmo to a new Gizmo
    gizmoProcessor.AddGizmosToThings(allThings);

    // Loops through setting thing.Widget to a new Widget
    widgetProcessor.AddWidgetsToThings(allThings);

    return allThings;
}

......这只是,感觉不对。

  1. 这是个坏主意吗?
  2. 我在这里使用的是反模式的名称吗?
  3. 有哪些替代方案?
  4. <小时/> 修改:假设GizmoProcessorWidgetProcessor都必须关闭并进行一些计算,并从其他表中获取一些额外的数据。它们不仅仅是存储在存储库中的数据。他们根据每个Thing创建新的Gizmo和Widgets,并将它们分配给Thing的属性。

    这对我来说很奇怪的原因是Thing不是一个自治对象;它无法创建自己和子对象。它需要更高级的代码来创建完全完成的对象。我不确定这是不是坏事!

4 个答案:

答案 0 :(得分:1)

ThingRepository应该是获取Thing集合的单一访问点,或者至少是开发人员直观看待的集合。出于这个原因,GetThings(DateTime date)应由另一个对象提供,这感觉很奇怪。我宁愿把这个方法放在ThingRepository本身。

Thing返回的GetThings(DateTime date)不同,ThingRepository.FetchThings()返回的动物比“{1}}返回的动物更胖”也感觉很尴尬和反直觉。如果GizmoWidget确实是Thing实体的一部分,那么每当您拥有Thing实例时,您应该能够访问它们,而不仅仅是针对返回的实例GetThings(DateTime date)

如果GetThings()中的日期参数不重要或可以在其他时间收集,我会使用Thing上的计算属性来实现对Gizmo和{的按需访问{1}}:

Widget

请注意,只要执行的计算成本太高,此方法就有效。不推荐使用昂贵处理的计算属性 - 请参阅http://msdn.microsoft.com/en-us/library/bzwdh01d%28VS.71%29.aspx#cpconpropertyusageguidelinesanchor1

但是,这些计算不必在getter中内联实现 - 它们可以委托给第三方Gizmo / Widget处理器,可能还有缓存策略等。

答案 1 :(得分:1)

如果您有复杂的初始化,那么您可以使用策略模式。以下是根据this strategy pattern overview

改编的快速概述

创建一个策略界面来抽象初始化

public interface IThingInitializationStrategy
{
  void Initialize(Thing thing);
} 

策略可以使用的初始化实现

public class GizmosInitialization
{
  public void Initialize(Thing thing)
  {
     // Add gizmos here and other initialization
  }
}

public class WidgetsInitialization
{
  public void Initialize(Thing thing)
  {
    // Add widgets here and other initialization
  }
}

最后是一个以抽象方式接受策略实现的服务类

internal class ThingInitalizationService
{
  private readonly IThingInitializationStrategy _initStrategy;

  public ThingInitalizationService(IThingInitializationStrategy initStrategy)
  {
     _initStrategy = initStrategy;
  }

  public Initialize(Thing thing)
  {
    _initStrategy.Initialize(thing);
  }
}

然后您可以使用像这样的初始化策略

var initializationStrategy = new GizmosInitializtion();
var initializationService = new ThingInitalizationService(initializationStrategy);


List<Thing> allThings = thingRepos.FetchThings();

allThings.Foreach ( thing => initializationService.Initialize(thing) );

答案 2 :(得分:0)

唯一真正的潜在问题是你在同一个循环中多次迭代,但如果你需要点击一个数据库来获取所有小玩意和小部件,那么批量请求它们可能会更有效率您的Add ...方法的完整列表是有意义的。

另一个选择是在第一个存储库调用中调用返回gizmo和小部件(假设它们位于同一个repo中)。它可能会使查询更复杂,但它可能会更有效。当然,除非您在获取东西时总是不需要获取小玩意和小部件。

答案 3 :(得分:0)

回答你的问题:

  1. 这是个坏主意吗?

    • 根据我的经验,在你需要改变之前,你很少知道这是好事还是坏事。
    • IMO,代码是:过度设计设计不足不可读
    • 与此同时,你会尽力而为,坚持最佳实践(KISS,单一责任等)
    • 就个人而言,我认为处理器类不应该修改任何Thing的状态。
    • 我也不认为处理器类应该被赋予一组要修改的东西。
  2. 我在这里使用的是反模式的名称吗?

    • 抱歉,无法提供帮助。
  3. 有哪些替代方案?

  4. 就个人而言,我会编写代码:

    public List<Thing> GetThings(DateTime date)
    {
        List<Thing> allThings = thingRepos.FetchThings();
    
        // Build the gizmo and widget for each thing
        foreach (var thing in allThings)
        {
            thing.Gizmo = gizmoProcessor.BuildGizmo(thing);
            thing.Widget = widgetProcessor.BuildWidget(thing);
        }
    
        return allThings;
    }
    

    我的理由是:

    1. 代码位于“获取内容”的类中。从逻辑上讲,我认为它可以遍历每个Thing对象并初始化它们。
    2. 目的很明确:我在返回之前初始化每个Thing的属性。
    3. 我更喜欢在中心位置初始化Thing的任何属性。
    4. 我不认为gizmoProcessor和widgetProcessor类应该与任何事物集合有任何业务
    5. 我更喜欢处理器有一个方法来构建和返回单个小部件/ gizmo
    6. 但是,如果您的处理器类一次构建多个属性,那么我只会将属性初始化重构为每个处理器。

      public List<Thing> GetThings(DateTime date)
      {
          List<Thing> allThings = thingRepos.FetchThings();
      
          // Build the gizmo and widget for each thing
          foreach (var thing in allThings)
          {
              // [Edited]
              // Notice a trend here: The common Initialize(Thing) interface
              // Could probably be refactored into some 
              // super-mega-complex Composite Builder-esque class should you ever want to
              gizmoProcessor.Initialize(thing);
              widgetProcessor.Initialize(thing);
          }
      
          return allThings;
      }
      

      P.S:

      • 我个人对(反)模式名称并不在意。
      • 虽然有助于在更高的抽象层次上讨论问题,但我不会将每个(反)模式名称都提交给内存。
      • 当我遇到一个我认为有用的模式时,我才会记住它。
      • 我很懒,我的理由是:如果我只想使用少数人,为什么要记住每一种模式和反模式呢?

      [编辑]

      注意到已经就使用战略服务给出了答案。