O SRP é talvez o princípio de software mais incompreendido.
Uma aplicação de software é construída a partir de módulos, que são construídos a partir de módulos, que são construídos a partir de ...
Na parte inferior, uma única função como CheckInput
contém apenas um pouco de lógica, mas à medida que você sobe, cada módulo sucessivo encapsula mais e mais lógica e isso é normal .
O SRP não é sobre fazer uma única ação atômica . É sobre ter uma única responsabilidade, mesmo que essa responsabilidade exija várias ações ... e, em última análise, é sobre manutenção e testabilidade :
- promove o encapsulamento (evitando objetos de Deus),
- promove a separação de interesses (evitando alterações graduais em toda a base de código),
- ajuda a testabilidade ao restringir o escopo de responsabilidades.
O fato de que CheckInput
é implementado com duas verificações e gera duas exceções diferentes é irrelevante até certo ponto.
CheckInput
tem uma responsabilidade limitada: garantir que a entrada atenda aos requisitos. Sim, existem vários requisitos, mas isso não significa que existam várias responsabilidades. Sim, você poderia dividir os cheques, mas como isso ajudaria? Em algum momento, as verificações devem ser listadas de alguma forma.
Vamos comparar:
Constructor(Stream stream) {
CheckInput(stream);
// ...
}
versus:
Constructor(Stream stream) {
CheckInput(stream,
(this.StreamIsNull, this.Throw<ArgumentNullException>),
(this.StreamIsReadonly, this.Throw<ArgumentException>));
// ...
}
Agora, CheckInput
faz menos ... mas o chamador faz mais!
Você alterou a lista de requisitos de CheckInput
, onde eles estão encapsulados, para Constructor
onde eles estão visíveis.
É uma boa mudança? Depende:
- Se
CheckInput
for chamado apenas lá: é discutível, por um lado, torna os requisitos visíveis e, por outro, desorganiza o código; - Se
CheckInput
for chamado várias vezes com os mesmos requisitos , isso violará DRY e você terá um problema de encapsulamento.
É importante perceber que uma única responsabilidade pode implicar em um lote de trabalho. O "cérebro" de um carro autônomo tem uma responsabilidade única:
Driving the car to its destination.
É uma responsabilidade única, mas requer coordenar uma tonelada de sensores e atores, tomar muitas decisões e até mesmo ter requisitos conflitantes 1 ...
... no entanto, tudo é encapsulado. Então o cliente não se importa.
1 segurança dos passageiros, segurança dos outros, respeito dos regulamentos, ...