如何将新的派生类型添加到工厂模式?

时间:2011-07-25 22:27:57

标签: c# oop design-patterns

给定基本类型A

三种派生类型 B:A C:A D:A

我有一个项目P. 我想要一个List来表示P中的A的出现。

有一种从P中提取B的策略,我想成为B类的一部分。 有一种从P中提取Cs的策略,我想成为C类的一部分。 等等 我希望他们都列在一个大清单中。

我想稍后能够添加一个E:A类,尽可能少地触摸。在接受P的实例上有一个虚拟的静态Factory方法作为参数会很酷,它会多态运行派生类中的所有静态重载,例如C中的重载将从P中提取C并将它们放入List中。 当然,C#中不存在虚拟静态方法。

我无法立即看到如何实现这一点,以便可以添加类D:A和E:A而不触及基类或一些不断更新的“上帝方法”工厂方法,具体依赖于每个派生类型。

这有点晚了,所以我可能会遗漏一些明显的东西。 你的想法?

编辑:

我的具体情况是我有一个由控制模块组成的过程工厂控制系统。我希望能够识别某些更高级别的构造,例如控制循环,反馈调整等。识别和管理这些构造的逻辑特定于所讨论的单个构造类型。简而言之,我想捆绑用于识别和处理构造的代码。

让我们考虑一个类比。我有一份文本文件。其中有各种类型的单词。 Word是“基础”类型“A”。文本文档是“P”项目。我可以实现单词类型“名词”和“动词”。在文本中识别“名词”的方式特定于“名词”而代码应该是。随着越来越多的类型被实现并因此在文本中被识别,列表变得更长。

对我来说,在名词类中实现它是有意义的:

 static function IEnumerable<noun> IdentifyAll (P project)

做一个

CompleteWordList.AddRange(noun.IdentifyAll(p));

在初始化期间,但这会从中央初始化/工厂方法创建对特定“名词”类型的依赖关系。然后很难在不触及它的情况下添加更多单词类。在我写这篇文章时,我觉得自己有点倾向于MEF。

这是一个简单的例子。也许“短语”或某种东西对于基类型来说是更合适的类比,但它现在必须要做。

尽管这些项目是具有可检查属性的连接控制节点的网络(而不是文本文档),但它非常像一个解析器。如果有一些普遍的解决方案让我望而却步,那将是件好事。

2 个答案:

答案 0 :(得分:1)

好的,我想我现在明白了。如果我理解,你想要从A派生一个类型的对象,并确定它的实际类型(比如B),但工厂不知道如何将其归类为B 。然后,您应该能够在将来引入任意数量的A子类型,而无需为每种新类型修改工厂。

有人知道如何从AB。如果它不是工厂,那么它本身必须是B。我看到你沿着这条路走下去并且想“我需要一个虚拟的工厂方法,这样A的每个子类型都可以提供规范来识别自己。”

正如您所说,虚拟静态方法不存在,因此您不能按照自己的想法去做。但为什么不使用实例方法?

只要项目包含所有已知子类型的列表,它就可以简单地遍历这些类型,创建它们的实例并询问它们“这个对象是你们中的一个吗?”。如果它说“是”,那么你可以问它“给我一个等同于这个基本类型实例的实例”。 (这听起来效率低;当然,在实践中你可以保持每种类型的一个“工厂实例”活着,这样你就不必继续重新创建它们。)

一个人为的例子,使用类似你的单词:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;

interface IWord
{
    bool IsTypeOf(IWord word);
    string Text { get; set; }
    IWord MakeFrom(IWord word);
}

// the base type
class UntypedWord : IWord
{
    public virtual string Text { get; set; }
    public virtual bool IsTypeOf(IWord word)
    {
        throw new NotImplementedException();
    }
    public virtual IWord MakeFrom(IWord word)
    {
        throw new NotImplementedException();
    }
}

// one specific subtype
class ColorWord : UntypedWord
{
    public Color Color { get; private set; }

    public override bool IsTypeOf(IWord word)
    {
        return word.Text == "red" || word.Text == "green" || word.Text == "blue";
    }

    public override IWord MakeFrom(IWord word)
    {
        var newMe = new ColorWord();
        newMe.Text = word.Text;
        if (word.Text == "red") newMe.Color = Color.Red;
        else if (word.Text == "blue") newMe.Color = Color.Blue;
        else if (word.Text == "yellow") newMe.Color = Color.Yellow;
        return newMe;            
    }
}

// another specific type
class NumberWord : IWord // note: not an UntypedWord (see comments below)
{
    public int Number { get; set; }
    public string Text { get; set; }

    public bool IsTypeOf(IWord word)
    {
        return word.Text == "one" || word.Text == "two" || word.Text == "three";
    }

    public IWord MakeFrom(IWord word)
    {
        var newMe = new NumberWord();
        newMe.Text = word.Text;
        if (word.Text == "one") newMe.Number = 1;
        else if (word.Text == "two") newMe.Number = 2;
        else if (word.Text == "three") newMe.Number = 3;
        return newMe;
    }
}


class WordList
{
    Collection<Type> WordTypes = new Collection<Type>();
    Collection<IWord> UntypedWords = new Collection<IWord>();
    Dictionary<Type, Collection<IWord>> StronglyTypedWords = new Dictionary<Type, Collection<IWord>>();

    public void AddWordType<T>() where T : IWord
    {
        WordTypes.Add(typeof(T));

        if (!StronglyTypedWords.ContainsKey(typeof(T)))
            StronglyTypedWords[typeof(T)] = new Collection<IWord>();
    }

    public void Add(IWord word)
    {
        bool foundType = false;
        foreach (Type type in WordTypes)
        {
            // in practice you'd cache these factories for efficiency
            IWord instance = Activator.CreateInstance(type) as IWord;
            if (instance.IsTypeOf(word))
            {
                if (!StronglyTypedWords.ContainsKey(type))
                    StronglyTypedWords[type] = new Collection<IWord>();

                StronglyTypedWords[type].Add(instance.MakeFrom(word));
                foundType = true;
            }
        }
        if (!foundType)
            UntypedWords.Add(word);
    }

    public int HowManyWordsOfType<T>()
    {
        if (StronglyTypedWords.ContainsKey(typeof(T)))
            return StronglyTypedWords[typeof(T)].Count;
        return 0;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var sentence = new WordList();
        sentence.AddWordType<ColorWord>();
        sentence.AddWordType<NumberWord>();

        sentence.Add(new UntypedWord { Text = "two" });
        sentence.Add(new UntypedWord { Text = "green" });
        sentence.Add(new UntypedWord { Text = "frogs" });
        sentence.Add(new UntypedWord { Text = "and" });
        sentence.Add(new UntypedWord { Text = "one" });
        sentence.Add(new UntypedWord { Text = "red" });
        sentence.Add(new UntypedWord { Text = "rose" });

        Console.WriteLine("color words: " + sentence.HowManyWordsOfType<ColorWord>());
        Console.WriteLine("number words: " + sentence.HowManyWordsOfType<NumberWord>());
    }
}    

输出:

color words: 2
number words: 2

现在,当您想要添加新的单词类型时,您必须添加的唯一代码是:

sentence.AddWordType<NewWordType>();

您会注意到ColorWordUntypedWord的子类型,但NumberWord不是。只要它们实现IWord,Word类型就不必共享公共基类型。

显然这是一个荒谬的例子,但它显示了每个子类型如何拥有基于公共超类型(或公共接口)的给定对象的属性对其自身进行分类的逻辑,并且它知道如何基于此创建自身的实例给定的对象。

现在,WordList(您的P)类永远不必更新以适应新的单词类型。您只需要在运行时告诉它所有类型。

你可以编写IdentifyAll(WordList sentence)方法,只有它是实例方法而不是静态方法。

答案 1 :(得分:0)

所以这里真正的问题是如何在不知道派生类型存在的情况下创建派生类型的实例;但是这个问题的重要之处在于会导致创建以前未知的派生类型的逻辑是什么。也就是说,必须有一些逻辑来定义一个新的状态,以前未知的派生类型是合适的;但是,你希望工厂事先没有意识到这个逻辑。

实际上,您的工厂将成为一个相关系统,它会在要满足的条件和要创建的类型之间进行相关描述;然后所有工厂都会读取配置(或利用其Injected配置)并执行定义的任何逻辑以确定要使用的创建代码。它是一个工厂作为数据驱动的对象。 Spring做了与此非常相似的事情; Spring的配置决定了对象和适合注入的类型之间的相互关系,而Spring只是按照它所说的去做。这种类型的系统很复杂,但是非常易于扩展。