问题
I am working on an implementation of the card game UNO (mostly to run simulations to test some house rules, but that's a different story).
For those who've never played it, it's similar to Crazy Eights. Players take turns playing a card on the discard pile. The card has to match in number or color. There are also draw cards, which force the next player to draw either two or four cards. It's also quite friendly to house rules, which makes it pretty interesting.
I have a Card
class with the value
and color
of the card. What I would like to do is to create DrawCard
which extends Card
. Then, in determining the game mechanics, I would use instanceof
to test whether I have a DrawCard
.
I know that instanceof
is often problematic, but this seems to be okay to me. Usually the cards are processed the same, and only a few special cards are treated differently, and only in specialized circumstances specific to the card type (That seems like a slippery-slope thing to say though...)
I could just use markers in the Card
class (actually, each type of card already has its own 'value'), but I am extending Card
anyway in order to have some methods that other types of cards may have (it's not just a means of identification). Using instanceof
seems more general to me, as I wouldn't need to know about which values of value
require this special behaviour, and it would be easy, for example, to add a Draw 8 card to the current contents of Draw 2 and Draw 4. (Saying this makes me wonder if I could use some sort of nested enum or something, but I don't know)
I know that both solutions would work (make the program run). To me using instanceof
feels nicer, but I don't have the experience to understand whether it is okay. Is this bad design?
回答1:
The short answer: Not really acceptable, so yes - it's a sign of a design deficiency. instanceof
is almost always a smell except in rare cases where a library is trying to do some kind of assistance that is meta or otherwise ugly but helps overcome a language/environment shortcoming.
With that opinion, the question is sort of a duplicate. See the answer to:
When is it acceptable to use instanceof?
It suggests to use the visitor pattern. This may be true if you genuinely have two different types in the system.
You also don't have to use the visitor pattern 'explicitly' but rather incorporate it in to your program flow. So consider an interface card:
interface Card {
void pick(Player player);
}
class DrawCard implements Card {
void pick(Player player) {
player.draw(value); // assume value passed in e.g. ctor
}
}
回答2:
I would say in general it's best to avoid instanceof
wherever possible.
While it's somewhat vague since you didn't post any actual code, one option in this type of situation would be to implement two different interfaces, say ICard
and IDrawCard
and have the Card
class implement the ICard
interface and the DrawCard
class implement both interfaces.
Then you would be able to differentiate the DrawCard
class by virtue of its interface in function signatures, etc. (that is, refer to it as an object of type IDrawCard
).
Edit in response to comment from OP:
In terms of handling the action after the card is played, you can leverage the interfaces to override the function that handles the actions. Since that's the context for this, I would probably recommend three different interfaces:
ICommonCard
ICard
IDrawCard
Where ICommonCard
is the generic interface for when you need that which perhaps handles has some general-purpose methods, and the other two interfaces handle the needs of those two specific types.
Then say you have an action handler method named handleAction
, which could be defined thusly:
void handleAction(ICard card)
{
// do ICard stuff here
return;
}
void handleAction(IDrawCard card)
{
// do IDrawCard stuff here
return;
}
And then when you call that function, you can pass off an object which is the type of the interface into the overloaded function (one option would be to have a member variable for each type, and if it's going to be one or the other, pass the one that isn't null
into the function).
来源:https://stackoverflow.com/questions/24709566/is-this-an-acceptable-use-of-instanceof