如何使类工厂创建所需的派生类

Posted

技术标签:

【中文标题】如何使类工厂创建所需的派生类【英文标题】:How to make a class factory to create the required derived class 【发布时间】:2013-02-20 10:12:32 【问题描述】:

我经常使用类工厂模式,其中一个类有一个私有构造函数和一个静态方法来创建类。这允许由于某种原因无法构造类并返回 null 的情况 - 非常方便。

我希望能够将其扩展到工厂方法,该方法根据条件从派生类的层次结构中创建特定类。但是我看不到隐藏派生类的构造函数以强制使用工厂方法的方法。如果工厂方法在基类中,则它不再有权访问派生类的私有构造函数。在每个派生类中放置工厂方法是行不通的,因为必须事先知道所需的类型。如果一个类可以访问嵌套类的私有成员,则嵌套类可能是一种方法,但遗憾的是,嵌套类似乎可以访问封闭类的私有成员,但反之则不行。

有人知道这样做的方法吗?

【问题讨论】:

【参考方案1】:

有几种可能,其中两种是:

    将所有这些类放在一个项目中,并将构造函数设为internal。其他项目将无法调用这些构造函数,但该项目中的代码可以。 使这些类的构造函数protected(而不是private)并在包含工厂方法的类中创建一个私有派生类。创建该私有类的实例并将其返回。

第二个选项的示例:

public static class AnimalFactory

    public static Animal Create(int parameter)
    
        switch(parameter)
        
            case 0:
                return new DogProxy();
            case 1:
                return new CatProxy();
            default:
                throw new ArgumentOutOfRangeException("parameter");
        
    

    private class DogProxy : Dog  

    private class CatProxy : Cat  


public abstract class Animal  

public class Dog : Animal

    protected Dog()  


public class Cat : Animal

    protected Cat()  

【讨论】:

丹尼尔和马修。真是聪明。它工作并满足所有要求。我喜欢。我只是不知道谁的答案被接受:-) @Dave:你必须自己决定。通常你会接受对你帮助最大的答案。如果有多个答案对您有同样的帮助,您要么不接受,要么接受您更喜欢的答案或最先发布的答案。【参考方案2】:

这是丹尼尔发布答案时我正在处理的示例代码。看起来它正在按照他的建议做:

public static class BaseFactory

    public static Base Create(bool condition)
    
        if (condition)
        
            return Derived1.Create(1, "TEST");
        
        else
        
            return Derived2.Create(1, DateTime.Now);
        
    


public class Base

    protected Base(int value)
    
    

    protected static Base Create(int value)
    
        return new Base(value);
    


public sealed class Derived1: Base

    private Derived1(int value, string text): base(value)
    
    

    internal static Derived1 Create(int value, string text)
    
        return new Derived1(value, text);
    


public sealed class Derived2: Base

    private Derived2(int value, DateTime time): base(value)
    
    

    internal static Derived2 Create(int value, DateTime time)
    
        return new Derived2(value, time);
    

[编辑] 对于丹尼尔的第二个建议:

public static class BaseFactory

    public static Base Create(bool condition)
    
        if (condition)
        
            return new Derived1Creator(1, "TEST");
        
        else
        
            return new Derived2Creator(1, DateTime.Now);
        
    

    private sealed class Derived1Creator: Derived1
    
        public Derived1Creator(int value, string text): base(value, text)
        
        
    

    private sealed class Derived2Creator: Derived2
    
        public Derived2Creator(int value, DateTime time): base(value, time)
        
        
    


public class Base

    protected Base(int value)
    
    

    protected static Base Create(int value)
    
        return new Base(value);
    


public class Derived1: Base

    protected Derived1(int value, string text): base(value)
    
    

    protected static Derived1 Create(int value, string text)
    
        return new Derived1(value, text);
    


public class Derived2: Base

    protected Derived2(int value, DateTime time): base(value)
    
    

    protected static Derived2 Create(int value, DateTime time)
    
        return new Derived2(value, time);
    

请注意,第二种方法意味着类不能被密封,很遗憾。

【讨论】:

丹尼尔和马修。真是聪明。它工作并满足所有要求。我喜欢。我只是不知道谁的答案被接受:-) 马修,第一个代码不是我的第一个选项的意思。正如我所说,将 constructor 设为internal。使用额外的工厂方法绕道而行不会增加任何好处。 是的,很公平。它只是为了演示使用工厂方法 - 尽管在这种情况下工厂方法非常简单,它不会隐藏任何东西。在一般情况下,它可以做到。 @Dave:Daniel 是第一个发布代码的,所以如果其他因素相同,您应该勾选他的。 :)【参考方案3】:

而不是将类本身内部的方法用作工厂,而是通过静态类(“工厂”)实现工厂模式,该类根据您编写的逻辑返回正确的实例。

【讨论】:

这并不能解决问题,即:“阻止用户通过new创建实例”。但我同意建筑应该留给第三类。基类不应该知道派生自它的类。 @DanielHilgarth 你是对的,因为他应该将另一个项目中的类型分离为内部类型并通过接口公开它们。 请重新阅读问题。我引用:“但是我看不到隐藏派生类的构造函数以强制使用工厂方法的方法”。【参考方案4】:

您可以在基类构造器中拦截派生类型的创建,并使用 StackFrames 检查调用者是否是您的工厂:

 protected Class1() //base class ctor
    
        StackFrame[] stackFrames = new StackTrace().GetFrames(); 
        foreach (var frame in stackFrames)
        
            //check caller and throw an exception if not satisfied
        
    

【讨论】:

以上是关于如何使类工厂创建所需的派生类的主要内容,如果未能解决你的问题,请参考以下文章

C++ 多重继承

抽象类和接口

C ++使派生类只能从工厂/基类实例化

C++ 将派生类的成员更改为其专用版本

《Head First 设计模式》之工厂模式

简单工厂模式