Search code examples
c#design-patternsrefactoringstrategy-pattern

Is it possible to use a Strategy pattern for data structure with no common ancestor?


I have two data structure classes (this is a simplified version of my code)

  • Animal: Has one property “int Age”
  • Person: Has one property “DateTime Birthday”

What I am trying to accomplish is to compile “Uploading” (persisting to database), which is common across all different data structure classes. So mainly my goal is to have a small Upload method that looks like

   foreach (TypeName typeName in Enum.GetValues(typeof(TypeName)))
   {
      IDataPopulator populator = 
            new DataFactory().CreateDataPopulator(typeName);
         populator.Populate(string.Empty);
   }

But the problem is that, populator returns an object instances of different types, which I am having trying to encapsulate since they have no common properties. (IDataPopulator.TResult Populate(string data) in the code below)

Is there a way to get around this? Or does Strategy pattern not fit for this kind of scenario?

Here is the code I’ve been working with

public class Program
{
    public static void Main()
    {
        foreach (TypeName typeName in Enum.GetValues(typeof(TypeName)))
        {
            IDataPopulator populator = new DataFactory().CreateDataPopulator(typeName);
            populator.Populate(string.Empty);
        }
    }
}

public enum TypeName { Person, Animal }
public class Person { public DateTime Birthday { get; set; } }
public class Animal { public int Age { get; set; } }

public interface IDataPopulator
{
    TResult Populate(string data);
}

class AnimalDataPopulator : IDataPopulator
{
    public Animal Populate(string data)
    {
        // create an instance of Animal using data
    }
}

class PersonDataPopulator : IDataPopulator
{
    public Person Populate(string data)
    {
        // create an instance of Person using data
    }
}

public class DataFactory
{
    public IDataPopulator CreateDataPopulator(TypeName typeName)
    {
        switch (typeName)
        {
            case TypeName.Person:
                return new PersonDataPopulator();
            case TypeName.Animal:
                return new AnimalDataPopulator();
            default:
                throw new ArgumentOutOfRangeException("typeName");
        }
    }
}

public class UploadContext
{
    private readonly IUploader _Uploader;
    public UploadContext(IUploader uploader) { _Uploader = uploader; }
    public void Upload() { _Uploader.Upload(); }
}

public interface IUploader
{
    void Upload();
}

class PersonUploader : IUploader
{
    private Person _Person;
    public PersonUploader(Person person) { _Person = person; }
    public void Upload()
    {
        Console.WriteLine("Uploading person...");
    }
}
class AnimalUploader : IUploader
{
    private Animal _Animal;
    public AnimalUploader(Animal animal) { _Animal = animal; }
    public void Upload()
    {
        Console.WriteLine("Uploading animal...");
    }
}

Solution

  • I don't see how the Strategy pattern would fit here. The pattern is about a family of algorithms, e.g. line breaking strategies. You don't have strategies here, you have a single abstract method

    TResult Populate(string data);

    and several implementations of it.

    I also don't understand the initial problem, maybe you want to queue several serialization operations? In that case the Command pattern is your friend.