Speaking of method on enum, I used to think it's very handy feature but finally 
I failed on some scenarios and switched to own defined class.
The problem is enum is singleton that you can't save stateful data in it. For 
example:

public enum Type {
        private Object userData;

        private Object getUserData() {...}
             private void setUserData(Object data) {...}
        .... 

        Type SOME_TYPE;
}

This kind of thing is somewhat useless, userData here is actually global, every 
call to SOME_TYPE.setUserData() would change its value. 
So for usage perspective the methods you define on enmu are like static method 
on class which has its limitations.

As for methods in enmu body in your example, the only usage I can recall now is 
like what TimeUnit does. For CloudStack, we can do the similar
thing: SizeUnit that translates storage/memory size in different quantities.

public enum SizeUnit {
    BYTE {
        public long toKiloByte(long s) {
            return (s / (k / b));
        }

        public long toMegaByte(long s) {
            return (s / (m / b));
        }

        public long toGigaByte(long s) {
            return (s / (g / b));
        }

        public long toTeraByte(long s) {
            return (s / (t / b));
        }
    },
    KILOBYTE {
        public long toByte(long s) {
            return (s * (k / b));
        }

        public long toMegaByte(long s) {
            return (s / (m / k));
        }

        public long toGigaByte(long s) {
            return (s / (g / k));
        }

        public long toTeraByte(long s) {
            return (s / (t / k));
        }
    },
    MEGABYTE {
        public long toByte(long s) {
            return (s * (m / b));
        }

        public long toKiloByte(long s) {
            return (s * (m / k));
        }

        public long toGigaByte(long s) {
            return (s / (g / m));
        }

        public long toTeraByte(long s) {
            return (s / (t / m));
        }
    },
    GIGABYTE {
        public long toByte(long s) {
            return (s * (g / b));
        }

        public long toKiloByte(long s) {
            return (s * (g / k));
        }

        public long toMegaByte(long s) {
            return (s * (g / m));
        }

        public long toTeraByte(long s) {
            return (s / (t / g));
        }
    },
    TERABYTE {
        public long toByte(long s) {
            return (s * (t / b));
        }

        public long toKiloByte(long s) {
            return (s * (t / k));
        }

        public long toMegaByte(long s) {
            return (s * (t / m));
        }

        public long toGigaByte(long s) {
            return (s * (t / g));
        }
    };

    private static final long b = 1;
    private static final long k = b * 1024;
    private static final long m = k * 1024;
    private static final long g = m * 1024;
    private static final long t = g * 1024;
    
    public long toByte(long s) {
        throw new AbstractMethodError();
    }
    public long toKiloByte(long s) {
        throw new AbstractMethodError();
    }   
    public long toMegaByte(long s) {
        throw new AbstractMethodError();        
    }
    public long toGigaByte(long s) {
        throw new AbstractMethodError();
    }
    public long toTeraByte(long s) {
        throw new AbstractMethodError();
    }
}

This may explain some of my views on java enum. It does have lots of advantages 
like compiler check, native == operator. Just like
Alex mentioned, for constants that unlikely to change in future, we can stick 
to enum. But for types that plugin may extend, we'd
better to use own defined classes. 

From: John Burwell [mailto:jburw...@basho.com] 
Sent: Tuesday, July 23, 2013 6:25 AM
To: dev@cloudstack.apache.org
Subject: Re: [DESIGN] Why is enum a class...

All,

+1 to Alex's design suggestion.  Another little know feature of enumerations is 
the ability to define abstract methods.  Therefore, Alex's example can be 
expanded as follows:

public enum Type {
       User(false)
        {
                @Override
                public void doWork() {
                }
        },
       DomainRouter(true)
        {
                @Override
                public void doWork() {
                }
        },
       ConsoleProxy(true)
        {
                @Override
                public void doWork(){
                {
        },
       SecondaryStorageVm(true)
        {
                @Override
                public void doWork(){
                {
        },
       ElasticIpVm(true)
        {
                @Override
                public void doWork(){
                {
        },
       ElasticLoadBalancerVm(true)
        {
                @Override
                public void doWork(){
                {
        },
       InternalLoadBalancerVm(true)
        {
                @Override
                public void doWork(){
                {
        }

       /*
        * UserBareMetal is only used for selecting VirtualMachineGuru, there is 
no
        * VM with this type. UserBareMetal should treat exactly as User.
        */
       UserBareMetal(false);

      private boolean _isSystemVm;

      private Type(Boolean isSystemVm) {
          _isSystemVm = isSystemVm;
      }

       public boolean isSystemVM() {
          return _isSystemVm;
       }

      public abstract doWork();

   }

Personally, I see using switch statements with Java enumerations as a bad code 
smell.  Java enumerations were specifically designed to allow the 
implementation of behavior on values.  In addition to simplifying code, it 
greatly increases cohesion by defining all of the uses of the value in one 
place and using the compiler to validate coverage.  Finally, it makes unit 
testing of this conditional functionality much more straight forward.

To Frank's point, I don't see the need for another class.  The Java compiler 
properly generates the boilerplate methods and ensures that all enumeration 
values are singletons within a class loader.  Regardless of the state added to 
an enumeration value, User==User will always be true.

Thanks,
-John

On Jul 23, 2013, at 3:48 AM, Daan Hoogland <daan.hoogl...@gmail.com> wrote:


+1 I am having a war on the enums in Networks.java that justifies your
solution, Frank


On Tue, Jul 23, 2013 at 2:57 AM, Alex Huang <alex.hu...@citrix.com> wrote:


+1 to do this for any enum that was designed to be added to by the plugins.

I actually wrote a class for this before called Constant that did exactly
this.  The problem I had was that there was no way to guarantee compile
time enumeration of all of the values like Enum did so it's possible for
errors to be introduced but certainly we can follow a convention in this
regard.  Another way would be to use Spring to gather them and inject them
into a holding class.

--Alex


-----Original Message-----
From: Frank Zhang [mailto:frank.zh...@citrix.com]
Sent: Monday, July 22, 2013 5:45 PM
To: dev@cloudstack.apache.org
Subject: RE: [DESIGN] Why is enum a class...

Frankly speaking, if we are going to change enum, I would suggest not
using

enmu anymore, instead, defining our own class like:

public class VmType {
     private static Map<String, VmType > types =
Collections.synchronizedMap(new HashMap<String, VmType >());
     private final String typeName;

     public VmType (String typeName) {
             this.typeName = typeName;
             types.put(typeName, this);
     }

     public static VmType valueOf(String typeName) {
             VmType type = types.get(typeName);
             if (type == null) {
                     throw new IllegalArgumentException("VmType type:
" + typeName + " was not registered by any VmType");
             }
             return type;
     }

     @Override
     public String toString() {
             return typeName;
     }

     @Override
     public boolean equals(Object t) {
             if (t == null || !(t instanceof VmType)) {
                     return false;
             }

             VmType type = (VmType)t;
             return type.toString().equals(typeName);
     }

     @Override
     public int hashCode() {
             return typeName.hashCode();
     }

     public static Set<String> getAllTypeNames() {
         return types.keySet();
     }
}

The only benefit of enum I see is you can use "==" instead of "equals()"
when comparing variables. However, it makes your code tight, every time
adding a new type you have to modify the enum.

By above method, when a new plugin wants to extend a new VmType, it
simply does:

class MagicVmManagerImpl {
       public static final VmType type = new VmType("MagicVm"); }



-----Original Message-----
From: Alex Huang [mailto:alex.hu...@citrix.com]
Sent: Monday, July 22, 2013 5:23 PM
To: dev@cloudstack.apache.org
Subject: RE: [DESIGN] Why is enum a class...

BTW, this code already shows a bug that stems from the static method
usage.

It says ElasticIpVm is not a system vm (which I don't believe is
true).  Probably because whoever added elastic ip vm didn't see the
static method and so didn't add the vm into the method.

--Alex


-----Original Message-----
From: Alex Huang [mailto:alex.hu...@citrix.com]
Sent: Monday, July 22, 2013 5:14 PM
To: dev@cloudstack.apache.org
Subject: [DESIGN] Why is enum a class...

I just went over this code and thought it was related and might be
interested to other developers.

What's the difference between declaring a enum like this

   public enum Type {
       User,
       DomainRouter,
       ConsoleProxy,
       SecondaryStorageVm,
       ElasticIpVm,
       ElasticLoadBalancerVm,
       InternalLoadBalancerVm,

       /*
        * UserBareMetal is only used for selecting
VirtualMachineGuru, there is no
        * VM with this type. UserBareMetal should treat exactly as
User.

        */
       UserBareMetal;

       public static boolean isSystemVM(VirtualMachine.Type vmtype)
{

           if (DomainRouter.equals(vmtype)
                   || ConsoleProxy.equals(vmtype)
                   || SecondaryStorageVm.equals(vmtype) ||
InternalLoadBalancerVm.equals(vmtype)) {
               return true;
           }
           return false;
       }
   }

Vs

   public enum Type {
       User(false),
       DomainRouter(true),
       ConsoleProxy(true),
       SecondaryStorageVm(true),
       ElasticIpVm(true),
       ElasticLoadBalancerVm(true),
       InternalLoadBalancerVm(true),

       /*
        * UserBareMetal is only used for selecting
VirtualMachineGuru, there is no
        * VM with this type. UserBareMetal should treat exactly as
User.

        */
       UserBareMetal(false);

      private boolean _isSystemVm;

      private Type(Boolean isSystemVm) {
          _isSystemVm = isSystemVm;
      }

       public boolean isSystemVM() {
          return _isSystemVm;
       }
   }

The second declaration is more self-descriptive:

- It tells developer when they add more to this enum, they have to
specify whether it is a system vm.  They may have missed the static
method in the first declaration and causes failures later.
- It tells developers using the enum that there's a property that
let's them know it is a system vm so they can do discovery using a
context-sensitive editor like Eclipse.

This is the reason why enum is not a simple constant declaration in
Java (vs C/C++).
--Alex


Reply via email to