views:

240

answers:

6

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?

A: 

I don't know that routing method calls is always such a bad idea.

Spencer Ruport
Routers violate the principle of design: High Cohesion because you are not directly talking to your "friend," you are talking through something (or maybe even more than one thing). (Think of this scenario as that you can only get to a certain set of data to use through that high-level interface which only routes).
IDreamOf362
+1  A: 

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.

akf
Not necessarily. See the bad code smell: Middleman. Also see an excerpt from my notes from design patterns class:"The first misuse comes from a simplistic application of the pattern as merely a "go-between" sitting between the application and a subsystem. Most behaviors in the Facade are simply pass-through operations that call a single operation in the subsystem. This is not a higher-level interface than the subsystem provides, and does not really make it any easier to use. If there is not higher-level interface, one aspect of the Facade's intention is missing."
IDreamOf362
A: 

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.

dbb
See the comment above about the "high cohesion" violation. If I'm the LaneView for example, a GUI for the Lane, why I am talking to the control desk to get to LaneView data? That does not make sense, as I am displaying Lane data. (Perhaps this will clarify the trap I am talking about in software design).
IDreamOf362
Should be Lane data in the second sentence, not LaneView data.
IDreamOf362
I agree, in most situations this kind of pattern doesn't make sense. But in the case of an MVC it makes perfect sense because at some point something has to delegate what handles the URL. So really the only two designs that really make sense is one giant monolithic piece of code that handles ALL requests or a small "router" that is configured to pass requests on to other segments of code based on the URL.This level of indirection also allows you to "hot swap" functionality while providing a consistent interface to your users.
dbb
A: 

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.

rwmnau
A: 

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!

Andrew Siemer
See the subsystem example with the LaneView talking to the Control Desk to get Lane data instead of the LaneView talking to the Lane to get Lane data.I agree that abstraction of a high-level interface is a good choice for a subsystem if the high-level interface simplifies the functions of the subsystem. A simple routing call (literally meaning one function call in the high-level interface) provides no abstraction benefit and misses an aspect of the intention of the facade pattern.
IDreamOf362
A: 

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.

hasalottajava