属性初始化反模式

本文关键字:模式 初始化 属性 | 更新日期: 2023-09-27 17:52:58

现在我结束了代码沿着这些行,在那里我创建一些对象,然后循环通过它们来初始化一些属性使用另一个类…

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. 有哪些替代方案?


编辑:假设GizmoProcessorWidgetProcessor都必须离开并做一些计算,并从其他表中获得一些额外的数据。它们不仅仅是存储在存储库中的数据。他们正在基于每个Thing创建新的Gizmos和Widgets,并将它们分配给Thing的属性。

我觉得奇怪的原因是Thing不是一个自主对象;它不能创建自己和子对象。它需要更高层的代码来创建一个完全完成的对象。我不确定这是不是件坏事!

属性初始化反模式

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

事实上,GetThings(DateTime date)返回的ThingThingRepository.FetchThings()返回的动物是不同的,"更胖"的动物,这也让人感到尴尬和反直觉。如果GizmoWidget确实是Thing实体的一部分,那么每次有Thing的实例时都应该能够访问它们,而不仅仅是GetThings(DateTime date)返回的实例。

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

public class Thing
{
  //...
  public Gizmo Gizmo
  {
    get 
    { 
       // calculations here 
    }
  }
  public Widget Widget
  {
    get 
    { 
       // calculations here 
    }
  }
}

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

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

如果你有复杂的初始化,那么你可以使用策略模式。以下是根据该策略模式概述改编的快速概述

创建一个策略接口来抽象初始化

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) );

唯一真正的潜在问题是你在同一循环中迭代多次,但如果你需要点击数据库以获得所有的小部件和小部件,那么批量请求它们可能更有效,因此将完整列表传递给你的Add…方法是有意义的。

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

回答您的问题:

  1. 这是个坏主意吗?

    • 从我的经验来看,你很少知道这是一个好主意或坏主意,直到你需要改变它。
    • IMO,代码要么是:设计过度设计不足,或者不可读
    • 与此同时,你尽你所能,坚持最佳实践(KISS,单一责任等)
    • 我个人认为处理器类不应该修改任何事物的状态。
  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类应该与Collection of Things有任何业务
  5. 我更喜欢处理器有一个方法来构建和返回一个小部件/小玩意

然而,如果你的处理器类在一次构建几个属性,那么我只会重构每个处理器的属性初始化。

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.
  • 我个人不太关心(反)模式名称。
  • 虽然它有助于在更高的抽象层次上讨论问题,但我不会将每个(反)模式名称都提交到内存中。
  • 当我遇到一个我认为有帮助的模式时,我只会记住它。
  • 我很懒,我的理由是:如果我只使用少数模式,为什么要记住每个模式和反模式呢?
[编辑]

注意到已经给出了关于使用策略服务的答案。