Saturday, April 21, 2012

Enum - Enumerated Type

In this post, we're going to look at the enumerated type or enum, as it might exist in Javascript and how one might use an enumerated type in a program. 


Interestingly,  'enum' is a reserved word, which is an indicator of its importance as a programming concept and its possible inclusion as a standard type in future versions of the Javascript language. You can try the examples in this post by cutting and pasting to here.


In Javascript, an enumerated type (or enum) is an immutable object whose fields consist of a fixed set of named constants. Usually, enums are used to express variations of type. For instance, an image type can be gif, jpg or png. 
In Javascript 1.8.5, we'd write an ImageType enum like so:

     function imageTypeEnum() {  
          var imageEnum = {  
             PNG: 'png',  
             JPG: 'jpg',  
             GIF: 'gif'  
          };       
          return Object.freeze(imageEnum);  
     }  
    
    Using Object.freeze(obj) :

    • Nothing can be added to or removed from the objects property set 
    • Data Property values cannot be changed
    • Accessor properties work the same, still giving the illusion that you are changing the value.

    Since our enumerated type only has data properties, it's now immutable.


    How would you use an enumerated type?

     var imageVerifier = (function() {  
        var validImageExtns = getValidImageTypes(),   
            rePattern = '\.(?:' + validImageExtns + ')',   
            isValidImageName = new RegExp(rePattern, 'i');  
        
        function getValidImageTypes() {   
           var validImagetypes,  
               imageTypes = imageTypeEnum();  
        
           for(var prop in imageTypes) {   
             if(!validImagetypes) {   
                validImagetypes = imageTypes[prop];   
             } else {   
                validImagetypes = validImagetypes + '|' + imageTypes[prop];   
             }   
           }   
           return validImagetypes;  
        }  
        
        function checkImageNameIsValid(imageName) {   
           if(!isValidImageName.test(imageName)) {   
             throw new Error(imageName + ' must be one of(' + validImageExtns + ')');      
           }   
        }    
        return {  
          checkImageNameIsValid: checkImageNameIsValid  
        };  
     })();  
       
     writeln(' ------------ TEST: Success Case ------------')  
     try {  
       imageVerifier.checkImageNameIsValid('one.jpg');  
       writeln(' Image name valid');  
     } catch(err) {  
       writeln(' Image is not valid: ' + err);  
     }  
     writeln(' ------------ TEST: Failure Case ------------')  
     try {  
       imageVerifier.checkImageNameIsValid('one.wtf');  
       writeln(' Image name valid');  
     } catch(err) {  
       writeln(' Image is not valid: ' + err);  
     }  
    
    Which outputs:
      ------------ TEST: Success Case ------------  
      Image name valid  
      ------------ TEST: Failure Case ------------  
      Image is not valid: Error: Image [ one.wtf ] must be one of(png|jpg|gif)  
    
    By creating an enum as a pure fixed set of named constants, we can use it in a number of different contexts; function getValidImageTypes()  being one example.


    Another classic use of an enum is in a switch statement:
     var reportPeriodEnum = (function() {  
       var period = {  
                 HOUR: 'hour',  
                 DAY: 'day',  
                 WEEK: 'week',  
                 MONTH: 'month',  
                 ALL_TIME: 'all_time'  
       };  
       return Object.freeze(period);  
     })();  
     function reportOn(rptPeriod) {  
          switch(rptPeriod) {  
               case reportPeriodEnum.HOUR:  
                    writeln('Activity in the last ' + reportPeriodEnum.HOUR);  
                    break;  
               case reportPeriodEnum.DAY:  
                    writeln('Activity in last ' + reportPeriodEnum.DAY);  
                    break;  
               case reportPeriodEnum.WEEK:  
               case reportPeriodEnum.MONTH:  
               case reportPeriodEnum.ALL_TIME:  
                    writeln('Can only report on activity in last ' + reportPeriodEnum.WEEK);  
                    break;       
               default:  
                    writeln('Unknown reporting period: ' + rptPeriod);  
          }            
     }  
     reportOn(reportPeriodEnum.WEEK); //  Can only report on activity in last week  
    


    Closing

    We've used an enumerated type to encapsulate types of images in one place for use anywhere in your application. The enumerated type pattern also requires that its result be immutable in order to guarantee a consistent answer, regardless of context. 


    Ideally, by encapsulating the image type variations in an enumerated type, you should never have to write gif, jpg or png again. In the case where you might want to include a new image type - lets say tiff; You should only have to change it in one place, in accordance with the well known development principle - DRY (Don't repeat yourself).


    Further Reading

    MDN - freeze

    No comments:

    Post a Comment