Melhor padrão de design de OOP para uma sequência de operações

11

Estou trabalhando em um aplicativo, cujo módulo executa as seguintes operações financeiras sequencialmente:

Quando um usuário solicita que uma certa quantia seja transferida para sua conta bancária:

  1. verificar se alguma transação pode acontecer agora? (a transação pode ser realizada apenas durante um determinado período)
  2. verifique se o usuário solicitou a retirada de um valor mínimo
  3. verifique se o usuário tem alguma conta padrão

O resultado de todas as ações acima deve ser registrado.

Se todas as condições acima estiverem em conformidade, a transação é realizada. No futuro, pode haver algumas verificações adicionais.

Qual padrão de design orientado a objetos deve ser mais adequado para o caso acima?

kumar
fonte
3
Nunca procure um padrão de design para resolver um problema. Use padrões de design para comunicar a solução correta. programmers.stackexchange.com/questions/70877/… Siga os princípios do SOLID e você não errará muito.
pdr
2
Discordo. Os padrões têm nomes que facilitam a comunicação e também devem ser usados ​​para comunicar soluções. Mas não concordo com "Nunca procure um padrão de design para resolver um problema". Eles não apenas resolvem problemas específicos, mas também lidam com diferentes forças e restrições. Dê uma olhada em "Proxy" e "Decorator". Eles parecem semelhantes, mas resolvem problemas diferentes. Portanto, na minha opinião, antes de resolver um problema, você deve pelo menos examinar os padrões de design conhecidos para lucrar com ambos, uma abordagem padrão para resolver um problema e uma maneira fácil de comunicá-lo.
Jonny Dee
10
Aqui está uma boa caracterização do que é um padrão: "Eles [padrões] fornecem soluções funcionais, concretas e adaptáveis ​​a problemas que surgem repetidamente em determinadas situações durante o desenvolvimento de software, dos contextos organizacional aos de programação". [POSA5, p. 30] Portanto, deste ponto de vista, é totalmente claro que procurar um padrão como uma solução adaptável é uma abordagem ligítima.
Jonny Dee
3
Você está pedindo uma construção orientada a objetos para descrever a programação processual antiga e simples?
Mouviciel 23/09
4
Siga o princípio do KISS. Até agora, seu problema pode ser resolvido com 3 instruções "if" em um único método. Não tente usar um padrão de design apenas para ser legal. Toda vez que você escrever uma aula adicional, sempre pense: Eu realmente preciso disso?
eiver

Respostas:

13

Parece que o que você está procurando é uma Cadeia de Responsabilidade . Nesse caso, você pode ter as seguintes classes:

  • TransactionValidatorBase classe base abstrata
  • TransactionTimeValidator
  • TransactionAmountValidator
  • TransactionAccountValidator

Que são encadeados para aplicar as regras que você especificar.

Furter Reading

pswg
fonte
11
Meu entendimento é que a Cadeia de Responsabilidade é mais um filtro - ou seja, descemos a cadeia até encontrar alguém equipado para lidar com a responsabilidade, então esse "link" lidará com a responsabilidade e sairá. Uma falha no COR em termos práticos é que é difícil fazê-lo retornar um valor, o que parece ser necessário para isso.
Amy Blankenship
Eu acho que você pode ter uma Cadeia de R. de um nível. Realmente não acho difícil retornar um valor de uma Cadeia, com um pouco de abtração. Cada nível será necessário para se comunicar aderindo a uma determinada interface primitiva e será necessário aceitar entradas de baixo, dado que essas entradas estão em conformidade com a interface primitiva. Quando a riqueza da interface é necessária entre dois níveis de cadeia que estão mais intimamente acoplados, a abtração pode ser poliformada para suportar isso.
Andyz Smith
2

O padrão correto aqui é realmente depende de um contexto. Antes de escolher qualquer padrão em particular, tentarei encontrar respostas para essas perguntas:

  • É necessário criar combinações diferentes de verificações (1,2,3) em tempo de execução?
  • Eles precisam das mesmas variáveis ​​para executar suas ações ou são muito diferentes?
  • Qual a precisão das mensagens de erro?
  • Em caso de falha, o usuário tenta sempre novamente a partir da (1) primeira etapa?
  • Como a simultaneidade é tratada?
  • Cada método adiciona algo à solicitação ou simplesmente valida? (diga ID de conta padrão?)

Baseado em um pressentimento, eu os codificaria como métodos simples, com agregação de parâmetros para códigos de erro.

public void DoTransaction(IErrorAgregator error, TransactionRequest request)
{
    if(!IsTransactionInCertainTimePeriod(request, error)) return;
    if(!IsTransactionAmountInUserBounds(request, error)) return;
    if(!UserHaveDefaultAccount(request, error)) return;
    bankingTransactor.PerformTransaction(request);
}

Pode ser uma boa ideia colocar DoTransaction na interface "ITransactionValidationStragegy" e criar um super-tipo de camada que conterá o código de validação.

No entanto, neste design, estou assumindo que a lógica de validação é determinada em tempo de compilação.

Valera Kolupaev
fonte
1

Se sua sequência de etapas estiver cumprindo principalmente tarefas de validação (como você parece), sem alterar as entradas, eu pensaria realmente no padrão "Cadeia de responsabilidade", conforme explicado em sua resposta por @pswg

Mas como sua pergunta é um pouco mais genérica, eu gostaria de adicionar também o "Processamento de pipeline", já que com essa, uma etapa produziria uma saída que se tornaria a entrada para a próxima etapa (alterando a entrada original) .

Aqui estão dois artigos:
Coleção de pipeline de Martin Fowler
Mais discussões teóricas sobre o padrão

julio.g
fonte
0

Embora os padrões já sejam mencionados aqui, sugiro que você pense em como gostaria de usar o mesmo em seu aplicativo, com base nas estruturas que você está usando.

Por exemplo, a validação que você gostaria de fazer provavelmente continuará mudando à medida que o tempo avança (pode ser que você queira adicionar uma nova validação no futuro que restrinja as transações a 10 por dia). Além disso, você pode não querer fazer a validação antes que o código de serviço ou integração de negócios real seja ativado. Seria bom se você pudesse adicionar as validações como configuráveis.

Caso você esteja usando Struts, o uso de interceptores pode ser uma boa ideia. No caso da primavera, a injeção de feijão como dependência oferece mais flexibilidade. Minha sugestão é não apenas olhar para os padrões / expressões idiomáticas, mas também para a estrutura que você usa para criar o aplicativo e ver a melhor forma de se adequar às suas necessidades do ponto de vista futurista.

Uma corrida
fonte
-2

De acordo com o meu entendimento, tudo o que for necessário pode ser ajustado ao padrão de comando como abaixo. O design da classe pode ser feito conforme abaixo.

interface Transaction{
void performAction();
}

class Banking{

void moneyValidation(){
//Validate Here
}

void timeValidation(){
//validate Here
}
}

class TimeValidation implements Transaction{

public Banking bank;

public TimeValidation (Banking bnk){
bank=bnk;
}

void performAction(){
bnk.timeValidation();
}


class MoneyValidation Implements Transaction{

public Banking bank;

public MoneyValidation(Banking bnk;){
bank=bnk;
}

void performAction(){
bnk.moneyValidation();
}
}


class Control{

private List val_list=new ArrayList();

void storeValidation(Transaction trans){
val_list.add(trans);
trans.performAction(val_list.getFirstAndRemove());
}
}

//Same for other validation classes

Sua classe Client conterá o seguinte snippet de código:

Banking bnk = new Banking();
MoneyValidation m_val = new MoneyValidation (bnk);
TimeValidation t_val = new TimeValidation (bnk);
Control ctrl = new Control();
ctrl.storeValidation(m_val);
ctrl.storeValidation(t_val);

Isto é de acordo com o meu entendimento, com o cenário que foi dado acima.

alok
fonte
É ruim porque quando a validação dinheiro falhar, validação tempo é inútil, mas isso será feito de qualquer maneira
Ewoks