Staram się w jak największym stopniu przestrzegać zasady jednolitej odpowiedzialności (SRP) i przyzwyczaiłem się do pewnego wzorca (dla SRP dotyczącego metod), w dużej mierze polegającego na delegatach. Chciałbym wiedzieć, czy to podejście jest dobre, czy też są z nim jakieś poważne problemy.
Na przykład, aby sprawdzić dane wejściowe do konstruktora, mógłbym wprowadzić następującą metodę (dane Stream
wejściowe są losowe, mogą być dowolne)
private void CheckInput(Stream stream)
{
if(stream == null)
{
throw new ArgumentNullException();
}
if(!stream.CanWrite)
{
throw new ArgumentException();
}
}
Ta metoda (prawdopodobnie) robi więcej niż jedną rzecz
- Sprawdź wejścia
- Rzuć różne wyjątki
Dlatego, aby zastosować się do SRP, zmieniłem logikę na
private void CheckInput(Stream stream,
params (Predicate<Stream> predicate, Action action)[] inputCheckers)
{
foreach(var inputChecker in inputCheckers)
{
if(inputChecker.predicate(stream))
{
inputChecker.action();
}
}
}
Co podobno robi tylko jedną rzecz (prawda?): Sprawdź dane wejściowe. Do faktycznego sprawdzania danych wejściowych i zgłaszania wyjątków wprowadziłem metody takie jak
bool StreamIsNull(Stream s)
{
return s == null;
}
bool StreamIsReadonly(Stream s)
{
return !s.CanWrite;
}
void Throw<TException>() where TException : Exception, new()
{
throw new TException();
}
i może zadzwonić CheckInput
jak
CheckInput(stream,
(this.StreamIsNull, this.Throw<ArgumentNullException>),
(this.StreamIsReadonly, this.Throw<ArgumentException>))
Czy jest to w ogóle lepsze niż pierwsza opcja, czy też wprowadzam niepotrzebną złożoność? Czy jest jakiś sposób, aby poprawić ten wzór, jeśli jest w ogóle wykonalny?
CheckInput
wciąż robi wiele rzeczy: zarówno iteruje tablicę, jak i wywołuje funkcję predykatu i wywołuje funkcję akcji. Czy to nie jest naruszenie SRP?