【问题标题】:What's this pattern called?这种模式叫什么?
【发布时间】:2010-07-23 14:06:58
【问题描述】:

有些我正在审查我团队代码库中的一些代码,我们在其中遍历一个分层数据结构并从中构建一个新的数据结构。没有嵌套循环——层次结构的每一层都有自己的专用功能。

所以我们有这样的代码:

public void DoA(A a, Transform transform)
{
    foreach(B b in a)
        DoB(b, transform);
}

public void DoB(B b, Transform transform)
{
    if (b != null && b.IsAvailable)
        return;

    foreach(C c in b)
        DoC(c, transform)
}

public void DoC(C c, Transform transform)
{
    var cAndAHalf = DoCAndAHalf(c.FindAll(x => x.Children > 0);

    foreach(D d in cAndAHalf)
        DoD(d, transform);
}

. . .

public void DoX(X x, Transform transform)
{
    Res res = new Res();
    if (x.Selected)
    {
        res.Selected = true;
        res.ResourceCount = 1;
    }

    transform.Add(res);
}

这样的方法有几十种,其中每个方法的长度为 3 到 5 行,名称类似,通常包含一个琐碎的 null 检查或过滤器,粗略查看代码表明没有任何方法真正被调用超过一次。出于单元测试目的,方法是公开的。

我个人觉得代码很难浏览,因为有几十个公共方法 == 几十个进入类内脏的潜在入口点。

这种编码模式有名字吗?它是反模式吗?这种风格是否比简单地将循环嵌套在单个函数中更有利?

【问题讨论】:

  • <joking>递归下降?</joking>
  • inb4:这看起来非常像(可以重构为)访问者模式。 :-D
  • 哦,因为每个方法都是public,所以很难找到启动级联函数的“根”方法。

标签: design-patterns anti-patterns


【解决方案1】:

对我来说看起来像是责任链。您只需处理部分传入请求,然后转移到链中的下一个项目。

Chain-of-responsibility

在您的情况下,它应该如下所示:

Action action = new Action;
action = action.SetNext(DoA);
action = action.SetNext(DoB);
action = action.SetNext(DoC);

A a = new A();
action.Process(a);

【讨论】:

  • +1, +answer:我与负责代码的开发人员发生了争执,并询问以他的方式构建代码的动机是什么。显然,所有方法都是 public 用于测试目的,virtual 因此它们可以被 RhinoMocks 覆盖并相对隔离地进行测试。树遍历逻辑并不完全复杂,代码的结构只是随着不同的人工作而变得失控。我认为这个建议将有助于清理代码,因此堆栈跟踪比深度更宽。谢谢你:)
【解决方案2】:

哎呀。那绝对应该有资格。但不确定它是否是反模式。我会选择反递归。

它也可能是Programming by Permutation 的标志(每次结构变深时,开发人员都会在层次结构中添加一个级别)。

【讨论】:

    【解决方案3】:

    这是我认为好的编码风格 - 具有单一职责的小方法。如果方法命名得当,这应该会产生易于理解和维护的代码。

    当然,如果许多方法都非常相似,则可以寻找一种通用模式并通过使用委托或类似的东西将其分解 - 但这实际上取决于实际代码。

    【讨论】:

    • 同意!并不是因为一个人不使用成员方法就没有好的代码风格。
    【解决方案4】:

    这有点像装饰器模式,但只是一种。

    http://en.wikipedia.org/wiki/Decorator_pattern

    查找它,看看它是否更具体地匹配您的代码库。

    【讨论】:

      【解决方案5】:

      这看起来有点像Composite pattern,只是你可以利用层次结构中父母和孩子的相似性来最小化你的编码。

      您可以通过让结构中的每个元素实现一个知道如何处理其子元素的接口来利用这一点,例如

      public interface ChildProcessor { 
         public void process()
      }
      public A implements ChildProcessor { 
        public void process() { foreach (B b )... }
      }
      public B implements ChildProcessor { 
        public void process() { foreach (C c )... }
      }
      
      public void DoA(A a, Transform transform)
      {
         A.process()... 
      }
      

      【讨论】:

        猜你喜欢
        • 2016-09-13
        • 2016-02-06
        • 1970-01-01
        • 2010-10-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多