Was ist ein "Feature Envy" -Code und warum wird er als Codegeruch angesehen?

52

In dieser Frage zu SO geht es darum, zu korrigieren, was der OP-Gedanke für Feature-Neid- Code ist. Ein weiteres Beispiel, in dem ich gesehen habe, wie dieser flotte Satz zitiert wurde, ist eine kürzlich von Programmierern hier gegebene Antwort . Obwohl ich in einem Kommentar zu dieser Antwort nach Informationen gefragt habe, dachte ich, dass es für Programmierer, die Fragen und Antworten befolgen, eine allgemeine Hilfe wäre, zu verstehen, was unter dem Begriff Feature-Neid zu verstehen ist . Gerne können Sie weitere Tags bearbeiten, wenn Sie dies für angemessen halten.

Aussenseiter
quelle

Antworten:

87

Feature-Neid ist ein Begriff, der verwendet wird, um eine Situation zu beschreiben, in der ein Objekt auf die Felder eines anderen Objekts gelangt, um eine Art Berechnung durchzuführen oder eine Entscheidung zu treffen, anstatt das Objekt aufzufordern, die Berechnung selbst durchzuführen.

Betrachten Sie als einfaches Beispiel eine Klasse, die ein Rechteck darstellt. Der Benutzer des Rechtecks ​​muss möglicherweise seinen Bereich kennen. Der Programmierer könnte belichten widthund heightFelder und dann die Berechnung außerhalb der RectangleKlasse durchführen. Alternativ Rectanglekönnte die halten widthund heightFelder privaten und bieten getAreaMethode. Dies ist wohl ein besserer Ansatz.

Das Problem mit der ersten Situation und der Grund, warum sie als Codegeruch betrachtet wird, ist, dass sie die Kapselung unterbricht.

Als Faustregel gilt, wenn Sie feststellen, dass Sie Felder einer anderen Klasse ausgiebig verwenden, um irgendeine Art von Logik oder Berechnung durchzuführen, ziehen Sie in Betracht, diese Logik auf eine Methode in der Klasse selbst zu verschieben.

jhewlett
quelle
7
+1, obwohl Ihr Beispiel nicht realistisch ist, da eine nützliche Rectangle-Klasse normalerweise auch Felder für Breite und Höhe verfügbar macht.
Doc Brown
2
Und obwohl das Brechen der Kapselung möglicherweise zusammen mit "Feature-Envirismus" auftritt, war dies in den meisten Beispielen aus der Praxis, die ich bisher gesehen habe, wahrscheinlich nicht der Fall. Es passiert eher in Situationen, "hey, ich muss einige Dinge nur im Code mit diesem Objekt berechnen, und ich bin nicht sicher, ob ich die Implementierung dieses Objekts berühren kann / ob wir dem Objekt die Verantwortung für diese Berechnung übertragen sollen". Und dann implementiert man die Berechnung unter Verwendung von Feldern, die bereits verfügbar sind (obwohl eine viel sauberere Implementierung innerhalb des Objekts wahrscheinlich möglich wäre).
Doc Brown
2
@DocBrown Stellen Sie sich ein Rechteck vor, das auf die Oberfläche eines Torus, eines Kegels oder einer Kugel gezeichnet ist. Wenn meine Formzeichnungsbibliothek Objekte erzeugt, die in solchen Kontexten die richtigen Ergebnisse liefern können, wäre es töricht, sie nicht ihre eigenen Flächen berechnen zu lassen - in jedem Kontext.
Itsbruce
1
@OskarN .: Per Definition sprechen wir über Funktionen, die benötigt werden. Sie werden offensichtlich benötigt, wenn andere Klassen sie immer wieder neu implementieren.
Aaronaught
1
@OskarN .: es kommt darauf an; Manchmal ist die Entscheidung klar, manchmal ist es eine Frage des Geschmacks und meistens ist es eine Frage der Erfahrung. In Ihrem Artikel gibt es gute Gründe , warum Scott Meyers schreibt „ manchmal ist weniger mehr“, und dass es ihn Jahre gedauert hat , als zu verstehen , nicht seinen „Algorithmus für die Entscheidung , wann fa Elementfunktion zu machen“ anzuwenden.
Doc Brown
1

Es gibt eine mögliche Situation, in der es in Ordnung ist, andere Klassen- / Strukturmethoden ausgiebig zu verwenden - wenn Ihre Klasse / Struktur ein Container für Daten ist. In der Regel gibt es ein wenig, was Sie mit diesen Daten ohne externen Kontext tun können.

Solche Klassen können immer noch interne Logik enthalten, werden jedoch häufiger als Container verwendet:

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 bezieht sich in seiner Antwort auf diesen Artikel, um zu beweisen, dass Sie keine anderen Klassenmitglieder ausgiebig verwenden sollten, aber es gibt eine andere Code-Geruchssituation , die dort mit Befürwortern meines Beispiels beschrieben wird:

Lange Parameterliste. Begrenzen Sie die Anzahl der Parameter, die Sie in einer bestimmten Methode benötigen, oder verwenden Sie ein Objekt, um die Parameter zu kombinieren.

Riga
quelle
1
Wie beantwortet dies die gestellte Frage?
gnat
@gnat Das Q handelt davon, warum es als "Code-Geruch" gilt. Jhewlett gibt ein A mit einigen zu allgemeinen Annahmen, die in den Kommentaren in Frage gestellt wurden. Meine Antwort ist 2 Cent, um "Codegeruch" von der normalen Praxis zu unterscheiden.
Riga