Remarque: l' exemple de code est écrit en c #, mais cela n'a pas d'importance. J'ai mis c # comme balise parce que je ne peux pas en trouver de plus approprié. Il s'agit de la structure du code.
Je lis Clean Code et j'essaie de devenir un meilleur programmeur.
J'ai souvent du mal à suivre le principe de responsabilité unique (les classes et les fonctions ne devraient faire qu'une seule chose), spécialement dans les fonctions. Peut-être que mon problème est qu'une "chose" n'est pas bien définie, mais quand même ...
Un exemple: j'ai une liste de Fluffies dans une base de données. Peu nous importe ce qu'est un Fluffy. Je veux qu'un cours récupère les peluches. Cependant, les peluches peuvent changer selon une logique. Selon une logique, cette classe retournera les données du cache ou obtiendra la dernière de la base de données. On pourrait dire qu'il gère les peluches, et c'est une chose. Pour faire simple, disons que les données chargées sont bonnes pendant une heure, puis elles doivent être rechargées.
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()
me semble bien. L'utilisateur demande des peluches, nous les lui fournissons. Aller les récupérer de la base de données si nécessaire, mais cela pourrait être considéré comme une partie de l'obtention des peluches (bien sûr, c'est quelque peu subjectif).
NeedsReload()
semble bien aussi. Vérifie si nous devons recharger les peluches. UpdateNextLoad est très bien. Met à jour l'heure du prochain rechargement. c'est certainement une seule chose.
Cependant, je pense que LoadFluffies()
ce qui ne peut pas être décrit comme une seule chose. Il récupère les données de la base de données et planifie le prochain rechargement. Il est difficile de prétendre que le calcul du temps pour le prochain rechargement fait partie de l'obtention des données. Cependant, je ne peux pas trouver une meilleure façon de le faire (renommer la fonction en LoadFluffiesAndScheduleNextLoad
peut être meilleur, mais cela rend le problème plus évident).
Existe-t-il une solution élégante pour vraiment écrire cette classe selon le SRP? Suis-je trop pédant?
Ou peut-être que ma classe ne fait pas vraiment une seule chose?
DateTime.UtcNow
afin d'éviter les changements d'heure d'été, ou même un changement dans le fuseau horaire actuel.Réponses:
Si cette classe était vraiment aussi triviale qu'elle semble l'être, alors il n'y aurait pas besoin de s'inquiéter de violer le SRP. Que se passe-t-il si une fonction à 3 lignes a 2 lignes qui font une chose et une autre 1 ligne qui fait autre chose? Oui, cette fonction triviale viole le SRP, et alors quoi? On s'en fout? La violation du SRP commence à devenir un problème lorsque les choses se compliquent.
Votre problème dans ce cas particulier vient très probablement du fait que la classe est plus compliquée que les quelques lignes que vous nous avez montrées.
Plus précisément, le problème réside très probablement dans le fait que cette classe gère non seulement le cache, mais contient également probablement l'implémentation de la
GetFluffiesFromDb()
méthode. Ainsi, la violation du SRP est dans la classe, pas dans les quelques méthodes triviales qui sont affichées dans le code que vous avez publié.Donc, voici une suggestion sur la façon de gérer toutes sortes de cas qui entrent dans cette catégorie générale, à l'aide du modèle de décorateur .
et il est utilisé comme suit:
Notez comment
CachingFluffiesProvider.GetFluffies()
n'a pas peur de contenir le code qui effectue le temps de vérification et de mise à jour, car c'est une chose triviale. Ce que ce mécanisme fait, c'est d'aborder et de gérer le SRP au niveau de la conception du système, là où cela compte, pas au niveau de minuscules méthodes individuelles, où cela n'a pas d'importance de toute façon.la source
Votre classe me semble bien, mais vous avez raison, ce
LoadFluffies()
n'est pas exactement ce que le nom annonce. Une solution simple serait de changer le nom et de déplacer le rechargement explicite de GetFluffies, dans une fonction avec une description appropriée. Quelque chose commeme semble propre (aussi parce que, comme le dit Patrick: il est composé d'autres minuscules fonctions obéissant à SRP), et surtout aussi clair, ce qui est parfois tout aussi important.
la source
Je crois que votre classe fait une chose; c'est un cache de données avec un délai d'attente. LoadFluffies semble être une abstraction inutile à moins que vous ne l'appeliez de plusieurs endroits. Je pense qu'il serait préférable de prendre les deux lignes de LoadFluffies et de les mettre dans le conditionnel NeedsReload dans GetFluffies. Cela rendrait l'implémentation de GetFluffies beaucoup plus évidente et est toujours du code propre, car vous composez des sous-programmes à responsabilité unique pour atteindre un seul objectif, une récupération en cache des données de la base de données. Vous trouverez ci-dessous la méthode mise à jour de get fluffies.
la source
Vos instincts sont corrects. Votre classe, si petite soit-elle, en fait trop. Vous devez séparer la logique de mise en cache d'actualisation temporisée en une classe complètement générique. Ensuite, créez une instance spécifique de cette classe pour gérer Fluffies, quelque chose comme ça (non compilé, le code de travail est laissé comme exercice pour le lecteur):
Un avantage supplémentaire est qu'il est désormais très facile de tester TimedRefreshCache.
la source
Votre classe va bien, SRP concerne une classe et non une fonction, toute la classe est responsable de fournir les "Fluffies" de la "Data Source" afin que vous soyez libre dans l'implémentation interne.
Si vous souhaitez étendre le mécanisme de cahing, vous pouvez créer une classe respnsible pour regarder la source de données
la source