Indirection (using one thing to represent another, in particular using a letter or variable to represent a value) was originally used in algebra. As in x = 2. The “x” variable’s value is 2. This may be what initially confuses math students. “Wait a minute– this x thing is 2. Why don’t you just say 2 instead of making it complicated?” Yeah.
Simple algebra demonstrates the value of indirection in certain contexts. Programming also uses indirection quite a bit. The problem is that excessive or inopportune use of indirection can make certain problems very difficult to troubleshoot. Unfortunately, the tendency in modern programming is to use more, not less indirection.
Years ago I wrote a “service constructor” which would maintain a list of include file names and instantiated classes. The service constructor was used any time someone wanted to instantiate a class object. I used this extensively in a whole raft of code I wrote for a very extensive suite of applications. It made the initial coding of various programs simpler. The problem came when I went back and had to debug that code. This indirection made it very difficult to track certain things down. Ultimately, it writing new code for other applications, I abandoned this service constructor and opted to simply write a line which included the class declaration/definition, and another which instantiated the object at the point where I needed it.
One other way I’ve used a sort of “indirection” is in using a page controller for two different functions.
For example, in “stage 1”, the controller would simply present data from the database and fields to provide more. Then the view would return to that controller with the user’s data. The controller would have to “sense” that this was the second time through, and now process the data returned. This is fairly common practice, and I normally recommend it. But in several cases of old code, it became very difficult to determine the path the code was taking and where the exact errors were happening. These were cases where a lot of processing was taking place before the initial view. And then a lot of processing was taking place once the user had supplied additional values.
I’ve become a critic of indirection over the years, after having to debug enough of own code to discover that indirection complicates troubleshooting immensely. In the case of the usage detailed in the prior paragraph, I generally still champion having a view return to the controller which called it. But if this becomes at all complicated, or requires a large amount of processing, my advice would be to add an additional controller and/or view to the chain, specifically for processing the data from the last view. I’ve found this makes things vastly easier in a lot of cases. And these days, if I have any doubt, I just automatically create a new controller and view.
None of this applies to the silly proliferation of useless “best practice” controllers I’ve seen in some professional code. All this adds new levels complexity, just to satisfy academics and readers of Datamation magazine. My advice? Flatten out your code and do everything the “hard” way first time through. If you want to make things more complex on the next pass, you can. But I’d advise against it.