3

This is an extension of an earlier question I made.

So, using the answer from the previous question I mentioned, I am able to make a generic utility that would convert/parse an Enumeration to a String.

Now the only problem I have with my solution is, I can't seem to refactor the code to my liking. It works the way I want it to, but it is a tad more on the code heavy side than I had hoped for. I was curious if there was a design pattern (or any other way to code it) to help solve this issue. I am thinking there is a creational design pattern that will help in this case. I would be easy if I had lambda expression but, Apex is not quite there yet ;).

The code I use has kind of patterns dug into it already. It uses the Strategy, Factory, and Template(ish) pattern I believe. I have the resources to find something to solve this issue (my favorite Design Pattern books), but nothing I can think of jumps out at me.

Here is the code for my utility for just one enumeration:

private interface EnumerationToStringHelper
{
    List<Object> ProvideEnumerationValues();
    Boolean CompareEnumerationToString(String possibleEnumeration, Object enumerationValue);
}

private class ExampleEnumToStringHelper implements EnumerationToStringHelperExampleEnum
{
    public List<Object> ProvideEnumerationValues(){ return (List<Object>)ExampleEnum.values(); }
    public Boolean CompareEnumerationToString(String possibleEnumeration, Object enumerationValue)
    { 
       return  ((ExampleEnum)enumerationValue).name().equalsIgnoreCase(possibleEnumeration);
    }
}

private static Map<Type, EnumerationToStringHelper> TypeToEnumerationMap;

private static void PopulateTypeToEnumerationMap()
{
    TypeToEnumerationMap = new Map<Type, EnumerationToStringHelper>
    {
        Type.forName('ExampleEnum') => new ExampleEnumToStringHelper()
    };
}

public static Object ConvertStringToEnumeration(Type enumerationType, String possibleEnumeration)
{
    if(possibleEnumeration == null) return null;
    if(TypeToEnumerationMap == null)
        PopulateTypeToEnumerationMap();

    EnumerationToStringHelper conversionAction = TypeToEnumerationMap.get(enumerationType);

    if(conversionAction == null)
        throw new EnumerationToStringException('The Type you enter is either a) not an enumeration or b) not a supported enumeration.');

    for(Object particalarEnumValue : conversionAction.ProvideEnumerationValues())
        if(conversionAction.CompareEnumerationToString(possibleEnumeration, particalarEnumValue))
            return particalarEnumValue;
    return null;
}


public class EnumerationToStringException extends Exception { }

Now in order to use the utility, it is exactly what I hoped for.

return (ExampleEnum)RT_GeneralUtilities.ConvertStringToEnumeration(Type.forName('ExampleEnum'), 'Testing');

It is a little long, but it beats having the entire for loop for 12 or more different Enum types. That is why I wanted to build the utility - essentially for better readability and test coverage purposes.

The downside with this is I have to make a helper for every single Enum type. So it makes it kind of an eyesore maintainability-wise.

I was hoping there was a pattern I could implement to assuage that eyesore and make it appealing on both sides. Any suggestions?

4

Analysis

Most of the work involved in scaling the below out would just be in maintaining the enumWrappers map. If you have varied namespacing, you may need to adjust the getEnumeration strategy somewhat. IMO it's a little strong to throw an exception if you can't find what you want, so I just return null.

Strategy

It might help to use a wrapper class (top-level or otherwise) something like:

public class EnumWrapper
{
    final Map<String, Object> values;
    public EnumWrapper(List<Object> enumValues)
    {
        values = new Map<String, Object>();
        for (Object enumValue : enumValues)
        {
            values.put(String.valueOf(enumValue).toUpperCase(), enumValue);
        }
    }
    public Object getValue(String enumeration)
    {
        return String.isBlank(enumeration) ? null :
            values.get(enumeration.toUpperCase());
    }
}

Then you could use a Map<Type, EnumWrapper> like:

public with sharing class EnumPoc
{
    public Enum Season { SPRING, SUMMER, FALL, WINTER }
    public Enum Beverage { TEA, BEER, COFFEE }
    public Enum Etc { LOREM, IPSUM }

    static Map<Type, EnumWrapper> enumWrappers
    {
        get
        {
            if (enumWrappers == null)
            {
                enumWrappers = new Map<Type, EnumWrapper>
                {
                    Type.forName('EnumPoc.Season') => new EnumWrapper(Season.values()),
                    Type.forName('EnumPoc.Beverage') => new EnumWrapper(Beverage.values()),
                    Type.forName('EnumPoc.Etc') => new EnumWrapper(Etc.values())
                };
            }
            return enumWrappers;
        }
        private set;
    }
    public static Object getEnumeration(String enumType, String enumValue)
    {
        EnumWrapper wrapper = enumWrappers.get(Type.forName('EnumPoc', enumType));
        return (wrapper == null) ? null : wrapper.getValue(enumValue);
    }
}

Debug testing:

system.debug(EnumPoc.getEnumeration('EnumPoc.season', 'winter')); // WINTER
system.debug(EnumPoc.getEnumeration('EnumPoc.beverage', 'beer')); // BEER
system.debug(EnumPoc.getEnumeration('EnumPoc.etc', 'bogus'));     // null
  • This is much more maintainable. I will close it once I put it in motion myself. In my defense on throwing an Exception: Due to the fact that the programmer has to use a String parameter, I didn't want them to use a Type that is not supported by my utility (like them putting in Decimal). Now if there is an Enum that is found but not in the given values, a null is provided. Invalid Types automatically throw an Exception if no valid type is found (like they put in seson). I could add a bit more (like what Type) to the Exception improving readability and debugging. – Programmable Medley Feb 22 '16 at 5:36
  • @ProgrammableMedley Makes sense, it's largely a matter of preference (and possibly documentation). Any progress? – Adrian Larson Feb 23 '16 at 0:06
  • 1
    Sorry about the late accept, it was kind of bust the past few days. But I wouldn't neglect accepting an answer (especially this one), I hate it when other people do that to me, I wouldn't do that to you ;), – Programmable Medley Feb 23 '16 at 20:01
  • Glad you got it worked out. :) – Adrian Larson Feb 23 '16 at 20:02

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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