Qu'est-ce qu'un code «caractéristique envie» et pourquoi est-il considéré comme une odeur de code?

53

Cette question sur SO parle de la correction de ce que le PO pensait être un code de jalousie . Un autre exemple où j'ai vu cette phrase astucieuse citée se trouve dans une réponse donnée récemment ici dans programmers.SE. Bien que j'aie laissé un commentaire à cette réponse demandant des informations, je pensais que cela aiderait les programmeurs suivant les questions-réponses à comprendre ce que l'on entend par le terme " envie de fonctionnalités" . N'hésitez pas à éditer d'autres tags si vous le jugez utile.

Geek
la source

Réponses:

88

Envie de fonctionnalité est un terme utilisé pour décrire une situation dans laquelle un objet accède aux champs d'un autre objet afin d'effectuer une sorte de calcul ou de prendre une décision, plutôt que de demander à l'objet d'effectuer le calcul lui-même.

Comme exemple trivial, considérons une classe représentant un rectangle. L'utilisateur du rectangle peut avoir besoin de connaître sa zone. Le programmeur peut exposer widthet heightchamps puis faire le calcul en dehors de la Rectangleclasse. Sinon, Rectanglepourrait garder le widthet les heightchamps privés et d' une getAreaméthode. C'est sans doute une meilleure approche.

Le problème avec la première situation, et la raison pour laquelle il est considéré comme une odeur de code, réside dans le fait qu’elle casse l’encapsulation.

En règle générale, chaque fois que vous utilisez fréquemment les champs d'une autre classe pour effectuer toute sorte de logique ou de calcul, envisagez de déplacer cette logique vers une méthode de la classe elle-même.

jhewlett
la source
7
+1, bien que votre exemple ne soit pas réaliste, car une classe Rectangle utile exposerait généralement les champs width et height.
Doc Brown
2
Et bien que la rupture de l’encapsulation puisse survenir conjointement avec «Feature envirism», dans la plupart des exemples réels que j’ai vus jusqu’à présent, ce n’était probablement pas le cas. Cela arrive plus souvent dans les situations "hé, je dois calculer certaines choses uniquement dans le code en utilisant cet objet, et je ne suis pas sûr de pouvoir toucher à la mise en oeuvre de cet objet / si nous devrions donner à l'objet la responsabilité de ce calcul". Et puis on implémente le calcul en utilisant des champs déjà exposés (bien qu'une implémentation beaucoup plus propre serait probablement possible dans l'objet).
Doc Brown
2
@DocBrown Imaginez un rectangle dessiné à la surface d'un tore, d'un cône ou d'une sphère. Si ma bibliothèque de dessins de formes produit des objets capables de produire les résultats corrects dans de tels contextes, il serait insensé de ne pas les laisser calculer leurs propres surfaces, quel que soit le contexte.
Itsbruce
1
@OskarN .: Par définition , nous parlons des fonctions qui sont nécessaires. Ils sont évidemment nécessaires si d'autres classes les réappliquent encore et encore.
Aaronaught
1
@Oskar .: ça dépend; parfois, la décision est claire, parfois c'est une question de goût et le plus souvent, c'est une question d'expérience. Dans votre article, il existe de bonnes raisons pour lesquelles Scott Meyers écrit " parfois moins, c'est plus", et qu'il lui a fallu des années pour comprendre quand ne pas appliquer son "algorithme pour décider quand faire fonctionner un membre fa".
Doc Brown
1

Il est possible que l'utilisation d'une autre méthode de classe / struct soit étendue, lorsque votre classe / structure est un conteneur de données. Généralement, vous pouvez faire quelque chose avec ces données sans contexte externe.

De telles classes peuvent toujours contenir une logique interne, mais le plus souvent, elles sont utilisées en tant que conteneurs:

class YourUid {
 public:
  YourUid(int id_in_workplace_, int id_in_living_place_, DB* FBI_database, int id_in_FBI_database);
  bool IsInvalidWorker() const { return id_in_workplace == consts::invalid_id_in_workplace; }
  bool CanMessWith() const { return !FBI_database_.is_cool(id_in_FBI_database_); }
  int id_in_workplace;
  int id_in_living_place;
 private:
  int id_in_FBI_database_;
  const DB* FBI_database_;
};

@Jhewlett dans sa réponse se réfère à cet article pour prouver qu'il ne faut pas utiliser les autres membres du groupe de manière intensive, mais il existe une autre situation de code odeurs décrite ici avec les avocats, mon exemple:

Long Parameter List. Limitez le nombre de paramètres dont vous avez besoin dans une méthode donnée ou utilisez un objet pour combiner les paramètres.

Riga
la source
1
Comment cela répond-il à la question posée?
moucher
@gnat Le Q concerne pourquoi il est considéré comme une "odeur de code". jhewlett donne un A avec des hypothèses trop générales remises en question dans les commentaires. Ma réponse est 2 cents pour distinguer "l'odeur de code" de la pratique normale.
Riga