简体   繁体   中英

Public static final variable with getter

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 ;).

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();
}

The way you did it is fine, if you want to do it that way:

  • The static final variable TYPE lets you treat it as a type constant,
  • The implementation on the instance lets you check the constant against the return value on the interface.

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);
        }
    }
}

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
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM