3

I have been experimenting lately with enums, and I found out that in Java they can do much more than simply representing a fixed set of constants.

Now, I am thinking about creating a new enum for my Logger class, to hold the different log severity levels (info, warn, error, etc), but I am wondering why shouldn't I take it a step forward and make my enum do the actual logging itself and avoid the Logging class altogether? (thanks to the framework I am using, the code that does the logging is ratter simple).

  1. On the other hand, and generally speaking, isn't this a violation of the SRP? In general, shouldn't an enum serve it's main purpose (enumerate values) instead of providing some additional functionality (like in my two examples)?
  2. Should I just stick to enums being just what their name implies and avoid the temptation of using the features that Java brings to this data type? What would be your criteria to evaluate how much functionality to put on enums?
  • Enum is a Singleton. Singleton is often treated as antipattern. I guess it would work for logging, but beware otherwise. – Basilevs May 13 '16 at 5:50
  • I liked certain aspects of all answers so far, but the one chosen is the one that gives a more balanced view (which in the end I think is the path I'll follow). Thanks all for your answers! – carlossierra May 16 '16 at 13:46
6

Perhaps it's because I used C/C++ in a previous life but I would argue strongly for keeping the enums simple. In those languages an enum was a glorified integer only. In Java, as you say, they are really a true object. But I feel that the spirt of an enum is not to have a ton of code in there - an enumeration is a complete, ordered listing of all the items in a collection. I like this definition because it doesn't imply any behavior - just a container.

  • +1 I don't see the benefits of attaching log components to a enum. Plus any API for logs brings such enum with in – Laiv May 12 '16 at 17:56
2

In Java, enums are designed to be more than just constants. Enum values are objects (like one you could instanciate from any standard class) but with a few restrictions:

  • Instances of an enum are restricted by yourself: when you write enum Number {ONE, TWO, THREE;} you explicitly say that theses 3 instances are the only one allowed to live
  • Java takes care to instanciate the instances of the enum (therefore the constructor must be private)
  • Java also generates a bunch of helper methods to help you using these instances of enum (values(), valueOf(), ...)

Since they are objects and since objects have behaviour, it's perfectly fine to have methods in enums.

However, I rarely faced a case where I ended up implementing something like that. The only time I do this is when I have a class that I know from the requirements or the domain it can only have a known set of instances. Example:

Norm A describe how a device counting energy should be identified.

Company X is working in energy domain and produces 3 devices that are separately identified according to norm A. Let's say that these 3 devices are identified as such:

  • Device1 is from generation 1 and of type 1
  • Device2 is from generation 2 and of type 2
  • Device3 is from generation 2 and of type 1

I am mandated by Company X to create a software that manage data extracted from these 3 devices.

So this could be a potential class that modelize a device from Company X:

public final class CompanyXDevice {
    private final int generation;
    private final int deviceType;

    public CompanyXDevice(int generation, int deviceType) {
        this.generation = generation;
        this.deviceType = deviceType;
    }

    public int[] generateHistoricalData() {
        ...
    }

    ...
}

Everything could perfectly work here... except that it is possible to do something like that: new CompanyXDevice(3, 3); which shouldn't be allowed since CompanyX has no such device. The next step is to find a way to restrict the possibilities of instanciation. This can be achieved (not only) by refactoring to an enum:

public enum CompanyXDevice {
    DEVICE_1(1, 1),
    DEVICE_2(2, 2),
    DEVICE_3(2, 1);

    private final int generation;
    private final int deviceType;

    private CompanyXDevice(int generation, int deviceType) {
        this.generation = generation;
        this.deviceType = deviceType;
    }

    public int[] generateHistoricalData() {
        ...
    }

    ...
}

Now is it only possible to play with devices that really reflect a real device of Company X.

1

It seems as if you're thinking of "values" in a constrained way (perhaps applying a concept from other languages).

What if you expanded your idea of the kinds of values that can be enumerated? That opens a great range of possible uses. Your design wisdom will keep you from doing horrible things with that awesome power.

Try this:

  • Think of an enum as nothing more than a type with a fixed set of values.
  • Expand your concept of enum values to match whatever Java allows.
  • Apply all of your design wisdom, given that expanded concept of enum.
  • Dale, so if I understand your reasoning correctly, putting the logging logic inside the enum would be appropriate? Or would this be one of those horrible things to do? – carlossierra May 12 '16 at 19:13
  • I think it's not obviously inappropriate. Enums create fixed a set of values. And it's really, really fixed. Users can't extend the set. Given that important effect of using an enum, the question is: Do you really, really, really want to establish a fixed, user-inextensible set of logging levels? If yes, use an enum. If you want users to be able to define additional logging levels, then use some other construct to define your initial set. – Dale Emery May 12 '16 at 20:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.