Search code examples
c#genericsdesign-patternscoding-styleclean-architecture

How to streamline class initialization within an object based on a parameter type


Based on a passed parameter containing a type I'm looking for a better way to initialize the class based on that type. Currently I'm "solving" it with a switch statement which is feels quite redundant.

Bad example showing what the functionality is:

protected Quest GenerateQuest(QuestConfiguration questConfiguration)
{

    List<QuestObjective> objectives = new();
    foreach ( QuestObjectiveConfiguration questConfigurationObjective in questConfiguration.ObjectiveConfigurations)
    {
        switch (questConfigurationObjective.ObjectiveType)
        {
            case ObjectiveType.Fetch:
                objectives.Add(new ObjectiveFetch(questConfigurationObjective.InternalId));
                break;
            case ObjectiveType.Gather:
                objectives.Add(new ObjectiveGather(questConfigurationObjective.InternalId));
                break;
            case ObjectiveType.Craft:
                objectives.Add(new ObjectiveCraft(questConfigurationObjective.InternalId));
                break;
            case ObjectiveType.Deliver:
                objectives.Add(new ObjectiveDeliver(questConfigurationObjective.InternalId));
                break;
            case ObjectiveType.Combat:
                objectives.Add(new ObjectiveCombatEncounter(questConfigurationObjective.InternalId));
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }
    }
    return new Quest(objectives);
}

Looked/googled for suiting patterns but I'm having a hard time getting specific results because the related terms are generic and the issue is an edgecase.


Solution

  • If the QuestObjective can be created with just QuestObjectiveConfiguration, then I'd consider having the switch statement in a method or extension method of it.

    This of course does not get rid of the switch statement but atleast keeps it in one place.

    static class QuestObjectiveConfigurationExtensions
    {
        public static QuestObjective ToQuestObjective(
            this QuestObjectiveConfiguration config) => config.ObjectiveType switch
        {
            ObjectiveType.Fetch => new ObjectiveFetch(config.InternalId),
            //rest of your types
            _ => //maybe throw an Exception here
        };
    }
    

    This shortens your objective creation to

    var objectives = questConfiguration.ObjectiveConfigurations.Select(x => x.ToQuestObjective)
    

    You can do the same for QuestConfiguration and Quest of course and implement a ToQuest method.

    One option to get rid of the switch statement completely would be to have different types of QuestObjectiveConfiguration that implement different methods of ToQuestObjective.