Eu tenho uma classe usada para processar pagamentos de clientes. Todos os métodos desta classe, exceto um, são os mesmos para todos os clientes, exceto um que calcula (por exemplo) quanto o usuário do cliente deve. Isso pode variar muito de cliente para cliente e não há uma maneira fácil de capturar a lógica dos cálculos em algo como um arquivo de propriedades, pois pode haver vários fatores personalizados.
Eu poderia escrever um código feio que alterna com base no customerID:
switch(customerID) {
case 101:
.. do calculations for customer 101
case 102:
.. do calculations for customer 102
case 103:
.. do calculations for customer 103
etc
}
mas isso requer a reconstrução da classe toda vez que obtemos um novo cliente. Qual é a melhor maneira?
[Editar] O artigo "duplicado" é completamente diferente. Não estou perguntando como evitar uma declaração de troca, estou pedindo o design moderno que melhor se aplica a este caso - que eu poderia resolver com uma declaração de troca se quisesse escrever código de dinossauro. Os exemplos fornecidos são genéricos e não são úteis, pois dizem essencialmente "Ei, a opção funciona muito bem em alguns casos, não em outros".
[Editar] Decidi ir com a resposta mais bem classificada (criar uma classe "Cliente" separada para cada cliente que implemente uma interface padrão) pelos seguintes motivos:
Consistência: posso criar uma interface que garanta que todas as classes de clientes recebam e retornem a mesma saída, mesmo se criadas por outro desenvolvedor
Manutenção: Todo o código é escrito na mesma linguagem (Java), portanto não é necessário que ninguém mais aprenda uma linguagem de codificação separada para manter o que deveria ser um recurso simples.
Reutilização: caso um problema semelhante apareça no código, eu posso reutilizar a classe Customer para armazenar qualquer número de métodos para implementar a lógica "personalizada".
Familiaridade: eu já sei como fazer isso, para que eu possa fazer isso rapidamente e passar para outras questões mais prementes.
Desvantagens:
Cada novo cliente requer uma compilação da nova classe Customer, o que pode adicionar alguma complexidade à maneira como compilamos e implantamos as alterações.
Cada novo cliente deve ser adicionado por um desenvolvedor - uma pessoa de suporte não pode simplesmente adicionar a lógica a algo como um arquivo de propriedades. Isso não é o ideal ... mas também não tinha certeza de como uma pessoa de Suporte seria capaz de escrever a lógica comercial necessária, especialmente se ela for complexa com muitas exceções (como é provável).
Não será bem dimensionado se adicionarmos muitos, muitos novos clientes. Isso não é esperado, mas se isso acontecer, teremos que repensar muitas outras partes do código, bem como esta.
Para os interessados, você pode usar o Java Reflection para chamar uma classe pelo nome:
Payment payment = getPaymentFromSomewhere();
try {
String nameOfCustomClass = propertiesFile.get("customClassName");
Class<?> cpp = Class.forName(nameOfCustomClass);
CustomPaymentProcess pp = (CustomPaymentProcess) cpp.newInstance();
payment = pp.processPayment(payment);
} catch (Exception e) {
//handle the various exceptions
}
doSomethingElseWithThePayment(payment);