Estou acenando com a cabeça em diferentes partes das várias respostas, mas o OP parece ainda ter a preocupação de controlar o fluxo. Há muito para tentar se unir em palavras. Eu apenas vou corrigir algum código - O Padrão de Estado.
Nomes de estados como pretérito
"In_Review" não é um estado, talvez, mas uma transição ou processo. Caso contrário, os nomes dos seus estados devem ser consistentes: "Aplicando", "Aprovando", "Recusando" etc. etc. OU também "Revisado". Ou não.
O estado Aplicado faz uma transição de revisão e define o estado para Revisado. O estado revisado faz uma transição de aprovação e define o estado como Aprovado (ou Recusado).
// Application class encapsulates state transition,
// the client is unable to directly set state.
public class Application {
State currentState = null;
State AppliedState = new Applied(this);
State DeclinedState = new Declined(this);
State ApprovedState = new Approved(this);
State ReviewedState = new Reviewed(this);
public class Application (ApplicationDocument myApplication) {
if(myApplication != null && isComplete()) {
currentState = AppliedState;
} else {
throw new ArgumentNullException ("Your application is incomplete");
// some kind of error communication would probably be better
}
}
public apply() { currentState.apply(); }
public review() { currentState.review(); }
public approve() { currentState.approve(); }
public decline() { currentState.decline(); }
//These could be done via an enum. I like enums!
protected void setSubmittingState() {}
protected void setApproveState() {}
// etc. ...
}
// could be an interface if we don't have any default or base behavior.
public abstract class State {
protected Application theApp;
// maybe these return an object communicating errors / error state.
public abstract void apply();
public abstract void review();
public abstract void accept();
public abstract void decline();
}
public class Applied implements State {
public Applied (Application newApp) {
if(newApp != null)
theApp = newApp;
else
throw new ArgumentNullException ("null application argument");
}
public override void apply() {
// whatever is appropriate when already in "applied" state
// do not do any work on behalf of other states!
// throwing exceptions here is not appropriate, as others
// have said.
}
public override void review() {
if(recursiveBureaucracyBuckPassing())
theApp.setReviewedState();
}
public override void decline() { // ditto }
}
public class Reviewed implements State {}
public class Approved implements State {}
public class Declined implements State {}
Editar - Comentários sobre o tratamento de erros
Um comentário recente:
... se você estiver tentando emprestar um livro que já foi emitido para outra pessoa, o modelo do livro conterá a lógica para impedir que seu estado seja alterado. Isso pode ser por meio de um valor de retorno (por exemplo, um código booleano yay / nay ou código de status) ou de uma exceção (por exemplo, IllegalStateChangeException) ou de algum outro meio. Independentemente dos meios escolhidos, este aspecto não é coberto como parte desta (ou de qualquer) resposta.
E a partir da pergunta original:
Mas esse tipo de código não torna óbvio que essa regra existe, permite que qualquer pessoa chame o método update () e somente depois de falhar o cliente sabe que essa operação não era permitida.
Há mais trabalho de design a ser feito. Não existe Unified Field Theory Pattern
. A confusão vem do pressuposto de que a estrutura de transição de estado executará funções gerais de aplicativos e manipulação de erros. Isso parece errado, porque é. A resposta mostrada foi projetada para controlar a mudança de estado.
Certamente, posso escrever um método update () na classe Application, verificar o estado do aplicativo e não fazer nada ou lançar uma exceção se o aplicativo não estiver no estado necessário
Isso sugere que existem três funcionalidades trabalhando aqui: O Estado, Atualização e interação dos dois. Nesse caso, Application
não é o código que escrevi. Pode ser usado para determinar o estado atual. Application
também não é applicationPaperwork
. Application
não é a interação dos dois, mas poderia ser uma StateContextEvaluator
classe geral . Agora Application
orquestrará essas interações de componentes e, em seguida, agirá de acordo, como emitir uma mensagem de erro.
Finalizar edição