web-dev-qa-db-fra.com

Comment respecter le principe de responsabilité unique et utiliser le traitement des exceptions en même temps?

Je suis confus sur la façon dont dois-je utiliser la gestion des exceptions dans une classe qui respecte le principe S?.

Par exemple, considérez ce code en C #:

public class BcryptDecrypt    
{    
    private string _password;
    private string _hash;

    public BcryptDecrypt(string password, string hash)
    {
      this._password = password;
      this._hash = hash;
    }

    public bool Verify()
    {
        return BCrypt.Net.BCrypt.Verify(this._password, this._hash);
    }    
}

Il indique clairement qu'il déchiffre un hachage bcrypt. Mais vous pouvez voir dans la méthode Verify qu'il n'y a pas de bloc try-catch pour gérer une exception.

Si nous ajoutons un try-catch pour gérer l'exception, nous ne pouvons pas suivre le principe S. Alors, comment les ingénieurs logiciels résolvent-ils ce problème?

// After adding try-catch
public class BcryptDecrypt    
{    
    private string _password;
    private string _hash;

    public BcryptDecrypt(string password, string hash)
    {
      this._password = password;
      this._hash = hash;
    }

    public bool Verify()
    {
        try
        {
            return BCrypt.Net.BCrypt.Verify(this._toDecrypt, 
                                            this._hash);
        }
        catch (System.Exception SomeException)
        {
            // Handle exception as you like and break S-Principle
        }
    }    
}

J'ai pensé à créer une classe distincte pour gérer chaque méthode ou exception de classe ou créer une classe universelle pour la gérer.

J'ai vu une question similaire sur Stack Overflow , mais la seule réponse donnée a dit de relancer l'exception pour laisser le contrôleur la gérer. Cela ne me semble pas être une solution étant donné que le contrôleur lui-même suivrait le principe S.

6
Suleman

Responsabilité unique?

Il y a ici un malentendu fondamental à propos de SRP:

  • La responsabilité unique ne signifie pas qu'une classe ne doit faire qu'une seule chose, mais qu'elle ne doit avoir qu'une seule raison de changer.

  • En d'autres termes, ce n'est pas la seule responsabilité de la classe, mais la seule responsabilité pour la classe de changer. Il s'agit donc de la prise de décision.

  • Voici un article éclairant de l'oncle Bob , qui a inventé SOLID, et l'explique mieux que moi avec toute son autorité sur le sujet.

Par conséquent, vous pouvez très bien gérer les exceptions dans cette classe sans aucun problème de conception. Vous pouvez même avoir une seule classe Cypher pour le chiffrement, le déchiffrement et l'autocheck, si vous le souhaitez vraiment.

Fais une chose ?

Dans Clean Code, Oncle Bob promeut également le Do-one-thing-and-do-it-well , pour les fonctions. Cela pourrait être remis en question pour la gestion des exceptions:

  • Faites une chose n'est pas la même chose que la moitié de la chose
  • Si quelque chose de mauvais se produit lorsque vous faites la chose, et si vous ne la manipulez pas comme vous le devriez, vous ne la ferez pas bien.
  • La gestion des exceptions n'empiète donc pas sur une chose.

Votre option 2 est donc acceptable.

Quelque chose à propos de try/catch?

Le seul principe restant à considérer pour votre fonction est Principe de niveau d'abstraction unique ( SLAP ) :

  • si vous try sur une fonction de haut niveau, vous devez catch et faire une fonction de haut niveau.
  • Il ne serait pas judicieux d'avoir une clause catch qui serait de très bas niveau et détaillée, comme deux pages de code pour la ligne catch vs 1 pour l'essai.

Si la gestion des exceptions devient trop complexe, voici un Nice pragmatique conseil :

Il est préférable d'extraire les corps de l'essai et de rattraper les blocs dans leurs propres fonctions.

13
Christophe

N'attrapez pas d'exceptions dès qu'elles sont levées. Détectez les exceptions au moment où vous pouvez faire quelque chose de significatif avec elles.

Mais votre exemple de code ne devrait pas y lancer . La seule exception raisonnable est ArgumentNullException, que vous devez jeter dans le constructeur.

public class BcryptDecrypt    
{    
    private string _password;
    private string _hash;

    public BcryptDecrypt(string password, string hash)
    {
      Ensure.ArgumentNotNull(password, nameof(password));
      Ensure.ArgumentNotNull(hash, nameof(hash));

      this._password = password;
      this._hash = hash;
    }

    public bool Verify()
    {
        return BCrypt.Net.BCrypt.Verify(this._password, this._hash);
    }    
}

Ensure.ArgumentNotNull d'ici .

10
Caleth

Vous ne compreniez pas le principe de responsabilité unique.

Cela signifie: Faites une chose - complètement. Avec tout ce qui est nécessaire pour le faire fonctionner. Comme la gestion des exceptions. Ajouter une autre classe pour la gestion des exceptions serait tout à fait ridicule.

Un chef cuisinant un homard prend une casserole, la remplit d'eau, la met au four, allume le feu, attend que l'eau bout, jette la langouste, etc. SRP ne pas dit que vous avez besoin d'un PotTakerClass, d'un PotWithWaterFillerClass, d'un PotOnOvenPutterClass et d'une douzaine de classes.

SRP ne dit même pas que vous avez besoin d'une LobsterCookerClass. Tout ce dont vous avez besoin est une ChefClass avec la seule responsabilité de cuisiner les aliments. PS. Un bon chef est comme Dieu, et il n'y a rien de mal à cela.

4
gnasher729

Comme d'autres l'ont dit, vous pouvez mal comprendre SRP. Ne vous sentez pas mal cependant, SRP n'est pas bien défini. Vous feriez mieux de l'ignorer et de vous concentrer sur d'autres principes plus bien définis comme Couplage et Cohésion et la Loi de Déméter .

Je ne suis pas vraiment ici pour commenter cela, mais à propos de votre conception. Vous dites:

Il indique clairement qu'il déchiffre un hachage bcrypt.

Il ne doit pas indiquer ce qu'il fait , mais ce que c'est . Les objets sont des choses, pas des procédures. Donc, ce que cela devrait être "BcryptHash". Et il devrait probablement vérifier si une chaîne y est hachée. Quelque chose comme:

public class BrcyptHash {
   private string _hash;
   ...
   public bool IsOf(string text)
      ...
0