Recentemente, tenho pensado em proteger parte do meu código. Estou curioso para saber como alguém poderia ter certeza de que um objeto nunca pode ser criado diretamente, mas apenas por meio de algum método de uma classe de fábrica. Digamos que eu tenha alguma classe de "objeto de negócios" e quero ter certeza de que qualquer instância dessa classe terá um estado interno válido. Para conseguir isso, vou precisar fazer algumas verificações antes de criar um objeto, provavelmente em seu construtor. Está tudo bem até eu decidir que quero fazer com que essa verificação faça parte da lógica de negócios. Portanto, como posso fazer com que um objeto de negócios seja criável apenas por meio de algum método em minha classe de lógica de negócios, mas nunca diretamente? O primeiro desejo natural de usar uma boa e velha palavra-chave "amiga" do C ++ ficará aquém do C #. Então, precisamos de outras opções ...
Vamos tentar alguns exemplos:
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;
}
}
Está tudo bem até você se lembrar que ainda pode criar a instância MyBusinessObjectClass diretamente, sem verificar a entrada. Eu gostaria de excluir totalmente essa possibilidade técnica.
Então, o que a comunidade pensa sobre isso?
fonte
CreateBusinessObject
método é validar argumentos AND para (retornar um novo objeto válido OU retornar um erro) em uma única chamada de método quando um chamador não sabe imediatamente se os argumentos são válidos para passar para o construtor.Distance
instância com um argumento negativo deve lançar um,ArgumentOutOfRangeException
por exemplo, você não ganharia nada criando umDistanceFactory
que faça a mesma verificação. Ainda não vejo o que você tem a ganhar aqui.Respostas:
Parece que você apenas deseja executar alguma lógica de negócios antes de criar o objeto - então por que você simplesmente não cria um método estático dentro da "BusinessClass" que faz toda a verificação suja de "myProperty" funcionar e torna o construtor privado?
Ligar seria muito simples:
fonte
Você pode tornar o construtor privado e a fábrica um tipo aninhado:
Isso funciona porque os tipos aninhados têm acesso aos membros privados de seus tipos de inclusão. Eu sei que é um pouco restritivo, mas espero que ajude ...
fonte
CreateBusinessObject
método dentro de umaFactory
classe aninhada em vez de fazer com que o método estático seja um método diretamente daBusinessObject
classe ... você pode se lembrar do seu motivação para fazer isso?Build
.)Ou, se você quiser ficar realmente sofisticado, inverta o controle: faça com que a classe devolva a fábrica e forneça à fábrica um delegado que possa criar a classe.
:)
fonte
Você poderia tornar o construtor em sua classe MyBusinessObjectClass interno e movê-lo junto com a fábrica para seu próprio assembly. Agora, apenas a fábrica deve ser capaz de construir uma instância da classe.
fonte
Além do que Jon sugeriu, você também pode fazer com que o método de fábrica (incluindo a verificação) seja um método estático de BusinessObject em primeiro lugar. Então, tenha o construtor privado e todos os outros serão forçados a usar o método estático.
Mas a verdadeira questão é - por que você tem esse requisito? É aceitável mover a fábrica ou o método de fábrica para a classe?
fonte
Depois de tantos anos, isso foi perguntado, e todas as respostas que vejo estão, infelizmente, dizendo a você como você deve fazer seu código em vez de dar uma resposta direta. A resposta real que você estava procurando é ter suas classes com um construtor privado, mas um instanciador público, o que significa que você só pode criar novas instâncias de outras instâncias existentes ... que estão disponíveis apenas na fábrica:
A interface para suas aulas:
Sua classe:
E, finalmente, a fábrica:
Em seguida, você pode modificar facilmente esse código se precisar de parâmetros extras para a instanciação ou para pré-processar as instâncias criadas. E esse código permitirá que você force a instanciação pela fábrica, pois o construtor da classe é privado.
fonte
Outra opção (leve) é fazer um método de fábrica estático na classe BusinessObject e manter o construtor privado.
fonte
Então, parece que o que eu quero não pode ser feito de uma forma "pura". É sempre algum tipo de "chamada de volta" para a classe de lógica.
Talvez eu pudesse fazer de forma simples, basta fazer um método construtor na classe de objeto primeiro chamar a classe lógica para verificar a entrada?
Dessa forma, o objeto de negócios não pode ser criado diretamente e o método de verificação pública na lógica de negócios também não causará danos.
fonte
Em um caso de boa separação entre interfaces e implementações, o
padrão protected-constructor-public-initializer permite uma solução muito simples.
Dado um objeto de negócios:
e uma fábrica de negócios:
a seguinte mudança para
BusinessObject.New()
inicializador fornece a solução:Aqui, uma referência à fábrica de negócios de concreto é necessária para chamar o
BusinessObject.New()
inicializador. Mas a única que tem a referência necessária é a própria fábrica de negócios.Nós conseguimos o que queríamos: o único que pode criar
BusinessObject
éBusinessFactory
.fonte
public static IBusinessObject New(BusinessFactory factory)
vão levantar muitas sobrancelhas se outra pessoa mantiver o código.factory
paraasFriend
. Em minha base de código, ele seria mostrado comopublic static IBusinessObject New(BusinessFactory asFriend)
fonte
Eu colocaria a fábrica no mesmo conjunto que a classe de domínio e marcaria o construtor da classe de domínio interno. Dessa forma, qualquer classe em seu domínio pode ser capaz de criar uma instância, mas você não confia em si mesmo, certo? Qualquer pessoa que escrever código fora da camada de domínio terá que usar sua fábrica.
No entanto, devo questionar sua abordagem :-)
Eu acho que se você quiser que sua classe Person seja válida na criação, você deve colocar o código no construtor.
fonte
Esta solução é baseada fora munificents idéia de usar um token no construtor. Feito nesta resposta, certifique-se de que o objeto foi criado apenas pela fábrica (C #)
fonte
Múltiplas abordagens com diferentes compensações foram mencionadas.
Create
método e um ctor privado.Eu gostaria de propor a fábrica como uma classe parcial que contém classes aninhadas privadas com construtores públicos. Você está 100% escondendo o objeto que sua fábrica está construindo e apenas expondo o que você escolhe por meio de uma ou várias interfaces.
O caso de uso que ouvi para isso seria quando você deseja rastrear 100% das instâncias na fábrica. Este design garante que ninguém, mas a fábrica tem acesso à criação de instâncias de "produtos químicos" definidos na "fábrica" e elimina a necessidade de uma montagem separada para isso.
fonte
Não entendo por que você deseja separar a "lógica de negócios" do "objeto de negócios". Isso soa como uma distorção da orientação do objeto, e você vai acabar se amarrando em nós ao fazer essa abordagem.
fonte
Eu não acho que haja uma solução que não seja pior do que o problema, todos acima exigem uma fábrica estática pública que IMHO é um problema pior e não vai impedir as pessoas de chamarem a fábrica para usar seu objeto - ele não esconde nada. Melhor expor uma interface e / ou manter o construtor como interno, se possível, essa é a melhor proteção, já que o assembly é um código confiável.
Uma opção é ter um construtor estático que registra uma fábrica em algum lugar com algo como um contêiner IOC.
fonte
Aqui está outra solução no estilo "só porque você pode, não significa que você deve" ...
Ele atende aos requisitos de manter o construtor do objeto de negócios privado e colocar a lógica de fábrica em outra classe. Depois disso, fica um pouco incompleto.
A classe de fábrica possui um método estático para criar objetos de negócios. Ele deriva da classe de objeto de negócios para acessar um método de construção protegido estático que invoca o construtor privado.
A fábrica é abstrata, então você não pode realmente criar uma instância dela (porque também seria um objeto de negócios, o que seria estranho) e tem um construtor privado para que o código do cliente não possa derivar dele.
O que não é evitado é o código do cliente também derivando da classe de objeto de negócios e chamando o método de construção estático protegido (mas não validado). Ou pior, chamar o construtor padrão protegido que tivemos que adicionar para que a classe de fábrica fosse compilada em primeiro lugar. (O que, aliás, é provável que seja um problema com qualquer padrão que separe a classe de fábrica da classe de objeto de negócios.)
Não estou tentando sugerir que alguém em sã consciência deva fazer algo assim, mas foi um exercício interessante. FWIW, minha solução preferida seria usar um construtor interno e o limite da montagem como proteção.
fonte
Gostaria de ouvir algumas reflexões sobre esta solução. O único capaz de criar 'MyClassPrivilegeKey' é a fábrica. e 'MyClass' requer isso no construtor. Evitando assim, reflexão sobre empreiteiros privados / “registo” à fábrica.
fonte