【问题标题】:How to keep a class from being instantiated outside of a Factory如何防止类在工厂之外实例化
【发布时间】:2013-10-14 07:18:18
【问题描述】:

我有一个工厂。我不想让这个工厂生产的类在工厂之外被实例化。如果我让它们抽象、静态或给它们私有构造函数,那么它们根本就不能实例化!这是语言限制还是什么?

我不想允许这个

var awcrap = new Extrude2013 (); // BAD !!!
awcrap.extrudify (); // I don't want to allow this

其余代码:

using System;

namespace testie
{
    public enum ExtrudeType { Extrude2013,  Extrude2014 }

    public interface IExtrudeStuff {
        void extrudify();
    }

    public class Extrude2013 : IExtrudeStuff { 
        public void extrudify(){ 
            Console.WriteLine ("extrudify 2013");
        }
    }

    public class Extrude2014 : IExtrudeStuff { 
        public void extrudify(){ 
            Console.WriteLine ("extrudify 2014");
        }
    }
    public static class ExtrudeFactory {
        public static IExtrudeStuff Create(ExtrudeType t) {
            switch (t) {
                case ExtrudeType.Extrude2013: return new Extrude2013 ();
                case ExtrudeType.Extrude2014: return new Extrude2014 ();
                default: return null; 
            } 
        }
    }

    class MainClass {
        public static void Main (string[] args) {
            // Now for the pretty API part
            var o = ExtrudeFactory.Create (ExtrudeType.Extrude2013);
            o.extrudify ();
            var p = ExtrudeFactory.Create (ExtrudeType.Extrude2014);
            p.extrudify ();

            var awcrap = new Extrude2013 (); // BAD !!!
            awcrap.extrudify (); // I don't want to allow this
        }
    }
}

【问题讨论】:

  • 不可能。 new 关键字将允许实例化。
  • 为什么不在工厂类中将它们设为私有嵌套类?
  • 您在寻找内部人员吗?
  • @mikez 嵌套类可能会让人眼花缭乱。
  • 或者内部构造函数可以工作。然后你的程序集之外的任何人都无法实例化它们。

标签: c# factory


【解决方案1】:

您不能完全禁止这样做。是否是语言“限制”是一个见仁见智的问题,但您可以考虑以下事项:

  • 使构造函数internal。这将允许声明程序集中的任何类型调用构造函数,但在程序集之外没有任何内容。这意味着您在该程序集中编写的任何代码都负责调用工厂,这也意味着您不能在另一个程序集中声明该类的子类型,因为它将无法调用构造函数。
  • 类似的方法是让你公开的类抽象(或接口),然后声明一个internal(甚至private作为工厂的子类,因为它会永远不会在工厂之外引用)实现抽象类或接口的类型。
  • 需要一个只有工厂才能在构造函数中提供的令牌。这就是DataTable 类的工作方式。虽然构造函数仍然可以调用,但用户必须传入null 作为值,而且至少很明显他们不应该这样做。

【讨论】:

  • RequireToken 如何保证类型安全?还是由编译器强制执行? RequiredToken 的类型是什么?
  • @dave:它不可能是必需的,因为用户仍然可以传递null。但它会在运行时失败,并且对于编写代码的人来说应该很明显,他们不应该调用它。
  • @dave:还要注意,如果您使用的类都在同一个程序集中,那么前两种方法会更可取。仅当您需要允许在定义程序集之外定义子类时,才需要最后一种方法。
  • 似乎嵌套子类是编译器强制执行此操作的唯一方法。
  • 您始终可以将工厂类定义为分部类,并且仍然在单独的文件中实现嵌套的子类。
【解决方案2】:

Factory Pattern 的全部意义在于只有工厂知道如何选择和制作对象,并且它只通过接口而不是具体类公开实例化对象的功能。将对象的构造函数设为私有会失败,因为Factory 本身无法实例化它。

解决方案:

1- 定义一个interface 类,所有类型的Extrude20XX 类都实现它,例如IExtrudeStuff

2- 将 Extrude20XX 类包装在 Factory 类中作为私有嵌套类。

3- 在所有ExtrudeXX 类中实现接口IExtrude

4- 编写一个(静态)Create (t) 方法,例如:

public static class ExtrudeFactory {
 public static IExtrudeStuff Create(ExtrudeType t) {
 {
   switch (t) {
       case ExtrudeType.Extrude2013: return new Extrude2013 ();
       case ExtrudeType.Extrude2014: return new Extrude2014 ();
       default: return null; 
   } 
 }
}

【讨论】:

  • 这似乎是解决方案,嵌套子类。最好为 Extrude2013 之类的类(无法实例化)提供单独的文件,然后在工厂内为那些 可以 实例化的类提供属性
  • @dave 虽然这是一个很好的做法,但将类分布在不同的文件中不会改变任何东西,除非它们在同一个程序集中。主题是它们在Factory 类中应该是私有的。这样Factory 类可以是部分的,并且可以扩展到具体的类。
  • 是的,完全不会改变代码的操作语义,但区别在于可读性
  • @dave 和可维护性:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-11
  • 2015-06-09
  • 2019-07-05
  • 2017-10-30
相关资源
最近更新 更多