I\'m attempting to write a java program that initializes certain layouts based on what the user selects. What I want to do is try to avoid writing a bunch of if-statements s
Something, somewhere, needs to specify the implementation. That might be a chain of if statements in source code -- but it could also be something else; e.g., a classname specified in an external data source, instantiated via reflection.
For some interfaces, there may be many more calls to the interface than instantiations. Polymorphism can reduce coupling to specific implementations.
This reduced coupling can help make code more maintainable. You can add a new implementation without modifying potentially many callers.
I'm a "little fuzzy" on your code architecture but the idea is that you only have the switch in one place rather than having multiple switches strewn throughout your code that have the same exact function signatures except the object it is operating on.
The exact implementation depends on what your goals are, but I would think that you would have one class that implements LayoutHander and has a member reference to a Layout. When the user picks his layout, the polymorphism takes hold HERE, not the level above as you have it now. That is to say, if the object that defines the different behavior is the "Layout" then you need to make Layout polymorphic, not LayoutHander.
When you think polymorhism, think code reuse. "What common set of functions do these relateded yet different objects share?"
Generally, it will be difficult to avoid some kind of conditional statement at some point to create an instance of the appropriate class.
The benefit of polymorphism comes when you have multiple if-else statements in multiple places. The polymorphism encapsulates the conditional logic for you. See this question for some other discussions on this topic.
This sort of scattered logic:
void initLayout() {
if (user choose layoutA) { initialize layoutA }
if (user choose layoutB) { initialize layoutB }
if (user choose layoutC) {initialize layoutC }
}
void refreshLayout() {
if (user choose layoutA) { refresh layoutA }
if (user choose layoutB) { refresh layoutB }
if (user choose layoutC) { refresh layoutC }
}
void cleanupLayout() {
if (user choose layoutA) { cleanup layoutA }
if (user choose layoutB) { cleanup layoutB }
if (user choose layoutC) { cleanup layoutC }
}
Gets replaced with something simpler:
layout = getLayout(user choice);
layout.initLayout();
layout.refreshLayout();
layout.cleanupLayout();
In short, yes. You'll need ifs or some mapping mechanic.
You will need some way to convert the user's input into the class you want. The ifs you have in your code will work just fine and be clear and readable. You could use a switch, too.
It is possible to avoid this, but you'll end up obfuscating your code and in the end, there will probably still be something like an if. You must define the mapping from user input to object; that cannot be circumvented. The clearest most maintainable way to do that is what you already have (though I'd put else ifs in there. :) )
The idea you have of a different class for each layout isn't bad. This is much like the concept of a traits class. Look into that, too. It will be helpful for your design.
I think using if-statements for initialization is fine. You're trying to avoid the repeated use of if-statements to "select" behavior throughout the program. Using if-statements once for initialization is fine.
To be sure, there are certainly ways to avoid even those initializer if-statements, but you have to decide what level of complexity and possible loss of readability is appropriate for your app.
For example, here are some approaches to this problem from simple to more complex:
A good example of the last method (dynamic registration) is to look at how JDBC works. JDBC drivers are registered in the app using Class.forName()
and then a specific JDBC driver is selected using a JDBC URL. Here's a typical workflow:
Potential target JDBC drivers are added classpath.
The app is configured with a list of these drivers, e.g. by listing the JDBC drivers in a property file.
The app initializes by calling Class.forName()
on each driver in the list. Each driver knows to register itself with the DriverManager when it gets loaded by Class.forName()
The app is determines what target database resource to use and resolving that to a JDBC URL, e.g. in a configuration dialog or user prompt.
The app asks the DriverManager for a connection based on the target JDBC URL. The DriverManager polls each registered driver to see if it can handle the target JDBC URL until the DriverManager finds one that works.
This would be the opposite extreme to avoid those if-statements.
No. Use a Map. When you get the choice just lookup the handler in the map and off you go.
psuedocode
Map handlers = new Map()
handlers.put(layoutA, HandlerForA)
// more handlers, possibly use dependency injection
Layout chosen = user choose layout // get a ref to the selected layout
Handler handler = handlers.get(chosen)
if (handler == null) { // No HandlerException }
handler.go()
only one if statement.