【问题标题】:Passing arguments to a specific subclass, through a factory method通过工厂方法将参数传递给特定的子类
【发布时间】:2009-05-13 00:53:50
【问题描述】:

假设我有一个抽象类 Drink,以及一个工厂方法,它选择在运行时创建的饮料类型(葡萄酒、啤酒等)。

每个 Drink 都需要一些参数来正确初始化自己。其中一些是所有饮料共有的;例如,它们可能都需要 DrinkConfig 参数。

但每种饮品也可能有其独特的要求。也许 Wine 需要一个侍酒师辅助对象来初始化自己。 Beer 不需要它,但它可能需要它自己的辅助对象。

那么我应该将什么传递给工厂方法?当我调用它时,我拥有所有可用的辅助对象,所以我可以将它们全部传递给工厂。但这最终可能会引发很多争论。有没有更好的设计方法?

编辑:假设我不能只在工厂中创建辅助对象;它们只能从调用者那里获得。

【问题讨论】:

    标签: oop subclass factory


    【解决方案1】:

    我会在你的工厂类中创建不同的重载方法。

    public class DrinkFactory {
    
        public static Drink CreateBeer(DrinkConfig config, string hops) {
            return new Beer(config, hops);
        }
    
        public static Drink CreateWine(DrinkConfig config, string grapes, int temperature) {
            return new Wine(config, grapes, temperature);
        }
    }
    

    编辑:

    如果希望在 Factory 类中只有一个方法,则替代实现将是:

    public enum DrinksEnum {
        Beer,
        Wine
    }
    
    public class DrinkFactory {
    
        public static Drink CreateDrink(DrinksEnum drinkType, DrinkConfig config) {
            switch(drinkType) {
                case DrinksEnum.Beer:
                    return new Beer(config);
                case DrinksEnum.Wine:
                    return new Wine(config);
                default:
                    throw new ApplicationException("Drink type not recognised.");
            }
        }
    }
    

    【讨论】:

    • 签名没问题。问题是你如何将参数传递给你的 Factory::CreateDrink() (或任何它被调用的)。
    • 如果你想在工厂中有一个 CreateDrink 方法,你可以使用一个枚举参数来指定你想要的饮料类型。我不相信或者上面的方法符合 GoF 工厂模式,其中对象是在 Drink 类中创建的(我今晚需要检查我的书),但我发现它更加实用,它仍然保持主要好处为子类层次结构集中创建对象。
    • 在查看了我的 GoF 设计模式书后,我很高兴我上面提供的示例与您使用工厂作为抽象工厂设计模式的一部分的方式非常接近。为了完全符合要求,应该有一个 DrinkFactory 继承自的抽象工厂类,但对于像这样的简单情况,我通常将其省略。如果需要另一个具体工厂,重构 DrinkFactory 就很容易了。
    • 不幸的是,这对我的情况不起作用。具体类型是在运行时选择的,所以我不能选择调用特定的 CreateBeer 或 CreateWine 方法。我只是调用一个通用的 CreateDrink 方法,它返回一个 Beer 或 Wine。
    • 不幸的是,您的编辑示例没有回答他的问题:如何处理不同的签名?
    【解决方案2】:

    工厂方法应该抽象出如何创建值的细节。所以你不应该将辅助对象传递给工厂方法,工厂方法应该创建它需要的辅助对象并将其传递给适当的构造函数。

    【讨论】:

      【解决方案3】:

      工厂应该首先创建非常相似的对象。这意味着即使所有这些对象都是饮料,工厂方法也可能不合适,因为每种饮料都与另一种完全不同。

      话虽如此,您可以改为传递一个大小等于您要设置的属性数量的对象列表。然后,每个对象将代表您要在相应对象的构造函数中设置的值,按照您要设置这些变量的顺序。这样做的缺点是你必须在调用之前在工厂之外格式化一个 List,这有点笨拙。

      【讨论】:

        【解决方案4】:

        我很想提供一个简单的解决方案,其中您的成分源自基类“DrinkIngredients”。您必须匹配要用于特定饮料的子类。

        显然,您可能很想为原料创建另一个工厂——但这会导致先有鸡还是先有蛋的问题。

        【讨论】:

          【解决方案5】:

          通常,存在工厂方法来隐藏这些细节。一个重要的问题是侍酒师从哪里来——如果所有这些其他助手都是单例的或者可以从已知来源获得,那么实例化工厂所需的信息以找到它们,这样您的调用代码就不需要担心它。

          此外,在许多情况下,可以使用 Spring 等框架来让您在配置文件中而不是在代码中描述这些关系。

          如果您确实需要在运行时从调用代码中传递帮助程序,我建议您阅读论文“参数和结果”(http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.50.7565),它描述了编组复杂参数的常见模式。本质上,您将创建必要参数的中间集合并将其传递给工厂。

          【讨论】:

            【解决方案6】:

            在这种情况下,我通常会寻找其他解决方案而不是传递变量。

            例如,在您的情况下 - WineFactory 需要侍酒师,因此它可以构建适当的 Wine -

            这是运行时依赖注入的一个很好的用例。某种形式的依赖注入框架将使这项工作变得非常简单、易于理解并且只是一种工作,而无需传递所有这些属性。

            【讨论】:

              【解决方案7】:

              这看起来是Builder 模式的完美案例。使用Factory 模式来创建相似的对象,使用Builder 模式来构造复杂的、不同的对象。尝试对这个问题使用Factory 模式将导致针对不同对象的许多不同的初始化构造函数(具有不同数量/类型的参数)。

              【讨论】:

                猜你喜欢
                • 2012-08-29
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2023-02-08
                • 1970-01-01
                • 2021-02-13
                相关资源
                最近更新 更多