2017-11-01 1 views
2

Ich arbeite in einer Produktionsstätte, die ein großes C++ - Projekt verwendet, um den Herstellungsprozess zu automatisieren.C++ Relais-Member-Funktionen in verschachtelten Klassen?

Ich sehe überall eine gewisse Übung, die den Code nur unnötig lang zu machen scheint und ich frage mich, ob es einen bestimmten Grund gibt, warum diese Praxis angewendet wird.

Siehe unten für ein vereinfachtes Beispiel, das diese Vorgehensweise demonstriert.

Erste Datei:

class A { 

private: 
    int a; 

public: 
    int get_a() 
    { return a; } 

    void set_a(int arg) 
    { a = arg; } 
}; 

Zweite Datei:

class B { 

private: 
    int b; 

public: 
    int get_b() 
    { return b; } 

    void set_b(int arg) 
    { b = arg; } 
}; 

Dritte Datei:

class C { 

private: 
    A obj1; 
    B obj2; 

public: 
    int get_a() 
    { return obj1.get_a(); } 

    int get_b() 
    { return obj2.get_b(); } 

    void set_a(int arg) 
    { obj1.set_a(arg); } 

    void set_b(int arg) 
    { obj2.set_b(arg); } 
}; 

Es scheint mir wie eine leichte Veränderung der Designphilosophie könnte drastisch die haben reduziert Menge an Code in der dritten Datei. Etwas wie folgt aus:

class C { 

public: 
    A obj1; 
    B obj2; 

}; 

Mit obj1 und obj2public Mitglieder in der C Klasse zu sein scheint nicht unsicher zu sein, weil die A und B Klassen jeweils sicher die immer und Einstellung ihrer eigenen Membervariablen behandeln.

Der einzige Nachteil, den ich es zu tun auf diese Weise denken kann, ist, dass alle Instanzen der C Klasse, die eine Funktion aufruft, müßten so etwas wie obj.obj1.get_a() anstatt nur tun obj.get_a() aber dies scheint wie viel weniger einen Nachteil, als wenn private A und B Objektinstanzen in der C Klasse und müssen dann manuell alle ihre Mitgliedsfunktionen "weiterleiten".

Ich realisiere für dieses einfache Beispiel, es ist nicht viel extra Code, aber für dieses große Projekt, das meine Firma verwendet, fügt es buchstäblich Zehntausende von Zeilen des Codes.

Fehle ich etwas?

+0

Die Getter und Setter sind ein bisschen ein Geruch, aber es sieht so aus, als ob der ursprüngliche Entwickler das [Gesetz von Demeter] (https://en.wikipedia.org/wiki/Law_of_Demeter) anwandte. Es gibt Vor- und Nachteile. –

+0

Die Nachteile scheinen offensichtlich - Hinzufügen einer Tonne mehr Code. Was sind die Nachteile? (Abgesehen davon, was ich bereits erwähnt habe, d. H. 'Obj.obj1.get_a()' vs. 'obj.get_a()') – ImaginaryHuman072889

+0

Nevermind ... erklärt die Wikipedia-Seite ziemlich gut. Vielen Dank! – ImaginaryHuman072889

Antwort

1

Es kann viele Gründe geben. Einer ist der folgende:

Stellen Sie sich vor, Sie schreiben eine Funktion, die etwas mit dem Mitglied a tut. Sie möchten den gleichen Code eine A sowie eine C akzeptieren. Ihre Funktion könnte wie folgt aussehen:

template <typename T> 
void foo(T& t) { 
    std::cout << " a = " << t.get_a(); 
} 

Dies würde nicht mit Ihrem C arbeitet, weil es eine andere Schnittstelle.

Kapselung hat ihre Vorteile, aber ich stimme Ihnen zu, dass die Kapselung wegen der Kapselung sehr oft zu viel mehr Code und oft zu nichts anderem führt.

Im Allgemeinen wird erzwungener Aufrufcode, um etwas wie obj.obj1.get_a() zu schreiben, entmutigt, weil es Implementierungsdetails enthüllt. Wenn Sie beispielsweise den Typ a ändern, hat Ihr C keinerlei Kontrolle über diese Änderung.Wenn sich andererseits in dem ursprünglichen Code a von int zu double ändert, dann kann C entscheiden, ob die Schnittstelle int behalten und einige Konvertierungen durchgeführt werden sollen (falls zutreffend) oder die Schnittstelle geändert werden soll.

+0

* Im allgemeinen wird es erzwungen, dass Aufrufcode etwas wie obj.obj1.get_a() schreibt, weil er Implementierungsdetails enthüllt. * Wow, guter Punkt, dachte nie so. Es macht definitiv den Code, der die C-Klasse verwendet viel sauberer, macht nur die C-Klasse selbst haben eine Menge von "Overhead" Art Code. Wie auch immer, deine Erklärung ist großartig und genau das was ich gesucht habe. – ImaginaryHuman072889

+1

@ ImaginaryHuman072889 Übrigens sieht der Code in Ihrem Beitrag aus wie ich in der Schule gelernt wurde und ich brauchte viele Jahre um zu verstehen, dass es nur einen einzigen guten Grund gibt Getter und Setter zu benutzen. Ich denke, es ist wichtig zu wissen, warum man folgenden Richtlinien folgt, denn manchmal möchte man nicht folgen;) – user463035818

1

Es fügt ein wenig zusätzlichen Code hinzu, aber das Wichtigste ist Ihre Schnittstelle. Eine Klasse hat eine Verantwortung und die Mitglieder, die sie enthält, sind Implementierungsdetails. Wenn Sie interne Objekte verfügbar machen und Benutzer zwingen, "das Objekt zu holen und dann darauf zu telefonieren", koppeln Sie den Aufrufer mehr an die Implementierung, als wenn Sie nur eine Schnittstelle bereitstellen, die den Auftrag für den Benutzer erledigt. Als eine Analogie, wenn man möchte, dass ein Hund spazieren geht, befiehlt man den Beinen des Hundes nicht, direkt zu gehen; stattdessen befiehlt man dem Hund, der dann seine eigenen Beine befehligt.

Law of Demeter/Wikipedia

formal Mehr verlangt das Gesetz von Demeter für Funktionen, die eine Methode m eines Objekts O nur die Methoden der folgenden Arten von Objekten aufrufen können:

  • O selbst
  • ms Parameter
  • Alle Objekte erstellt/instanziiert in m
  • O direkten Komponentenobjekte
  • eine globale Variable, zugänglich durch O, in den Rahmen m

Insbesondere ist es eine Aufgabe vermeiden sollte von einem anderen Verfahren zurück Verfahren eines Mitglieds Objekt aufruft. Für viele moderne objektorientierte Sprachen, die einen Punkt als Feldkennung verwenden, kann das Gesetz einfach als "nur einen Punkt verwenden" angegeben werden. Das heißt, der Code a.b.c.Method() bricht das Gesetz, wo a.b.Method() nicht gilt.

Verwandte Themen