I have the following preudo-code
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) { ... } } } }
where all the code has, say, 500 lines with a lot of logic inside the for statements. The problem is that refactoring is readable and supported by the code and as best practice for such situations. Here are the possible solutions that I have found so far.
1: Divide into methods Given that we extract each for into our own method to improve readability, we get method2, method3 and method4. Each method has its own parameters and dependencies, which is good, except for method4. Method4 depends on list1, which means that list1 must also be passed to methods 2 and 3. In my opinion, this becomes unreadable. Any developer looking at method2 will understand that it makes no sense to list1, so he should look down the chain until method4 can actually implement the dependency β inefficiently. Then, what happens if the variable list4 or item4 changes and no longer needs to depend on list1? I need to remove the list1 parameter for method4 (which should be done, of course), but also for methods 2 and 3 (outside the range of changes) -> again inefficient. Another side effect is that in the case of many dependencies and several levels, the number of transmitted parameters will increase rapidly. Think about what happens if list4 also depends on list11, list12, and list13, all created at list1 level.
2: Keep a long single method The advantage is that each list and item can access each parent list and item, which makes further changes one-line. If list4 is no longer dependent on list1, just remove / replace the code without changing anything. Obviously, the problem is that the method is a couple of hundred lines, which we all know is not good.
3. The best of both worlds? The idea is to divide into methods only the internal logical part of each for . Thus, the main method will decrease, and we will get readability and, possibly, maintainability. Leaving for in the main method, we can still access each dependency as a single layer. The result is something like this:
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) { ... } } }
But there is another problem. What do you call these compute methods for reading? Let's say that I have a local variable instanceA or type ClassA , which I populate from item2. Class A contains the LastItem4 property, which must be saved, for example, by the last item4 in the order the date was created or something else. If I use compute to create instanceA , then this calculation will only be partial. Since the LastItem4 property needs to be filled at the item4 level, in another calculation method. So, how can I name these 2 calculation methods in order to suggest what I'm actually doing? The tough answer, in my opinion.
4. #region Leave one long method, but use regions. This is a feature strictly used in some programming languages, and it seems to me that this is a patch, and not as a best practice, but maybe it's just me.
I would like to know how would you do this refactoring? Keep in mind that this can be even more complicated. The fact that this is a service is a little misleading, the idea is that I don't really like using disposable objects as method parameters, because you don't know the intent without reading the actual method code. But I expected this in the comments, as others may feel different.
For example, suppose the service is third-party, without the ability to change the way it works.