Search code examples
c#factory-pattern

Factory Pattern and object persistence


I am using factory design pattern in one of the segments of my application and I noticed that using a regular factory pattern creates a new object every time. I have a scenario where I need to iterate the createinstance method in the factory class multiple times and every time a new consumer object is created.

In Program.cs, I have created List of persons, where I keyed some random input data. The code based on action on each person should dynamically execute the process method in that class. This logic seems to work as I expected but if the same action is repeated then the same object is created multiple times.

In order to fix this issue, I modified the ActionFactory class (showed in Modified Action Factory snippet below) where I am maintaining a dictionary to save the instance created for a specific action. If the action is repeated then instead of creating a new instance, I am getting the previously created instance from the dictionary. This seems to have fixed the issue as shown in Output2 image.

But I would like know/learn from the pros if what I am doing in "Modified Action Factory" class is sufficient (or) are there any other patterns should I be using instead of Factory design pattern for this type of problems. For maintaining a single object, a another approach I can think of is to probably create a Singleton Object in each of the Action classes, but the issue with that would be how do I call the singleton instance from the factory class?? As the factory class always calls createinstance and that would not call the singleton instance..I am new to design patterns and I am thriving to learn more about them and improve my coding standards. Thanks in advance

Below is the code:

  class Program
{
    static void Main(string[] args)
    {
        List<Person> ListofPeople = new List<Person>();

        Person p1 = new Person() {Name = "P1", State = "CA", Action = "Add"};

        ListofPeople.Add(p1);

        Person p2 = new Person() { Name = "P2", State = "NJ", Action = "Add" };

        ListofPeople.Add(p2);

        Person p3 = new Person() { Name = "P3", State = "VA", Action = "Update" };

        ListofPeople.Add(p3);

        Person p4 = new Person() { Name = "P4", State = "VA", Action = "Update" };

        ListofPeople.Add(p4);


        Person p5 = new Person() { Name = "P5", State = "VA", Action = "Update" };

        ListofPeople.Add(p5);


        Person p6 = new Person() { Name = "P6", State = "VA", Action = "Delete" };

        ListofPeople.Add(p6);


        ActionFactory factory= new ActionFactory();

        foreach (var person in ListofPeople)
        {
            IAction action = (IAction) factory.CreateInstance(person.Action.ToLower());

            action.Process();

        }
        Console.ReadKey();


    }
}

public class Person
{
    public string Name { get; set; }
    public string State { get; set; }
    public string Action { get; set; }

}

ActionFactory:

public class ActionFactory
{
    Dictionary<string, Type> actions;

    public ActionFactory()
    {
        LoadTypesICanReturn();
    }

    public object CreateInstance(string actionName)
    {
        Type t = GetTypeToCreate(actionName);

        if (t == null)
            return null;

        var actionProcessor = Activator.CreateInstance(t) as IAction;

        return actionProcessor;
    }

    Type GetTypeToCreate(string actionName)
    {
        foreach (var action in actions)
        {
            if (action.Key.Contains(actionName))
            {
                return actions[action.Key];
            }
        }

        return null;
    }

    void LoadTypesICanReturn()
    {
        actions = new Dictionary<string, Type>();

        Type[] typesInThisAssembly = Assembly.GetExecutingAssembly().GetTypes();

        foreach (Type type in typesInThisAssembly)
        {
            if (type.GetInterface(typeof(IAction).ToString()) != null)
            {
                actions.Add(type.Name.ToLower(), type);
            }
        }
    }
}

IAction:

   public interface IAction
{
    void Process();
}

Add.cs

  public class Add: IAction
{

    public Add()
    {
        Console.WriteLine("add constructor...");
    }

    #region IAction Members

    public void Process()
    {
        Console.WriteLine("Add Processor....");
    }

    #endregion
}

Update.cs

 public class Update: IAction
{
    public Update()
    {
        Console.WriteLine("Update constructor...");
    }


    public void Process()
    {
        Console.WriteLine("Update Processor...");
    }
}

Delete.cs

public class Delete : IAction
{

    public Delete()
    {
        Console.WriteLine("Delete Constructor...");
    }



    #region IAction Members

    public void Process()
    {
        Console.WriteLine("Delete Processor...");
    }

    #endregion
}

The console output shows the number of times the IAction consumer classes are instantiated

Output:

enter image description here

Modified Action Factory:

 public class ActionFactory
{
    Dictionary<string, Type> actions;

    private Dictionary<Type, IAction> actionInstances; 

    public ActionFactory()
    {

        actionInstances = new Dictionary<Type, IAction>();
        LoadTypesICanReturn();
    }

    public object CreateInstance(string actionName)
    {
        Type t = GetTypeToCreate(actionName);

        if (t == null)
            return null;

        if (actionInstances.ContainsKey(t))

            return actionInstances[t];

        else
        {
            var actionProcessor = Activator.CreateInstance(t) as IAction;

            LoadIAction(t, actionProcessor);

            return actionProcessor;
        }


    }


    private void LoadIAction(Type t, IAction actionProcessor)
    {

        if (!actionInstances.ContainsKey(t))
        {
            actionInstances.Add(t, actionProcessor);
        }
    }

    Type GetTypeToCreate(string actionName)
    {
        foreach (var action in actions)
        {
            if (action.Key.Contains(actionName))
            {
                return actions[action.Key];
            }
        }

        return null;
    }

    void LoadTypesICanReturn()
    {
        actions = new Dictionary<string, Type>();

        Type[] typesInThisAssembly = Assembly.GetExecutingAssembly().GetTypes();

        foreach (Type type in typesInThisAssembly)
        {
            if (type.GetInterface(typeof(IAction).ToString()) != null)
            {
                actions.Add(type.Name.ToLower(), type);
            }
        }
    }
}

Output with Modified Action Factory:

enter image description here


Solution

  • The Factory design pattern is used to ease creation of objects, usually when you don't know the type to be created, and it will be supplied in the runtime.
    Sometimes, it may also be because they have dependencies, and you want to abstract the dependency creation, or just because you want something to be done.

    In your case, Add, Update, and Delete are methods that are usually called by some 3rd-party, not the datatype itself, as specified by @sokohavi above.

    Your case can be handled by the Command Design Pattern.

    Say you want to load a bunch of commands (aka: Add Person X, Update Person Y, Delete Person Z), and you want to buffer them for future use.

    abstract class PersonCommand
    {
        Person Person { protected get; } // you can call this payload, or person, w/e
    
        PersonCommand(Person person)
        {
            Person = person;
        }
    
        public abstract void Apply(); // can be called Execute or Process as well, or w/e. problem domain.
    }
    

    Then, you'd have an implementation for each command:

    class AddCommand : PersonCommand
    {
        AddCommand(Person person) : base(person) { }
        public override void Apply()
        {
            // send REST PUT request, add person to DB, whatever you want to do here.
        }
    }
    
    class UpdateCommand : PersonCommand
    {
        UpdateCommand(Person person) : base(person) { }
        public override void Apply()
        {
            // send REST POST request, update person in DB, whatever you want to do here.
        }
    }
    
    class DeleteCommand : PersonCommand
    {
        DeleteCommand(Person person) : base(person) { }
        public override void Apply()
        {
            // send REST DELETE request, remove person from DB, whatever you want to do here.
        }
    }
    

    When you have this, you can do the following:

    var commandsToExecute = new List<PersonCommand>();
    
    commandsToExecute.Add(new AddCommand(new Person { Name = "asd", State = "CA" }));
    
    commandsToExecute.Add(new UpdateCommand(new Person { Name = "barry", State = "CA" }));
    
    commandsToExecute.Add(new DeleteCommand(new Person { Name = "barry", State = "CA" }));
    
    commandsToExecute.ForEach(cmd => cmd.Apply());
    

    Now, this is a basic example of what you can do. Maybe, in some scenario, Commands aren't so easily created, they might need to know about the service endpoint for your REST requests, for example.

    For that, a CommandFactory can be created, which can then handle creation by action name:

    class CommandFactory
    {
        private readonly Dictionary<string, Func<Person, PersonCommand>> creationFuncs;
    
        CommandFactory(string backendUrl)
        {
            creationFuncs = new Dictionary<string, Func<Command, Person>>();
    
            creationFuncs.Add("add", (person) => return new AddCommand(backendUrl, person));
            creationFuncs.Add("update", (person) => return new UpdateCommand(backendUrl, person));
            creationFuncs.Add("delete", (person) => return new DeleteCommand(backendUrl, person));
        }
    
        PersonCommand Create(string action, Person person)
        {
            // validation can be added here
            return creationFuncs[action](person);
        }
    }
    

    And usage would be:

    var factory = new CommandFactory("http://localhost:4200/person/");
    
    var commandsToExecute = new List<PersonCommand>();
    
    commandsToExecute.Add(factory.Create("add", new Person { Name = "asd", State = "CA" }));
    
    commandsToExecute.Add(factory.Create("update", new Person { Name = "barry", State = "CA" }));
    
    commandsToExecute.Add(factory.Create("delete", new Person { Name = "barry", State = "CA" }));
    
    commandsToExecute.ForEach(cmd => cmd.Apply());
    

    There are design patterns for everything, you should just find the one that fits your need.