Home > database >  Factory Method with Generic Interface
Factory Method with Generic Interface

Time:10-29

I have two different types of item: Picture and Video. In order to manage them, I have created an interface and two classes which implement it

public interface Item{
}

public class ItemPicture: Item{
}

public class ItemVideo: Item {
}

Now I have some classes to manage that items, that inherit from a manager interface

public interface ItemManager<T>{
   IList<T> FGetByGroup(string idgroup);
}

public class ItemManagerPictures : IItemManager<ItemPicture>{
   public IList<ItemFoto> FGetByGroup(string idgroup){
      ...
   }
}
public class ItemManagerVideos: IItemManager<ItemVideo>{
   public IList<ItemVideo> FGetByGroup(string idgroup){
      ...
   }
}

In order to have a factory method which creates the appropriate object, I have created this class

public class ItemManagerCreator
    {
        public static IItemManager<Item> MakeItemManager(string type)
        {
            IItemManager<Item> objMgr = null;

            switch (type)
            {
                
                case "Picture":
                    objMgr = (IItemManager<Item>)new ItemManagerPictures();
                    break;

                case "Video":
                    objMgr = (IItemManager<Item>)new ItemManagerVideos();
                    break;

                default:
                    break;
            }
            return objMgr ;

        }
    }

From the controller I want to do this

var type="Picture";

IItemManager<Item> itemMgr = ItemManagerCreator.MakeItemManager(type);
var itemList = itemMgr.FGetByGroup(string idgroup);

But I get this casting error

Can't convert from type '...ItemManagerPictures' to type '...IItemManager`1[...Item]'.

which makes me think I'm doing something wrong, but after 1000 turns, I think the problem is with the factory method and generics, which is not well designed.

I'm new to design patterns, and maybe I'm not applying the right one.

Thanks in advance

CodePudding user response:

It doesn't work as is, because ItemManagerPictures and ItemManagerVideos are indeed not convertible to IItemManager<Item>, check covariance and contravariance to see why.

In this case, because you actually do not need to use IList as return type of FGetByGroup (as figured out in comments), you can make it work by using some interface which is covariant in generic type T (such as IEnumerable<T> or IReadOnlyList<T>) and then declare your type T as covariant too:

public interface IItemManager<out T>{
    IReadOnlyList<T> FGetByGroup(string idgroup);
}

public class ItemManagerPictures : IItemManager<ItemPicture>{
    public IReadOnlyList<ItemPicture> FGetByGroup(string idgroup) {
        return null;
    }
}

Now, ItemManagerPictures is assignable to IItemManager<Item> so your code will work without exceptions.

CodePudding user response:

From your code you seem to know beforehand what type of ItemManager you want, so you can do something like this :

public class ItemManagerCreator
{
    public static IItemManager<T> MakeItemManager<T>() where T : Item
    {
        if (typeof(T) == typeof(ItemPicture))
        {
            return (IItemManager<T>)new ItemManagerPictures();
        }

        if (typeof(T) == typeof(ItemVideo))
        {
            return (IItemManager<T>)new ItemManagerVideos();
        }

        throw new InvalidOperationException();
    }
}

And for use :

string groupId = string.Empty;
dynamic itemManager = null;

I want an IItemManager for pictures

itemManager = ItemManagerCreator.MakeItemManager<ItemPicture>();
IList<ItemPicture> pictures = itemManager.FGetByGroup(groupId);

Then I want an IItemManager for videos

itemManager = ItemManagerCreator.MakeItemManager<ItemVideo>();
IList<ItemVideo> videos = itemManager.FGetByGroup(groupId);
  • Related