C#中的工厂模式实现

5
我正在实现以下工厂模式:

我正在实现以下工厂模式。

public class FeedFactory
{
    #region Singleton Pattern
    //..
    #endregion

    private static Feed[] _factory = new Feed[(int)FeedType.Total];

    public void RegisterFeed(FeedType feedType,Feed feed)
    {
        if (_factory[(int)feedType] == null)
        {
            _factory[(int)feedType] = feed;
        }
        else
        {
            // already registered
        }
    }

    public Feed GetFeed(FeedType feedType)
    {
        return _factory[(int)feedType];
    }
}

在这里,Feed 是一个抽象类,其他不同的类都是从它继承而来。我该如何注册这些不同的类?能否在它们的构造函数中进行注册?


如果在else子句中什么也不做,最好将其删除,这样可以使代码更易读。 - Jonas Van der Aa
请查看此答案:https://dev59.com/zlLTa4cB1Zd3GeqPa3_I#4387761 - Steven
5个回答

8
这不是工厂模式。工厂总是会有一些构造逻辑,至少一个new。这就是工厂的理念:调用者不必担心对象如何创建。这是一个单例存储库。
首先,您应该使用类型索引字典,而不是数组。
private static Dictionary<Type, Feed> _singletons = new Dictionary<Type, Feed>();

接下来,您不需要注册方法。当您检索单例时,字典应该自动填充。

现在我假设您的Feed类具有没有参数的默认构造函数。在这种情况下,您可以直接从抽象类Feed实现工厂方法。我们将在这里使用一些泛型,因为它允许您控制继承:

public abstract class Feed
{
    public static T GetInstance<T>() where T:Feed, new()
    {
        T instance = new T();
        // TODO: Implement here other initializing behaviour
        return instance;
    }
}

现在回到你的单例仓库。
public class FeedSingletonRepository
{
    private static readonly object _padlock = new object();
    private static Dictionary<Type, Feed> _singletons = new Dictionary<Type, Feed>();

    public static T GetFeed<T>() where T:Feed
    {
        lock(_padlock)
        {
             if (!_singletons.ContainsKey(typeof(T))
             {
                 _singletons[typeof(T)] = Feed.GetInstance<T>();
             }
             return (T)_singletons[typeof(T)];
        }
    }
}

请注意,我包含了线程安全的行为。当您使用单例模式时,这是一个好习惯。
现在,如果您想要获取从Feed继承的给定类型的单例(让我们称之为SpecializedFeedType),您只需要执行以下操作:
var singleton = FeedSingletonRepository.GetFeed<SpecializedFeedType>();

或者

SpecializedFeedType singleton = FeedSingletonRepository.GetFeed();

这与稍有不同的语法是同一行。

编辑2:更正了一些语法错误。


当您调用 typeof(T).GetInstance() 时,GetInstance() 调用不应该来自 System.Type,而应该来自类本身,对吗? - Aks
是的,您正在按类型索引实例数组。这样,您就可以获得关系类型=>实例,其中实例是给定类型的单例。 - Evren Kuzucuoglu
嗯,其实你说得对!我现在正在研究它。 - Evren Kuzucuoglu
写成 T.GetInstance() 会更好,因为该方法是静态的,但它不起作用。我正在尝试找到一种解决方法! - Evren Kuzucuoglu
很好的解释,Evren。加一! - ouflak
显示剩余2条评论

6

顺便提一下,工厂模式的目的是封装创建过程,但是你创建对象并将其注册到工厂中,这是一个有趣的选择。这是否更像是对象存储库而不是工厂,或者这个类还有其他我没有看到的功能?

如果这是一个对象存储库,那么你可能会在其他问题中找到一些额外的灵感,比如这个问题


1
通常工厂会创建对象--一个简单的实现方式是有一个工厂接口和一个Create方法,以及可以创建特定类型的工厂类的具体实现。虽然有一些变化,但关键是它为您处理了创建过程。如果您想要重用它们,它也可以像您正在做的那样缓存对象实例,但是这时您可能明确地想要将工厂和缓存/存储库方面分开。这取决于您想要实现什么目标 :) 如果您现在正在探索,我建议先找到满足您目标的东西。 - Tim Barrass

1

只需注册您想要创建的类的类型,然后使用Activator.CreateInstance来创建该类型的实例。

它应该按照以下方式工作:

private static Type[] _factory = new Type[(int)FeedType.Total];

public void RegisterFeed(FeedType feedType, Type type)
{
  ...
  _factory[(int)feedType] = type;
  ...
}

public Feed GetFeed(FeedType feedType)
{
    return Activator.CreateInstance(_factory[(int)feedType]) as Feed;
}

你可以像下面这样调用RegisterFeed函数:
RegisterFeed(FeedType.SomethingSpecial, typeof(MyDerivedSpecialFeed));

1
当您调用RegisterFeed方法时,需要传递一个Feed类的具体实例。因此,提供具体实现的责任在于调用者。

那么调用方是不是“feed”的一种类型? - Aks
调用者可以是任何东西。当它调用RegisterFeed方法时,需要传递一个特定的Feed实现。 - Darin Dimitrov
假设我有一个 class FeedTypeA : Feed。如果我在 FeedTypeA 中声明一个公共静态对象 FeedTypeA,并将其注册在构造函数中,那么这样会起作用吗? - Aks
是的,它会起作用,你可以将“this”传递给“RegisterFeed”方法。 - Darin Dimitrov

0
class FeedFactory {


    public IFeedFactory GetFeedFactory(string type) {
       switch(type) {
          case "1": return new Feed1(); break;
          case "2": return new Feed2(); break;
       }

    }

}

注意所有提要均必须实现IFeedFactory接口并实现所需方法。
//来自客户端
FeedFactory ff1 = new FeedFactory();
IFeedFactory obj = ff1.GetFeedFactory("1");
obj.ExecuteMethod();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接