In subsystem design, I sometimes see software designs that have one high-level class that has only one feature: It routes a call from a client using the class to another a certain class the client would like to use. However, it alone does not have any functionality. Take this scenario:
Say there are five classes in the bowling alley subsystem: An alley, a lane, a bowler, control desk, and a score. Anytime a client outside the subsystem wants any data to display to a user, it would communicate only to the control desk (the router) that would call any of the classes it holds to get the client's requested data (a score for example: Client calls control desk with getScore(), which calls a Lane's getScore(), which calls a Bowler's getScore()).
I understand this is a bad design decision, but I'd like to hear real-world examples with consequences you discovered of having this router class (Can also be known as a "middleman"). What issues did you run into as the system you were working on evolved? What arguments would you make to persuade software designers to avoid router classes?
I'd argue that in some designs a router is the preferred design pattern, such as in MVC frameworks to delegate handlers for URLs. In that situation it's really helpful because it provides a very clean separation between what the client "sees" and the actual logic behind it.
Anytime a client outside the subsystem wants any data to display to a user, it would communicate only to the control desk (the router) that would call any of the classes it holds to get the client's requested data
this sounds like the Facade pattern
As for the middleman, in the following example, wouldnt the Lane be the culprit?
a score for example: Client calls control desk with getScore(), which calls a Lane's getScore(), which calls a Bowler's getScore())
simplifying the interface to a subsystem for the benefit of clients outside the subsystem could be considered good design.
The Facade pattern, and the Mediator pattern perform similar tasks to what you are describing. Your use of the Middleman moniker implies the Mediator pattern over the Facade pattern, as a Middleman is responsible for negotiating between two entities with neither entity needing to know the specifics of how to communicate with the other.
You can use either of these patterns to reduce coupling for the client class, which needs to use the system the Mediator or Facade is masking. In the case of the Facade pattern, the intention is to provide a convenient way to interface a system of classes. For the Mediator pattern, the purpose is to abstract the steps required to perform a complex task from the client.
I don't know that routing method calls is always such a bad idea.
It seems that you'd just have the problem associated with any additional layer of abstraction - that the abstraction can break, or that it's one more thing that can potentially misbehave if there's a change made to something underlying.
I've never seen anything that called more than a few layers deep, but I just imagine that adding extra calls would make it more difficult to trace the path information takes, and make troubleshooting more difficult.
One potential problem, though, is if each layer implements its own error handling or retry process, making something that's insignificant at each level overwhelming as a whole. For example, if the Lane makes two attempts to check the bowler's score, and the desk makes 3 attempts to check the score, then a failure of the bowler to return a score will result in 6 queries being made. Add a 30 second timeout at the bowler, and you're suddenly waiting for 3 minutes for what should take 30 seconds.
OldNewThing had an article about an example of this in the Windows OS, and the problems it caused, but now I can't seem to find it.
I think that both ASP.NET MVC and MVP patterns utilize this type of concept where you end up with something simply handling the logic that is executed from one end to the other or on behalf of a lower layer to a higher layer. This certainly makes testing more easy to perform so that in and of itself is a MAJOR benefit. This type of pattern does create some manual or tedeious work in that you could click a button and have it do a task rather than click the button, have something intercept that click, then call into some service managing class that does some work. But on the front of keeping your code clean and readable the more separation there is often times the better.
If you are not a tester or could care less about patterns directly then think of it in another format. You have a link that takes a user to a page. This link is scattered across your site all over the place as the destination is very important or used a lot. The destination changes. This could be a find and replace operation...or you could insert a RedirectService (call it what you will) that when someone clicks a link takes change and directs the clicker to the right location. This allows the location to be defined once in one location and therefore changed once. Find and replace often times changes things that weren't meant too be changed!
No matter how you look at this...separation of concerns is a good think. The UI is one concern. The controller of activities is another concern. The activity itself is yet another concern!