web-dev-qa-db-fra.com

SonarQube Refactor cette méthode pour réduire sa complexité cognitive

J'ai la méthode d'utilité ci-dessous et j'utilise plusieurs déclarations if, ce qui pose un problème de complexité cognitive. Je suis passé par quelques liens, mais je ne peux pas comprendre comment changer mon code sans affecter les utilisateurs de cette méthode.

public static boolean isWrapperValid(WrapperClass wrapper, boolean isTechnicalToken){

    String key=null;
    boolean isValidWrapper = false;

    if (wrapper != null && wrapper.length() > 7
        && wrapper.substring(0, 6).equalsIgnoreCase("XYZ"))
    {
        wrapper= wrapper.substring(7, wrapper.lastIndexOf('.')+1);
    }
    if(wrapper != null && wrapper.equalsIgnoreCase("TFR")) {
        isValidWrapper=Boolean.TRUE;
    }
    try {
         key = wrapper.getKey();
    }
    catch (Exception exception) {
        return isValidWrapper;
    }

    if(key!=null) {

        Date tokenExpiryTime = key.getExpiresAt();

        if(tokenExpiryTime!=null) {
            return isValidWrapper;
        }

        String algorithm=key.getAlgorithm();
        if(!DESIRED_ALGO.equals(algorithm)) {
            return isValidWrapper;
        }

        String value6=key.getType();
        if(!DESIRED_TYPE.equals(value6)) {
            return isValidWrapper;
        }


        if(key.getValue1()!=null && key.getValue2().size()>0 && key.getValue3()!=null && key.getValue4()!=null && key.getValue5()!=null) {
            isValidWrapper=Boolean.TRUE;
        }
    }

    return isValidWrapper;
}

S'il vous plaît partagez vos suggestions pour refactoriser ce code.

2
smruti ranjan

Je ne pense pas que la fusion de plusieurs conditions if dans une seule ou simplement le nettoyage du code, par exemple en modifiant l'ordre de certaines instructions, puisse résoudre votre problème.

Votre code ne correspond pas au principe de responsabilité unique . Vous devriez refactoriser cette grande méthode en petites parties. Grâce à cela, il sera testable, plus facile à entretenir et à lire. J'ai passé du temps et j'ai fait ceci:

public static boolean isWrapperValid(WrapperClass wrapper, boolean isTechnicalToken) {

    final WrapperClass unpackedWrapper = unpackWrapper(wrapper);
    boolean wrapperValid = isUnpackedWrapperValid(unpackedWrapper);

    Key key = null;
    try {
        key = unpackedWrapper.getKey();
    } catch (final Exception exception) {
        return wrapperValid;
    }

    if (key != null) {   
        if (doesKeyMeetsBasicConditions(key)) {
            return wrapperValid;
        }
        if (doesKeyMeetsValueConditions(key)) {
            return true;
        }
    }
    return wrapperValid;
}

protected static WrapperClass unpackWrapper(final WrapperClass wrapper) {      
    if (wrapper != null && wrapper.length() > 7 && wrapper.substring(0, 6).equalsIgnoreCase("XYZ")) {
        return wrapper.substring(7, wrapper.lastIndexOf('.') + 1);
    }
    return wrapper;
}

protected static boolean isUnpackedWrapperValid(final WrapperClass wrapper) {
   return wrapper != null && wrapper.equalsIgnoreCase("TFR");
}

protected static boolean doesKeyMeetsBasicConditions(final Key key) {
    Date tokenExpiryTime = key.getExpiresAt();
    if (tokenExpiryTime != null) {
        return true;
    }

    String algorithm = key.getAlgorithm();
    if (!DESIRED_ALGO.equals(algorithm)) {
        return true;
    }

    String value6 = key.getType();
    if (!DESIRED_TYPE.equals(value6)) {
        return true;
    }
    return false;
}

protected static boolean doesKeyMeetsValueConditions(final Key key) {
    return key.getValue1() != null && key.getValue2().size() > 0
           && key.getValue3() != null && key.getValue4() != null
           && key.getValue5() != null;
}

Je ne connais pas la logique de domaine, certaines de mes méthodes ont donc des noms stupides, etc. Comme vous pouvez le constater, vous avez maintenant beaucoup de méthodes plus petites avec peu de branches (conditions if) - plus faciles à tester (un code statique n'est pas Bien, mais vous pouvez vous en moquer en utilisant par exemple PowerMock ).

4
agabrys

Tout d’abord, Sonar devrait vous donner plus d’indicateurs: réutiliser le paramètre wrapper est généralement une mauvaise pratique, NPE où invoquer wrapper.getKey car wrapper peut être null, mais quoi qu’il en soit, ce n’est pas le point ...

Essayez de réduire le nombre d'instructions if en créant des variables booléennes locales (ou éventuellement une grosse instruction if si vous avez moins de 5 ou 6 tests, mais souvent moins lisibles). Une fois que c'est fait, vous ne devriez avoir qu'un bloc testant ces variables booléennes et une seule instruction return, comme dans l'exemple ci-dessus (pas nécessairement exacte!): 

boolean expired = tokenExpiryTime != null;
boolean desiredAlgo = DESIRED_ALGO.equals(key.getAlgorithm());
boolean desiredType = DESIRED_TYPE.equals(value6);
if (expired || !desiredAlgo || !desiredType) {
    return isValidWrapper;
}

Cependant, votre niveau de complexité cognitive semble assez faible si ce type d'algorithme le déclenche ...

Un autre moyen efficace de réduire la complexité d'un algorithme consiste à transformer des sous-blocs de code (boucles, if et try-catch) en méthodes privées. Dans votre exemple, cela pourrait être quelque chose comme une méthode checkWrapperValidity, responsable de chaque test renvoyant isValidWrapper

0
HBo

Un peu de réécriture a apporté une simplification, qui pourrait encore être améliorée.

public static boolean isWrapperValid(WrapperClass wrapper, boolean isTechnicalToken){
    if (wrapper != null && wrapper.length() > 7
        && wrapper.substring(0, 6).equalsIgnoreCase("XYZ"))
    {
        wrapper = wrapper.substring(7, wrapper.lastIndexOf('.')+1);
    }
    boolean isValidWrapper = wrapper != null && wrapper.equalsIgnoreCase("TFR");

    try {
        String key = wrapper.getKey();
        if (key != null && key.getExpiresAt() == null
                && DESIRED_ALGO.equals(key.getAlgorithm())
                && DESIRED_TYPE.equals(key.getType())
                && key.getValue1() != null && !key.getValue2().isEmpty()
                && key.getValue3() != null && key.getValue4() != null
                && key.getValue5() != null) {
            isValidWrapper = true;
        }
    }
    catch (Exception exception) {
        // DO NOTHING
    }
    return isValidWrapper;
}

Après le commentaire: ici, je capture toute exception pour tous les appels.

0
Joop Eggen