jjzjj

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

coder 2024-04-01 原文

我有以下伪代码

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 - 重构具有大量级别之间依赖关系的内部循环的更多相关文章

  1. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  2. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  3. ruby - 树顶语法无限循环 - 2

    我脑子里浮现出一些关于一种新编程语言的想法,所以我想我会尝试实现它。一位friend建议我尝试使用Treetop(Rubygem)来创建一个解析器。Treetop的文档很少,我以前从未做过这种事情。我的解析器表现得好像有一个无限循环,但没有堆栈跟踪;事实证明很难追踪到。有人可以指出入门级解析/AST指南的方向吗?我真的需要一些列出规则、常见用法等的东西来使用像Treetop这样的工具。我的语法分析器在GitHub上,以防有人希望帮助我改进它。class{initialize=lambda(name){receiver.name=name}greet=lambda{IO.puts("He

  4. ruby-on-rails - 在 Ruby 中循环遍历多个数组 - 2

    我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代

  5. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  6. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  7. ruby-on-rails - 在 ruby​​ .gemspec 文件中,如何指定依赖项的多个版本? - 2

    我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这

  8. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  9. ruby - #之间? Cooper 的 *Beginning Ruby* 中的错误或异常 - 2

    在Cooper的书BeginningRuby中,第166页有一个我无法重现的示例。classSongincludeComparableattr_accessor:lengthdef(other)@lengthother.lengthenddefinitialize(song_name,length)@song_name=song_name@length=lengthendenda=Song.new('Rockaroundtheclock',143)b=Song.new('BohemianRhapsody',544)c=Song.new('MinuteWaltz',60)a.betwee

  10. ruby-on-rails - `a ||= b` 和 `a = b if a.nil 之间的区别? - 2

    我正在检查一个Rails项目。在ERubyHTML模板页面上,我看到了这样几行:我不明白为什么不这样写:在这种情况下,||=和ifnil?有什么区别? 最佳答案 在这种特殊情况下没有区别,但可能是出于习惯。每当我看到nil?被使用时,它几乎总是使用不当。在Ruby中,很少有东西在逻辑上是假的,只有文字false和nil是。这意味着像if(!x.nil?)这样的代码几乎总是更好地表示为if(x)除非期望x可能是文字false。我会将其切换为||=false,因为它具有相同的结果,但这在很大程度上取决于偏好。唯一的缺点是赋值会在每次运行

随机推荐