Hinweis: Das Codebeispiel ist in c # geschrieben, aber das sollte keine Rolle spielen. Ich habe c # als Tag angegeben, weil ich kein passenderes finde. Hier geht es um die Codestruktur.
Ich lese Clean Code und versuche, ein besserer Programmierer zu werden.
Ich habe oft Mühe, das Prinzip der Einzelverantwortung zu befolgen (Klassen und Funktionen sollten nur eines tun), insbesondere in Funktionen. Vielleicht ist mein Problem, dass "eine Sache" nicht genau definiert ist, aber immer noch ...
Ein Beispiel: Ich habe eine Liste von Fluffies in einer Datenbank. Es ist uns egal, was ein Fluffy ist. Ich möchte, dass eine Klasse Flaumiges wiederherstellt. Fluffies können sich jedoch nach einer bestimmten Logik ändern. Je nach Logik gibt diese Klasse die Daten aus dem Cache zurück oder ruft die neuesten Daten aus der Datenbank ab. Wir könnten sagen, dass es mit Flusen umgeht, und das ist eine Sache. Nehmen wir zum Vereinfachen an, dass geladene Daten eine Stunde lang gültig sind und dann neu geladen werden müssen.
class FluffiesManager
{
private Fluffies m_Cache;
private DateTime m_NextReload = DateTime.MinValue;
// ...
public Fluffies GetFluffies()
{
if (NeedsReload())
LoadFluffies();
return m_Cache;
}
private NeedsReload()
{
return (m_NextReload < DateTime.Now);
}
private void LoadFluffies()
{
GetFluffiesFromDb();
UpdateNextLoad();
}
private void UpdateNextLoad()
{
m_NextReload = DatTime.Now + TimeSpan.FromHours(1);
}
// ...
}
GetFluffies()
scheint mir in Ordnung zu sein. Der Benutzer bittet um einige Flaumigkeiten, wir stellen sie zur Verfügung. Ich werde sie bei Bedarf aus der Datenbank wiederherstellen, aber das kann als Teil des Erhalts der Fluffies angesehen werden (natürlich ist das etwas subjektiv).
NeedsReload()
scheint auch richtig. Überprüft, ob die Fluffies neu geladen werden müssen. UpdateNextLoad ist in Ordnung. Aktualisiert die Zeit für das nächste Neuladen. Das ist definitiv eine einzige Sache.
Ich fühle jedoch, was LoadFluffies()
nicht als eine einzige Sache beschrieben werden kann. Es ruft die Daten aus der Datenbank ab und plant das nächste Neuladen. Es ist schwer zu argumentieren, dass das Berechnen der Zeit für das nächste Neuladen Teil des Erhalts der Daten ist. Ich kann jedoch keinen besseren Weg finden, dies zu tun (das Umbenennen der Funktion in ist LoadFluffiesAndScheduleNextLoad
möglicherweise besser, aber es macht das Problem nur offensichtlicher).
Gibt es eine elegante Lösung, um diese Klasse wirklich nach dem SRP zu schreiben? Bin ich zu pedantisch?
Oder macht meine Klasse nicht wirklich nur eine Sache?
DateTime.UtcNow
Sie ihn, um Umstellungen auf Sommerzeit oder sogar eine Änderung der aktuellen Zeitzone zu vermeiden.Antworten:
Wenn diese Klasse wirklich so trivial wäre, wie es scheint, dann müsste man sich keine Sorgen über eine Verletzung der SRP machen. Was ist, wenn bei einer 3-Zeilen-Funktion zwei Zeilen eine und eine weitere Zeile eine andere Aufgabe haben? Ja, diese triviale Funktion verstößt gegen das SRP, und was nun? Wen interessiert das? Die Verletzung der SRP wird zu einem Problem, wenn die Dinge komplizierter werden.
Ihr Problem in diesem speziellen Fall ergibt sich höchstwahrscheinlich aus der Tatsache, dass die Klasse komplizierter ist als die wenigen Zeilen, die Sie uns gezeigt haben.
Insbesondere liegt das Problem höchstwahrscheinlich in der Tatsache, dass diese Klasse nicht nur den Cache verwaltet, sondern wahrscheinlich auch die Implementierung der
GetFluffiesFromDb()
Methode enthält. Die Verletzung der SRP liegt also in der Klasse, nicht in den wenigen trivialen Methoden, die in dem von Ihnen veröffentlichten Code angezeigt werden.Hier ist ein Vorschlag, wie alle Arten von Fällen, die in diese allgemeine Kategorie fallen, mithilfe des Decorator-Musters behandelt werden können .
und es wird wie folgt verwendet:
Beachten Sie, dass Sie
CachingFluffiesProvider.GetFluffies()
keine Angst haben, den Code zu enthalten, mit dem die Zeit überprüft und aktualisiert wird, denn das ist trivial. Mit diesem Mechanismus wird die SRP auf der Ebene des Systemdesigns angesprochen und gehandhabt, auf der es darauf ankommt, nicht auf der Ebene winziger Einzelmethoden, auf der es sowieso nicht darauf ankommt.quelle
Ihre Klasse selbst scheint mir in Ordnung zu sein, aber Sie haben Recht damit, dass
LoadFluffies()
nicht genau das stimmt, was der Name verspricht. Eine einfache Lösung wäre, den Namen zu ändern und das explizite Neuladen von GetFluffies in eine Funktion mit einer entsprechenden Beschreibung zu verschieben. Etwas wiesieht für mich sauber aus (auch weil, wie Patrick sagt: es besteht aus anderen winzigen SRP-gehorsamen Funktionen) und vor allem auch klar, was manchmal genauso wichtig ist.
quelle
Ich glaube, deine Klasse tut eine Sache; Es ist ein Datencache mit einer Zeitüberschreitung. LoadFluffies scheint eine nutzlose Abstraktion zu sein, es sei denn, Sie rufen sie von mehreren Stellen aus auf. Ich denke, es wäre besser, die beiden Zeilen aus LoadFluffies zu nehmen und sie in die NeedsReload-Bedingung in GetFluffies zu setzen. Dies würde die Implementierung von GetFluffies viel offensichtlicher machen und ist immer noch sauberer Code, da Sie Subroutinen mit einer einzigen Verantwortung erstellen, um ein einziges Ziel zu erreichen, nämlich einen zwischengespeicherten Abruf von Daten aus der Datenbank. Nachfolgend finden Sie die aktualisierte Get Fluffies-Methode.
quelle
Ihre Instinkte sind richtig. Ihre Klasse, so klein sie auch sein mag, tut zu viel. Sie sollten die Cache-Logik für die zeitgesteuerte Aktualisierung in eine vollständig generische Klasse unterteilen. Erstellen Sie dann eine bestimmte Instanz dieser Klasse zum Verwalten von Fluffies (nicht kompiliert, Arbeitscode bleibt als Übung für den Leser übrig):
Ein zusätzlicher Vorteil ist, dass es jetzt sehr einfach ist, TimedRefreshCache zu testen.
quelle
Ihre Klasse ist in Ordnung, SRP ist eine Klasse, keine Funktion. Die gesamte Klasse ist für die Bereitstellung der "Fluffies" aus der "Datenquelle" verantwortlich, sodass Sie bei der internen Implementierung frei sind.
Wenn Sie den Cahing-Mechanismus erweitern möchten, können Sie eine Klasse erstellen, die für das Überwachen der Datenquelle zuständig ist
quelle