java - 重构具有大量级别之间依赖关系的内部循环

标签 java c# for-loop nested convention

我有以下伪代码

using (some web service/disposable object)
{
    list1 = service.get1();

    list2 = service.get2();

    for (item2 in list2)
    {
        list3 = service.get3(depending on item2);

        for (item3 in list3)
        {
            list4 = service.get4(depending on item3 and list1);

            for (item4 in list4)
            {
                ...
            }
        }
    }
}

整个代码有 500 行,其中包含 for 语句中的大量逻辑。问题是将其重构为可读和可维护的代码,并作为类似情况的最佳实践。以下是我目前找到的可能解决方案。

1:拆分成方法 考虑到我们将每个 for 提取到它自己的方法中,以提高可读性,我们最终得到 method2、method3 和 method4。每个方法都有自己的参数和依赖关系,这很好,除了 method4。 Method4 依赖于 list1,这意味着 list1 也必须传递给方法 2 和 3。在我看来,这变得不可读了。任何查看 method2 的开发人员都会意识到其中的 list1 没有意义,因此他必须向下查看链,直到 method4 才能真正意识到依赖 -> 低效。那么,如果 list4 或 item4 发生变化并且不再需要依赖 list1 会发生什么?我必须删除方法 4 的 list1 参数(当然应该这样做),但也必须删除方法 2 和 3 的参数(超出更改范围)-> 再次效率低下。另一个副作用是,在很多依赖和多层次的情况下,传递的参数数量会迅速增加。想想如果 list4 还依赖于 list11、list12 和 list13,它们都是在 list1 级别创建的,会发生什么情况。

2:保持长单方法 优点是每个列表和项目都可以访问每个父列表和项目,这使得进一步的更改成为一行。如果 list4 不再依赖于 list1,只需删除/替换代码,而无需更改任何其他内容。显然,问题在于该方法是几百行,我们都知道这不好。

<强>3。两全其美? 这个想法是只将每个 for 的内部逻辑部分拆分为方法。这样 main 方法将减少,我们获得可读性和可能的​​可维护性。将 for 留在 main 方法中,我们仍然能够将每个依赖项作为一行来访问。我们最终得到这样的结果:

for (item2 in list2)
{
    compute();

    list3 = service.get3(depending on item2);

    for (item3 in list3)
    {
        compute(item2, eventually item1)

        list4 = service.get4(depending on item3 and list1);

        for (item4 in list4)
        {
            ...
        }
    }
}

但是还有一个问题。您如何命名这些compute 方法以便于阅读?假设我有一个局部变量 instanceA 或类型 ClassA,我从 item2 填充它。 A 类包含一个名为 LastItem4 的属性,比方说,它需要按照创建日期或其他顺序保留最后一个 item4。如果我使用计算来创建 instanceA,那么这个计算只是部分的。因为 LastItem4 属性需要在不同的计算方法中在 item4 级别填充。那么我如何调用这两种计算方法来建议我实际在做什么呢?在我看来,这是一个艰难的答案。

<强>4。 #地区 保留一个长方法但使用区域。这是一些编程语言中严格使用的功能,在我看来就像一个补丁,而不是最佳实践,但也许只有我一个人喜欢。

我想知道您将如何进行重构?请注意,它可能比这更复杂。这是一个服务的事实有点误导,这个想法是我真的不喜欢使用一次性对象作为方法参数,因为如果不阅读实际的方法代码你就不知道意图。但我在评论中期待这一点,因为其他人可能会有不同的看法。

为了举例,我们假设该服务是第 3 方服务,无法更改其工作方式。

最佳答案

您似乎在寻找一般答案而不是特定答案。一般答案的关键是分离职责并努力实现单一职责原则

让我从您的方法做太多事情开始。理想情况下,一个类(class)也不应该做这么多事情。它应该在类组的帮助下完成。

Robert C.Martin 曾经说过“类往往隐藏在方法中”,这里就是这种情况。如果你的方法很大,你可以将它重构为更小的方法,但如果整个方法都需要一个局部变量,那么该方法可能应该放在它自己的类中。

如果您需要同时访问某些字段,则它们必须在一起(我的意思是在同一类型中)。所以你所有的 List1、List2、List3、List4 都应该放在一个类中,而不是作为方法参数。

鉴于您无法修改 Service 类(假设它是第三方 API)。没关系,您始终可以创建自己的类来包装上述服务。

所以你本质上是一个类似下面的类:

public class ServiceWrapper
{
    private IList<string> list1;
    private IList<string> list2;
    private IList<string> list3;
    private IList<string> list4;

    private readonly Service1 service;
    public ServiceWrapper(Service1 service)
    {
        this.service = service;
    }

    public SomeClass GetSomething()
    {
        list1 = service.Get1();
        list2 = service.Get2();

        foreach (var item2 in list2)
        {
            PopulateMore(item2);
        }

        return result;//Return some computed result
    }

    private void PopulateMore(string item2)
    {
        list3 = service.Get3(item2);

        foreach (var item3 in list3)
        {
            PopulateEvenMore(item3);
        }
    }

    private void PopulateEvenMore(string item3)
    {
        list4 = service.Get4(item3);

        foreach (var item4 in list4)
        {
            //And so on
        }
    }
}

鉴于您的服务看起来像这样

public class Service1 : IDisposable
{
    public void Dispose()
    {
        throw new NotImplementedException();
    }

    public IList<string> Get1()
    {
        throw new NotImplementedException();
    }

    public IList<string> Get2()
    {
        throw new NotImplementedException();
    }

    public IList<string> Get3(string item2)
    {
        throw new NotImplementedException();
    }

    public IList<string> Get4(string item3)
    {
        throw new NotImplementedException();
    }
}

所以你很长的方法现在变得很小了。

public void YourVeryLongMethodBecomesVerySmall()
{
    using (Service1 service = new Service1())
    {
        SomeClass result = new ServiceWrapper(service).GetSomething();
    }
}

正如您在评论中所说,如果您在这些方法中还有一些其他职责,则需要将这些职责分开并将其移至自己的类中。例如,如果你想解析一些东西,那应该放在一些 Parser 类中,而 ServiceWrapper 应该使用解析器来完成工作(理想情况下有一些抽象和松耦合) .

你应该尽可能多地提取方法和类,这样提取就没有意义了。

关于java - 重构具有大量级别之间依赖关系的内部循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28476845/

相关文章:

java - 当节点也有内部节点时,有没有办法映射该节点的值?

c# - FindAll 与 Where

c# - Foreach MenuStrip 中的每个子项

c - 尝试理解冒泡排序函数

java - 最大化内部框架

java - API 29 java android 中不推荐使用 getBitmap

java - 如何在另一个类中使用在循环内部定义的字符串?

javascript - "for await of"的控制流程是什么?

java - 至强比i5慢?

c# #region/#endregion 不匹配