The use of instanceof
or getClass()
is largely considered code smell. Is using a variable to indicate the type of object you're using also considered code smell?
Suppose if I had an enum
called WeaponType
:
public enum WeaponType {
ReloadableWeapon // -- imagine more weapon types
}
and Weapon
class:
public abstract class Weapon
{
private WeaponType wt;
public Weapon(WeaponType wt)
{
this.wt = wt;
}
}
public ReloadableWeapon extends Weapon{
public ReloadableWeapon()
super(WeaponType.ReloadableWeapon);
{
}
}
In this example, I'm using an enum
to determine the type of weapon, essentially, I'm doing with the enum
what I can do with instanceof
or getClass()
.
I can check if the weapon is a certain type and proceed, for example, suppose in my game I allow the player to view their weapons based on type in a quick view while under attack.
I can collect all the weapons like so:
List<Weapon> reloadableWeapons = new ArrayList<Weapon>();
for (Weapon weapon : inventory){
if weapon.istypeof(WeaponType.ReloadableWeapon){
reloadableWeapons.add(weapon);
}
}
// code to render reloadableWeapons to UI
Of course this doesn't have to be an enum
, it could be any variable, String
or int
but it's purpose is to indicate what type of object you have.
Notice, I'm not using the enum
to check the Weapon
and downcast, or controlling behavior. I simply want to single out weapons of a specific type and perform an action, in this case, display in a UI.
5 Answers 5
When your weapon types enum just mirrors the class hierarchy, then this is a blatant violation of the DRY principle - you encode the same information, the type of a weapon redundantly in two places - one place is the class name itself, one place the enum. That's definitely not just a code smell, that is bad code.
Introducing such a type just to avoid instanceof
formally does not make your code better - quite the opposite, you just "reinvent" instanceof
and call it istypeof
, with all the same drawbacks of the former: whenever you add a new Weapon
subclass, you have to check all places in code with an istypeof
statement, if the code is still correctly dealing with the new type. There is no difference in this as if you would use instanceof
for it, just an additional disadavantage: whenever you add a new subclass, you will also have to make sure you don't forget to extend the enum
.
The situation may be different when you use the enum differently, for example, for modeling types of weapons in a finer or coarser granularity than your class hierarchy (or if you do not use a class hierarchy at all, just a type field). But in general, I recommend trying to achieve orthogonality, your code stays more maintainable if enums do not have implicit, hidden dependencies to something like a class hierarchy, that will become error prone sooner or later.
-
In the context that I'm using it, to display weapons of a specific type in the UI, nothing more, is it still a code smell? i.e I'm not using it to control behavior of the Weapon (checking if it's reloadable, then calling
reload()
).user306112– user3061122018年06月30日 17:39:29 +00:00Commented Jun 30, 2018 at 17:39 -
2@Sveta: in that context, using
isinstanceof
would probably be ok. Violating the DRY principle is not.Doc Brown– Doc Brown2018年06月30日 17:40:57 +00:00Commented Jun 30, 2018 at 17:40 -
Bare with me here, how would I be violating the DRY principal? I know this goes beyond the scope of the question, but in the interest of learning :D. You can edit your answer rather an explain in the comments.user306112– user3061122018年06月30日 17:42:10 +00:00Commented Jun 30, 2018 at 17:42
-
@Sveta: clearer now?Doc Brown– Doc Brown2018年06月30日 20:22:38 +00:00Commented Jun 30, 2018 at 20:22
-
1@Sveta: let me add one note: using
instanceof
is not necessarily a bad thing, "code smell" just means "the code is not generic any more and will not be automatically deal with new subtypes". For example, if your UI deals specificially with "reloadable weapons", with a static field or columns explicitly for this, usinginstanceof
is ok. If, however, you want to create a generic UI, which adapts automatically to new weapon subtypes, then usinginstanceof
has a high risk of causing issues. So if it is are the first situation, don't overthink this and useinstanceof
.Doc Brown– Doc Brown2018年06月30日 20:55:20 +00:00Commented Jun 30, 2018 at 20:55
Your example is different from using getClass, you have but one generic class and you enum is just a member that tells the type of weapon (not the type of class). If your weapon/game is simple enough (your weapon just strikes) this is a valid approach.
It gets different when you start adding weapon type specific behavior to your generic weapon and you get lots of if statements and switch statements, checking your enum to get to the desired behavior.
-
In your second point, you mean if
Weapon
had a method calledreload()
, it can get difficult if I'm using theenum
/getClass
/instanceof
to check the type and call the method? Is this an indication thatWeapon
needs to be redesigned?user306112– user3061122018年06月30日 17:07:06 +00:00Commented Jun 30, 2018 at 17:07 -
@Sveta As your weapons gets more complex and get specific behavior you should turn your generic weapon class into an abstract base class and make a sub class for each weapon. Then you no longer need your enum.Martin Maat– Martin Maat2018年06月30日 17:45:43 +00:00Commented Jun 30, 2018 at 17:45
-
That's exactly what's shown in my example right now,
Weapon
isabstract
, my issue is, in places where I might have usedinstanceof
orgetClass()
to single out weapons of a specific type, is it okay to use anenum
or another other type of variable instead? In this context, I want to grab all weapons that can reload to display in the UI for the player. I'm not using theenum
to control behavior of the object.user306112– user3061122018年06月30日 18:11:42 +00:00Commented Jun 30, 2018 at 18:11 -
@Sveta I did not read your code well enough, sorry for that. In this case, a virtual property named CanReload would be more approptiate. I am not a Java guy, it may have to be a method, I don't know, you get the point.Martin Maat– Martin Maat2018年06月30日 18:28:44 +00:00Commented Jun 30, 2018 at 18:28
Like anything, it becomes a code smell when it starts to make the code harder to understand and maintain. It can be OK in small doses, or where there is some useful differentiating feature. For example, if some types of weapons can be composed of other types of weapons, but some can't, then it might make sense to have an enum, string, or method that says, "contains sub weapons" or something like that. In most cases anything you can do to a weapon, you can also do to a weapon composed of other weapons. But there are things you can't do with a non-composed weapon (such as break it down into its components) that you can do with a composed weapon.
It might be better to use polymorphism for this case. You could have a base class that does something like this, for example:
public abstract class Weapon
{
public reloadWeapon()
{
/* Do nothing in the base class */
}
};
public class ReloadableWeapon extends Weapon
{
public reloadWeapon()
{
/* Do whatever is necessary here */
}
};
For non-reloadable weapons, the base class simply returns. For reloadable weapons, it reloads them as necessary.
The "right" way to do it in OOP world is the Visitor pattern.
Otherwise I agree with Doc Brown's answer that introducing explicit variable adds other points of failure, but does not give any benefit. Particularly, it does not remove the code smell which is explicit request for the object type. So I think you should not do it. An important exception would be the case when you have to interoperate with another language, reflecting the hierarchy, in which case you may have to introduce the enum anyway.
-
1Doesn't the visitor introduce a lot of code for the simple task for displaying all weapons of a specific type? I don't want to access the method of a specific type, I just want to display them in the UI, not control behavior of the weaponsuser306112– user3061122018年06月30日 18:42:41 +00:00Commented Jun 30, 2018 at 18:42
-
it does. There are arguments out there in internet. I am not quite convinced myself. I just pointed out the popular answer to the problemmax630– max6302018年07月01日 14:39:14 +00:00Commented Jul 1, 2018 at 14:39
This is pretty much the same as checking the class, and has the same problems.
What will you do when you add more classes of weapons? Say you have Weapon
, ReloadableWeapon
(guns), MeleeWeapon
(swords), ConsumableWeapon
(bombs) and PlaceableWeapon
(laser turrets).
If you go with an enum-based or class-based approach to distinguishing them, you will find yourself adding endless combination classes, and then forgetting to check them. You may wish to add cattle prods as a new class ReloadableMeleeWeapon
, but then forget to check for ReloadableMeleeWeapon
in the reloading code. Or you add it to the reloading code, but then forget to update the code that spawns weapons so that they start full of ammo, so that reloadable weapons start full of ammo, but reloadable melee weapons don't.
-
I agree with this answer. My method is just a poor way to avoid
instanceof
. Given my scenario, is the use ofinstanceof
okay? What should I do when I need the class to tell me what it is? Remember, I'm not downcasting to access a method, I simply want to display all the weapons that are the same type in a UI. For example, imagine a quick view when you press a shortcut that brings up all your reloadable weapons while you're under attack, I need the game to distinguish between Reloadables and non reloadables and display them.user306112– user3061122018年07月01日 15:33:45 +00:00Commented Jul 1, 2018 at 15:33 -
So why not call
isReloadable()
?Stack Exchange Broke The Law– Stack Exchange Broke The Law2018年07月01日 23:57:33 +00:00Commented Jul 1, 2018 at 23:57 -
1I could, or I could use the approach mentioned in the comments above and have a list of attributes for each weapon, so a ReloadableWeapon will obviously have Reloadable as an attribute, as well as portable, etc. I like the
list
approach, it's flexiable makes the code more maintainable.user306112– user3061122018年07月02日 04:48:19 +00:00Commented Jul 2, 2018 at 4:48
ReloadableWeapon
should not be a type at all. Your baseWeapon
class should have anisReloadable
flag, default tofalse
... Likewise for every attribute you'd want to filter weapons on... I won't actually make that argument. It's an argument I would flesh out pretty thoroughly if this were my design though.Weapon
should worry if it's reloadable or not. What about other types? This meansWeapon
is going to change every time i add a new type?