How to keep a class from being instantiated outsid

2019-02-22 00:35发布

问题:

I have a Factory. I do not want to allow classes that this factory produces to be instantiated outside of the factory. If I make them abstract, static, or give them private constructors then they won't be instantiable at all! Is this a language restriction or what?

I don't want to allow this

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

Rest of code:

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
        }
    }
}

回答1:

You can't completely disallow this. Whether or not it's a language "restriction" would be a matter of opinion, but there are things that you could consider:

  • Make the constructor internal. This will allow any type within the declaring assembly to call the constructor, but nothing outside the assembly. This would mean that any code you write in that assembly to be responsible for calling the factory, and it also means that you could not declare subtypes of the class in another assembly, since it would be unable to call the constructor.
  • A similar approach would be to make the class you expose abstract (or an interface), then declare an internal (or even private as a subclass of the factory, since it would never be referenced outside of the factory) type that implements the abstract class or interface.
  • Require a token that only the factory can provide in the constructor. This is how the DataTable class works. While the constructor could still be called, the user would have to pass in null for the value and it would at least be obvious that they shouldn't be doing this.


回答2:

The whole point of Factory Pattern is that only the Factory knows how to choose and make an object and it only exposes the instantiated object's functionality through an interface not a concrete class. Making the object's constructor private fails because Factory itself cannot instantiate it.

Solution:

1- Define an interface class which all types of the Extrude20XX classes implement it such as IExtrudeStuff.

2- Wrap the Extrude20XX classes inside the class of Factory as private nested classes.

3- Implement the interface IExtrude in all the ExtrudeXX classes.

4- Write a (static) Create (t) method like:

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; 
   } 
 }
}


标签: c# factory