Reputation: 53
I have the following classes in my program:
public abstract class Question {
private Topic topic;
private String text;
// methods
}
public class OpenQuestion extends Question {
// methods
}
public class MultipleChoiceQuestion extends Question {
private List<String> options = new ArrayList<String>();
private String correct;
// methods
}
Also, there is another class Test where I have:
Question question;
// if the question is open
if(question instanceof OpenQuestion) {
...
}
// if the question is multiple choice
if(question instanceof MultipleChoiceQuestion) {
...
}
I'm trying to find an alternative to instanceof
, because I've been told that it breaks an OOP Principle.
Is there any other better way to know if a question is open or multiple choice?
Upvotes: 0
Views: 743
Reputation: 62769
It isn't instanceof itself that breaks OOP, it's knowing what specific class your class is an instance of that breaks it.
If your class is "Animal" which can be dog, cat or bird... Animal.legs() SHOULD be sufficient to count the legs without knowing the type of animal--this is the easy case of course. The harder case is when some animals have methods that don't make sense to others.
Take the case fly() which doesn't apply to dog or cat. In this case there are a few solutions and strangely in THIS case instanceof may be the best one!
You can: implemet fly() on bird only and reflect to see if bird has a fly() method. Implement canFly() and fly() on every class, have fly() throw an exception if it can't.
Neither of these are good--not OO at all, ugly and repetitive for no reason (The first isn't bad if you have duck-typing available, but it's not the most obvious solution even then)
So what's a good solution? Have a "Flies" interface that declairs a fly() method and only "Bird" implements Flies. In this case, instanceof is a pretty good solution--possibly the best!
bad: if(animal instancef Bird) ((Bird)animal).fly()
good: if(animal instanceof Flies) ((Flies)animal).fly()
It's a pretty subtle difference until you see why--what happens when you add Bat? In the first one you have:
if(animal instancef Bird)
((Bird)animal).fly()
else if(animal instancef Bat)
((Bat)animal).fly()
This is a switch, construct and any time you find yourself coding a switch construct in an OO language, you are probably doing something wrong (which is the reason they say instanceof is wrong, because it leads to switch constructs)
The second example still works unchanged as long as Bat implements Flies and will continue to for any new objects you ever implement. This is awesomeness.
Using interfaces this way has the advantage over duck typing and reflection--because it's specific and deliberate. You won't accidentally call .fly() on a Pants object and get back a Zipper you didn't expect, and you can examine the code of "bat" and easily understand the intent.
Upvotes: 0
Reputation: 6805
The instanceof
keyword itself does not break OOP principle. They probably meant that you should use polymorphism to execute logic.
Instead of:
if(question instanceof OpenQuestion) {
...
}
//Countless ifs
if(question instanceof MultipleChoiceQuestion) {
...
}
You should
question.doAction();
Another example (more explicit):
abstract class Animal{
public abstract void speak();
}
class Dog extends Animal{
public void speak(){ System.out.println("Bark!"); }
}
class Cat extends Animal{
public void speak(){ System.out.println("Meow!"); }
}
Upvotes: 4
Reputation: 3942
If you interested in an OO design, then think if you can do something like this: Let the Question class have a method that would be called instead of the code that you have inside the if statement
public abstract class Question {
private Topic topic;
private String text;
// methods
public void abstract doSomething(Object arg);
}
public class OpenQuestion extends Question {
@Override
public void doSomething(Object arg) {
// do something with an open question
}
// other methods
}
public class MultipleChoiceQuestion extends Question {
private List<String> options = new ArrayList<String>();
private String correct;
@Override
public void doSomething(Object arg) {
// do something with an open question
}
// other methods
}
And the code in the other class would be:
Question question;
question.doSomething(arg);
The parameter in do something is there in order to pass any information that the Question object doesn't know, but you have it when you want to do something.
Upvotes: 0
Reputation: 76
Ofcourse you can see this example:
import java.lang.reflect.*;
import java.awt.*;
class SampleName {
public static void main(String[] args) {
Button b = new Button();
printName(b);
}
static void printName(Object o) {
Class c = o.getClass();
String s = c.getName();
System.out.println(s);
}
}
Upvotes: 0