Search code examples
c#factory-pattern

Getting a list of cases from a switch/case to use in a validation method


Long story short, I'm creating a factory that looks a bit like this:

    public static ITag GetByTagName(string tagName)
    {
        tagName = tagName.ToLower();
        switch (tagName)
        {
            case "devicevar":
                return new DeviceVar();
            case "devicefilter":
                return new DeviceFilter();
        }
        return null;
    }

But I also need a method that checks if tagName corresponds to an ITag. Essentially I'm checking to see if GetByTagName would return null without actually running it:

private List<string> tagNames = new List<string> { "devicevar", "devicefilter" };

public static bool IsValidTagName(string tagName)
{
    return tagName.Contains(tagName);
}

See how we have some ugly duplication there? I don't want to specify the possible values for tagName twice. I could always change my method so it uses the factory to try and instanciate an ITag and if it returns null, then I know it wasn't a valid value. But this just seems wasteful:

public static bool IsValidTagName(string tagName)
{
    return GetByTagName(tagName) != null;
}

There must be a better way

NOTE:

I have a good (albiet longwinded) reason for needed to make this check, and it's not

"Would GetByTagName return an ITag if I gave it this string? Yes? Okay, call GetByTagName"


Solution

  • You can create dictionary of type Dictionary<string, Type> which will map tag types to their names.

    Dictionary<string, Type> tags = new Dictionary<string, Type>() {
        { "devicevar", typeof(DeviceVar) },
        { "devicefilter", typeof(DeviceFilter) }
    };
    

    Use ContainsKey to check if name is valid:

    public static bool IsValidTagName(string tagName)
    {
        return tags.ContainsKey(tagName);
    }
    

    Use Activator to create instances:

    public static ITag GetByTagName(string tagName)
    {
        tagName = tagName.ToLower();
    
        if (!IsValidTagName(tagName))
            throw new ArgumentException(); // or return null
    
        return (ITag)Activator.CreateInstance(tags[tagName]);
    }
    

    UPDATE (for performance) Instead of storing type in dictionary, store creation methods there:

    var tagsFactory = new Dictionary<string, Func<ITag>>() {
        { "devicevar", _ => new DeviceVar() },
        { "devicefilter", _ => new DeviceFilter() }
    };
    

    And getting tags will look like:

    return tagsFactory[tagName]();