variable usar son qué modificador metodos metodo los estáticos estaticos estaticas cuando clases clase atributos c# design-patterns oop factory

usar - Patrón de fábrica en C#: ¿Cómo asegurar que una instancia de objeto solo pueda ser creada por una clase de fábrica?



qué son los atributos de clase estáticos (16)

Además de lo que Jon sugirió, también podría tener el método de fábrica (incluido el cheque) como método estático de BusinessObject en primer lugar. Luego, haga que el constructor sea privado, y todos los demás se verán obligados a usar el método estático.

public class BusinessObject { public static Create (string myProperty) { if (...) return new BusinessObject (myProperty); else return null; } }

Pero la verdadera pregunta es: ¿por qué tienes este requisito? ¿Es aceptable mover la fábrica o el método de fábrica a la clase?

Recientemente he estado pensando en proteger algunos de mis códigos. Tengo curiosidad de cómo uno puede asegurarse de que un objeto nunca se pueda crear directamente, sino solo a través de algún método de una clase de fábrica. Digamos que tengo alguna clase de "objeto comercial" y quiero asegurarme de que cualquier instancia de esta clase tenga un estado interno válido. Para lograr esto, tendré que realizar algunas comprobaciones antes de crear un objeto, probablemente en su constructor. Todo está bien hasta que decida que quiero que esta verificación forme parte de la lógica comercial. Entonces, ¿cómo puedo hacer para que un objeto de negocio sea creable solo a través de algún método en mi clase de lógica de negocios, pero nunca directamente? El primer deseo natural de usar una buena palabra clave de "amigo" de C ++ no será suficiente con C #. Entonces necesitamos otras opciones ...

Vamos a probar algunos ejemplos:

public MyBusinessObjectClass { public string MyProperty { get; private set; } public MyBusinessObjectClass (string myProperty) { MyProperty = myProperty; } } public MyBusinessLogicClass { public MyBusinessObjectClass CreateBusinessObject (string myProperty) { // Perform some check on myProperty if (true /* check is okay */) return new MyBusinessObjectClass (myProperty); return null; } }

Todo está bien hasta que recuerdes que todavía puedes crear la instancia MyBusinessObjectClass directamente, sin verificar la entrada. Me gustaría excluir esa posibilidad técnica por completo.

Entonces, ¿qué piensa la comunidad sobre esto?


Aquí hay otra solución en la línea de "solo porque no puede significar que debería" ...

Cumple con los requisitos de mantener privado al constructor de objetos comerciales y colocar la lógica de fábrica en otra clase. Después de eso, se pone un poco superficial.

La clase de fábrica tiene un método estático para crear objetos comerciales. Se deriva de la clase de objeto comercial para acceder a un método de construcción protegido estático que invoca el constructor privado.

La fábrica es abstracta, por lo que no se puede crear una instancia (porque también sería un objeto comercial, por lo que sería extraño) y tiene un constructor privado, por lo que el código del cliente no puede derivarse de ella.

Lo que no se evita es que el código del cliente también se derive de la clase de objeto comercial y llame al método de construcción estático protegido (pero no validado). O peor, llamar al constructor predeterminado protegido que tuvimos que agregar para obtener la clase de fábrica para compilar desde el principio. (Que, por cierto, es probable que sea un problema con cualquier patrón que separe la clase de fábrica de la clase de objeto de negocio).

No estoy tratando de sugerir que alguien en su sano juicio haga algo como esto, pero fue un ejercicio interesante. FWIW, mi solución preferida sería usar un constructor interno y el límite de ensamblaje como guardia.

using System; public class MyBusinessObjectClass { public string MyProperty { get; private set; } private MyBusinessObjectClass(string myProperty) { MyProperty = myProperty; } // Need accesible default constructor, or else MyBusinessObjectFactory declaration will generate: // error CS0122: ''MyBusinessObjectClass.MyBusinessObjectClass(string)'' is inaccessible due to its protection level protected MyBusinessObjectClass() { } protected static MyBusinessObjectClass Construct(string myProperty) { return new MyBusinessObjectClass(myProperty); } } public abstract class MyBusinessObjectFactory : MyBusinessObjectClass { public static MyBusinessObjectClass CreateBusinessObject(string myProperty) { // Perform some check on myProperty if (true /* check is okay */) return Construct(myProperty); return null; } private MyBusinessObjectFactory() { } }


Después de tantos años me preguntaron esto, y todas las respuestas que veo desafortunadamente te dicen cómo debes hacer tu código en lugar de dar una respuesta directa. La respuesta real que buscaba era tener sus clases con un constructor privado pero un instalador público, lo que significa que solo puede crear instancias nuevas a partir de otras instancias existentes ... que solo están disponibles en la fábrica:

La interfaz para tus clases:

public interface FactoryObject { FactoryObject Instantiate(); }

Tu clase:

public class YourClass : FactoryObject { static YourClass() { Factory.RegisterType(new YourClass()); } private YourClass() {} FactoryObject FactoryObject.Instantiate() { return new YourClass(); } }

Y, finalmente, la fábrica:

public static class Factory { private static List<FactoryObject> knownObjects = new List<FactoryObject>(); public static void RegisterType(FactoryObject obj) { knownObjects.Add(obj); } public static T Instantiate<T>() where T : FactoryObject { var knownObject = knownObjects.Where(x => x.GetType() == typeof(T)); return (T)knownObject.Instantiate(); } }

Luego puede modificar fácilmente este código si necesita parámetros adicionales para la instanciación o para preprocesar las instancias que crea. Y este código le permitirá forzar la creación de instancias a través de la fábrica ya que el constructor de la clase es privado.


En un caso de buena separación entre interfaces e implementaciones,
protected-constructor-public-initializer pattern permite una solución muy ordenada.

Dado un objeto comercial:

public interface IBusinessObject { } class BusinessObject : IBusinessObject { public static IBusinessObject New() { return new BusinessObject(); } protected BusinessObject() { ... } }

y una fábrica de negocios:

public interface IBusinessFactory { } class BusinessFactory : IBusinessFactory { public static IBusinessFactory New() { return new BusinessFactory(); } protected BusinessFactory() { ... } }

el siguiente cambio al inicializador BusinessObject.New() da la solución:

class BusinessObject : IBusinessObject { public static IBusinessObject New(BusinessFactory factory) { ... } ... }

Aquí se necesita una referencia a la fábrica comercial concreta para llamar al inicializador BusinessObject.New() . Pero el único que tiene la referencia requerida es la propia fábrica comercial.

Obtuvimos lo que queríamos: el único que puede crear BusinessObject es BusinessFactory .


Esta solución se basa en la idea munificents de usar un token en el constructor. Hecho en esta respuesta, asegúrese de que el objeto solo se haya creado de fábrica (C #)

public class BusinessObject { public BusinessObject(object instantiator) { if (instantiator.GetType() != typeof(Factory)) throw new ArgumentException("Instantiator class must be Factory"); } } public class Factory { public BusinessObject CreateBusinessObject() { return new BusinessObject(this); } }


No creo que haya una solución que no sea peor que el problema, todo lo que antecede requiere una fábrica pública estática que en mi humilde opinión es un problema peor y no impide que la gente llame a la fábrica para usar su objeto, no oculta nada. Lo mejor es exponer una interfaz y / o mantener el constructor como interno si puede, esa es la mejor protección ya que el ensamblado es un código de confianza.

Una opción es tener un constructor estático que registre una fábrica en algún lugar con algo así como un contenedor IOC.


No entiendo por qué quiere separar la "lógica comercial" del "objeto comercial". Esto suena como una distorsión de la orientación del objeto, y terminará atando en nudos tomando ese enfoque.


O, si desea ir realmente elegante, invierta el control: haga que la clase devuelva la fábrica e instrumente la fábrica con un delegado que pueda crear la clase.

public class BusinessObject { public static BusinessObjectFactory GetFactory() { return new BusinessObjectFactory (p => new BusinessObject (p)); } private BusinessObject(string property) { } } public class BusinessObjectFactory { private Func<string, BusinessObject> _ctorCaller; public BusinessObjectFactory (Func<string, BusinessObject> ctorCaller) { _ctorCaller = ctorCaller; } public BusinessObject CreateBusinessObject(string myProperty) { if (...) return _ctorCaller (myProperty); else return null; } }

:)


Otra opción (ligera) es hacer un método de fábrica estático en la clase BusinessObject y mantener el constructor privado.

public class BusinessObject { public static BusinessObject NewBusinessObject(string property) { return new BusinessObject(); } private BusinessObject() { } }


Parece que solo quieres ejecutar una lógica comercial antes de crear el objeto, así que ¿por qué no creas un método estático dentro de la "BusinessClass" que hace todo el trabajo sucio de "myProperty" y hace que el constructor sea privado?

public BusinessClass { public string MyProperty { get; private set; } private BusinessClass() { } private BusinessClass(string myProperty) { MyProperty = myProperty; } public static BusinessClass CreateObject(string myProperty) { // Perform some check on myProperty if (/* all ok */) return new BusinessClass(myProperty); return null; } }

Llamarlo sería bastante sencillo:

BusinessClass objBusiness = BusinessClass.CreateObject(someProperty);


Pondría la fábrica en el mismo ensamblaje que la clase de dominio y marca el constructor interno de la clase de dominio. De esta forma, cualquier clase de tu dominio puede crear una instancia, pero confías en que no, ¿verdad? Cualquier persona que escriba código fuera de la capa de dominio tendrá que usar su fábrica.

public class Person { internal Person() { } } public class PersonFactory { public Person Create() { return new Person(); } }

Sin embargo, debo cuestionar su enfoque :-)

Creo que si quieres que tu clase Person sea válida al momento de la creación, debes poner el código en el constructor.

public class Person { public Person(string firstName, string lastName) { FirstName = firstName; LastName = lastName; Validate(); } }


Por lo tanto, parece que lo que quiero no se puede hacer de una manera "pura". Siempre es una especie de "devolución de llamada" a la clase lógica.

Tal vez podría hacerlo de una manera simple, simplemente haga un método contructor en la clase de objetos primero llame a la clase lógica para verificar la entrada?

public MyBusinessObjectClass { public string MyProperty { get; private set; } private MyBusinessObjectClass (string myProperty) { MyProperty = myProperty; } pubilc static MyBusinessObjectClass CreateInstance (string myProperty) { if (MyBusinessLogicClass.ValidateBusinessObject (myProperty)) return new MyBusinessObjectClass (myProperty); return null; } } public MyBusinessLogicClass { public static bool ValidateBusinessObject (string myProperty) { // Perform some check on myProperty return CheckResult; } }

De esta manera, el objeto de negocio no se puede crear directamente y el método de comprobación pública en la lógica de negocios tampoco hará daño.


Puede hacer que el constructor de su clase MyBusinessObjectClass sea interno, y moverlo y la fábrica a su propio ensamblaje. Ahora solo la fábrica debería poder construir una instancia de la clase.


Puede hacer que el constructor sea privado y de fábrica un tipo anidado:

public class BusinessObject { private BusinessObject(string property) { } public class Factory { public static BusinessObject CreateBusinessObject(string property) { return new BusinessObject(property); } } }

Esto funciona porque los tipos anidados tienen acceso a los miembros privados de sus tipos adjuntos. Sé que es un poco restrictivo, pero espero que ayude ...


Se han mencionado múltiples enfoques con diferentes intercambios.

  • Anidar la clase de fábrica en la clase de construcción privada solo permite a la fábrica construir 1 clase. En ese punto, es mejor con un método Create y un ctor privado.
  • Usar herencia y un ctor protegido tiene el mismo problema.

Me gustaría proponer la fábrica como una clase parcial que contiene clases privadas anidadas con constructores públicos. Está ocultando al 100% el objeto que está construyendo su fábrica y solo está exponiendo lo que elige a través de una o múltiples interfaces.

El caso de uso que escuché para esto sería cuando quieres rastrear el 100% de las instancias en la fábrica. Este diseño no garantiza a nadie más que la fábrica tiene acceso a la creación de instancias de "productos químicos" definidos en la "fábrica" ​​y elimina la necesidad de un ensamblaje separado para lograr eso.

== ChemicalFactory.cs == partial class ChemicalFactory { private ChemicalFactory() {} public interface IChemical { int AtomicNumber { get; } } public static IChemical CreateOxygen() { return new Oxygen(); } } == Oxygen.cs == partial class ChemicalFactory { private class Oxygen : IChemical { public Oxygen() { AtomicNumber = 8; } public int AtomicNumber { get; } } } == Program.cs == class Program { static void Main(string[] args) { var ox = ChemicalFactory.CreateOxygen(); Console.WriteLine(ox.AtomicNumber); } }


public class HandlerFactory: Handler { public IHandler GetHandler() { return base.CreateMe(); } } public interface IHandler { void DoWork(); } public class Handler : IHandler { public void DoWork() { Console.WriteLine("hander doing work"); } protected IHandler CreateMe() { return new Handler(); } protected Handler(){} } public static void Main(string[] args) { // Handler handler = new Handler(); - this will error out! var factory = new HandlerFactory(); var handler = factory.GetHandler(); handler.DoWork(); // this works! }