具有通用接口的工厂方法

Factory Method with Generic Interface

我有两种不同类型的项目:图片和视频。为了管理它们,我创建了一个接口和两个实现它的 classes

public interface Item{
}

public class ItemPicture: Item{
}

public class ItemVideo: Item {
}

现在我有一些 classes 来管理那些继承自管理器界面的项目

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){
      ...
   }
}

为了有一个创建适当对象的工厂方法,我创建了这个 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 ;

        }
    }

从控制器我想这样做

var type="Picture";

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

但是我得到这个转换错误

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

这让我觉得我做错了什么,但是转了1000次之后,我认为问题出在工厂方法和泛型上,它们设计得不好。

我是设计模式的新手,也许我没有应用正确的模式。

提前致谢

它不能按原样工作,因为 ItemManagerPicturesItemManagerVideos 确实不能转换为 IItemManager<Item>,检查 covariance and contravariance 看看为什么。

在这种情况下,因为您实际上不需要将 IList 用作 FGetByGroup 的 return 类型(如评论中所示),您可以使用一些在通用类型 T 中协变的接口(例如 IEnumerable<T>IReadOnlyList<T>),然后将您的类型 T 也声明为 covariant

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

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

现在,ItemManagerPictures 可分配给 IItemManager<Item>,因此您的代码将毫无例外地工作。

从你的代码中你似乎事先知道你想要什么类型的 ItemManager,所以你可以这样做:

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();
    }
}

并供使用:

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

我想要一张IItemManager的图片

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

然后我想要一个 IItemManager 的视频

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