Reputation: 53
at work we do a peer review of code and I found something I don't like and I want to ask about best practice for this particular problem.
We have an interface:
public interface Item {
public String getType();
//some other methods
}
and an implementing class:
public class EmailItem implements Item {
public static final String TYPE = "email";
@Override
public String getType() {
return TYPE;
}
}
and some code that uses the classes:
for (Item item : items) {
if (EmailItem.TYPE.equals(item.getType())) {
isProcessed = Processor.process(item);
} else {
LOGGER.error("Unknown failover type received to process. Type: {}", item.getType());
}
}
At this point we have only one implementing class so checking the type is not necessary but we will add some other implementations and then it would make sense (though switch
will be used).
The main issue is that EmailItem
has variable TYPE
set as public and this variable has also a getter.
Both class and instance of that class should have access to this variable, but having it public static final
and accessing it with instance directly doesn't seem right/best practice (although it is technically possible) and when it would be private (as it should) then it won't be accessible from other classes (where for
cycle is and static would not make sense at that point).
Through discussion we come up with solution with usage of instanceOf(...)
or instance.getClass().getName()
and EmailItem.class.getName()
but none of them seem elegant to me :).
So finally, my question is what is the most elegant solution for described problem?
Heh, this is my first question here and I hope it makes sense to you ;).
Upvotes: 2
Views: 705
Reputation: 726509
The way you did it is fine, if you want to do it that way:
static final
variable TYPE
lets you treat it as a type constant,However, when you find yourself dispatching on a type represented by String
or some other value, you are usually going down a wrong path of switch
statements in object-oriented code. If you have a choice of action at this point, consider an alternative technique of double dispatch, such as implementing the Visitor Pattern:
interface ItemVisitor {
void visitEmail(EmailItem item);
void visitSms(SmsItem item);
}
interface Item {
void accept(ItemVisitor v);
}
class EmailItem implements Item {
public void accept(ItemVisitor v) { v.visitEmail(this); }
}
class SmsItem implements Item {
public void accept(ItemVisitor v) { v.visitSms(this); }
}
Now you can do this:
class Example implements ItemVisitor {
public void visitEmail(EmailItem item) {
// Do something with an e-mail
}
public void visitSms(SmsItem item) {
// Do something with an SMS
}
public static void main(String[] args) {
Example e = new Example();
for (Item item : ItemSource.getManyItems()) {
item.accept(e);
}
}
}
Upvotes: 1
Reputation: 40388
If all "types" for your Item
are known at compile time, you could use an enum like this:
public interface Item {
enum ItemType { MAIL, OTHER; }
public ItemType getType();
//some other methods
}
Upvotes: 1
Reputation: 35008
Thinking about it from an OO point of view I'd consider the following approach:
public interface Item {
public boolean process();
//some other methods
}
public class EmailItem implements Item {
@Override
public boolean process() {
// email specific implementation
}
}
public class AnotherItem implements Item {
@Override
public boolean process() {
// another implementation
}
}
for (Item item : items) {
isProcessed = item.process();
}
Upvotes: 3