Recientemente he estado pensando en proteger parte de mi código. Tengo curiosidad por saber cómo uno podría 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 una clase de "objeto comercial" y quiero asegurarme de que cualquier instancia de esta clase tenga un estado interno válido. Para lograr esto, necesitaré realizar algunas comprobaciones antes de crear un objeto, probablemente en su constructor. Todo esto está bien hasta que decida que quiero que esta verificación sea parte de la lógica empresarial. Entonces, ¿cómo puedo hacer que un objeto comercial se pueda crear solo a través de algún método en mi clase de lógica comercial, pero nunca directamente? El primer deseo natural de usar una palabra clave de "amigo" de C ++ se quedará corto con C #. Entonces necesitamos otras opciones ...
Probemos con un ejemplo:
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 recuerde que aún puede crear la instancia MyBusinessObjectClass directamente, sin verificar la entrada. Me gustaría excluir por completo esa posibilidad técnica.
Entonces, ¿qué piensa la comunidad sobre esto?
fuente
CreateBusinessObject
método es validar argumentos Y para (devolver un nuevo objeto válido O devolver un error) en una única llamada al método cuando una persona que llama no sabe inmediatamente si los argumentos son válidos para pasar al constructor.Distance
instancia con un argumento negativo debería arrojar un,ArgumentOutOfRangeException
por ejemplo, no ganaría nada al crear unDistanceFactory
que haga la misma verificación. Todavía no veo lo que puedes ganar aquí.Respuestas:
Parece que solo desea ejecutar algo de lógica comercial antes de crear el objeto, entonces, ¿por qué no crea un método estático dentro de "BusinessClass" que hace todo el trabajo de verificación sucio de "myProperty" y hace que el constructor sea privado?
Llamarlo sería bastante sencillo:
fuente
Puede hacer que el constructor sea privado y la fábrica un tipo anidado:
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 ...
fuente
CreateBusinessObject
método dentro de unaFactory
clase anidada en lugar de que ese método estático sea un método directamente de laBusinessObject
clase ... ¿puede recordar su motivación para hacerlo?Build
.)O, si quieres ir realmente elegante, invierte el control: haz que la clase devuelva la fábrica e instrumente la fábrica con un delegado que pueda crear la clase.
:)
fuente
Puede hacer que el constructor de su clase MyBusinessObjectClass sea interno y moverlo y la fábrica a su propio ensamblado. Ahora solo la fábrica debería poder construir una instancia de la clase.
fuente
Aparte de lo que sugirió Jon, también puede hacer que el método de fábrica (incluida la verificación) sea un 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.
Pero la verdadera pregunta es: ¿por qué tiene este requisito? ¿Es aceptable trasladar la fábrica o el método de fábrica a la clase?
fuente
Después de tantos años me preguntaron esto, y todas las respuestas que veo desafortunadamente le dicen cómo debe hacer su código en lugar de dar una respuesta directa. La respuesta real que estaba buscando es tener sus clases con un constructor privado pero un instanciador público, lo que significa que solo puede crear nuevas instancias a partir de otras instancias existentes ... que solo están disponibles en la fábrica:
La interfaz para tus clases:
Tu clase:
Y, finalmente, la fábrica:
Luego, puede modificar fácilmente este código si necesita parámetros adicionales para la creación de instancias o para preprocesar las instancias que cree. 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.
fuente
Otra opción más (ligera) es crear un método de fábrica estático en la clase BusinessObject y mantener el constructor privado.
fuente
Entonces, 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 constructor en la clase de objeto, primero llame a la clase lógica para verificar la entrada.
De esta manera, el objeto comercial no se puede crear directamente y el método de verificación pública en la lógica comercial tampoco perjudicará.
fuente
En el caso de una buena separación entre interfaces e implementaciones, el
patrón protected-constructor-public-initializer permite una solución muy ordenada.
Dado un objeto comercial:
y una fábrica de negocios:
el siguiente cambio al
BusinessObject.New()
inicializador da la solución:Aquí se necesita una referencia a la fábrica de negocios de hormigón para llamar al
BusinessObject.New()
inicializador. Pero el único que tiene la referencia requerida es la propia fábrica de negocios.Conseguimos lo que queríamos: el único que puede crear
BusinessObject
esBusinessFactory
.fuente
public static IBusinessObject New(BusinessFactory factory)
levantarán muchas cejas si alguien más mantiene el código.factory
aasFriend
. En mi base de código se mostraría entonces comopublic static IBusinessObject New(BusinessFactory asFriend)
fuente
Pondría la fábrica en el mismo ensamblado que la clase de dominio y marcaría el constructor de la clase de dominio como interno. De esta manera, cualquier clase en su dominio puede crear una instancia, pero confía en que no lo hará, ¿verdad? Cualquiera que escriba código fuera de la capa de dominio tendrá que usar su fábrica.
Sin embargo, debo cuestionar su enfoque :-)
Creo que si desea que su clase Person sea válida en el momento de la creación, debe colocar el código en el constructor.
fuente
Esta solución se basa en la idea de munificents de usar un token en el constructor. Hecho en esta respuesta, asegúrese de que el objeto solo sea creado por fábrica (C #)
fuente
Se han mencionado múltiples enfoques con diferentes compensaciones.
Create
método y un cliente privado.Me gustaría proponer la fábrica como una clase parcial que contiene clases anidadas privadas con constructores públicos. Estás ocultando al 100% el objeto que está construyendo tu fábrica y solo expones lo que eliges a través de una o varias interfaces.
El caso de uso que escuché para esto sería cuando desea rastrear el 100% de las instancias en la fábrica. Este diseño garantiza que nadie más que la fábrica tenga 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 lograrlo.
fuente
No entiendo por qué desea separar la "lógica empresarial" del "objeto empresarial". Esto suena como una distorsión de la orientación del objeto, y terminará haciendo nudos al adoptar ese enfoque.
fuente
No creo que haya una solución que no sea peor que el problema, todo lo anterior requiere una fábrica estática pública que, en mi humilde opinión, es un problema peor y no impedirá que la gente simplemente llame a la fábrica para usar su objeto, no oculta nada. Es mejor 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 confiable.
Una opción es tener un constructor estático que registre una fábrica en algún lugar con algo como un contenedor IOC.
fuente
Aquí hay otra solución en la línea de "solo porque puedas no significa que debas" ...
Cumple los requisitos de mantener privado el constructor de objetos de negocio y colocar la lógica de fábrica en otra clase. Después de eso, se vuelve un poco vago.
La clase de fábrica tiene un método estático para crear objetos comerciales. Se deriva de la clase de objeto de negocio para acceder a un método de construcción protegido estático que invoca al constructor privado.
La fábrica es abstracta, por lo que no puede crear una instancia de ella (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 él.
Lo que no se impide es que el código de cliente también se derive de la clase de objeto de negocio y llame al método de construcción estático protegido (pero no validado). O peor aún, llamar al constructor predeterminado protegido que tuvimos que agregar para que la clase de fábrica se compilara en primer lugar. (Lo 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 comercial).
No estoy tratando de sugerir a nadie en su sano juicio que 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.
fuente
Agradecería escuchar algunos pensamientos sobre esta solución. El único capaz de crear 'MyClassPrivilegeKey' es la fábrica. y 'MyClass' lo requiere en el constructor. Evitando así la reflexión sobre contratistas privados / "registro" a la fábrica.
fuente