Frage

In einem Projekt verwendet unser Team Objektlisten, um Massenoperationen an Datensätzen durchzuführen, die alle auf ähnliche Weise verarbeitet werden sollen.Insbesondere würden sich unterschiedliche Objekte im Idealfall gleich verhalten, was mit Polymorphismus sehr einfach zu erreichen wäre.Das Problem, das ich damit habe, ist, dass Vererbung das impliziert ist ein Beziehung, statt der hat ein Beziehung.Zum Beispiel mehrere Objekte habe einen Schadenszähler, aber um die Verwendung in einer Objektliste zu vereinfachen, könnte Polymorphismus verwendet werden – außer dass dies einen implizieren würde ist ein Beziehung, die nicht wahr wäre.(Eine Person ist kein Schadenszähler.)

Die einzige Lösung, die mir einfällt, besteht darin, dass ein Mitglied der Klasse bei der impliziten Umwandlung den richtigen Objekttyp zurückgibt, anstatt sich auf die Vererbung zu verlassen.Wäre es besser, darauf zu verzichten? ist ein / hat ein ideal im Austausch für einfache Programmierung?

Bearbeiten:Genauer gesagt verwende ich C++, sodass die Verwendung von Polymorphismus es den verschiedenen Objekten ermöglichen würde, sich „gleich zu verhalten“, in dem Sinne, dass sich die abgeleiteten Klassen in einer einzigen Liste befinden und von einer virtuellen Funktion der Basisklasse bearbeitet werden könnten.Die Verwendung einer Schnittstelle (oder deren Nachahmung durch Vererbung) scheint eine Lösung zu sein, die ich gerne verwenden würde.

War es hilfreich?

Lösung

Dies kann durch Mehrfachvererbung erreicht werden.In Ihrem speziellen Fall (C++) können Sie rein virtuelle Klassen als Schnittstellen verwenden.Dadurch ist eine Mehrfachvererbung möglich, ohne dass Umfangs-/Mehrdeutigkeitsprobleme entstehen.Beispiel:

class Damage {
    virtual void addDamage(int d) = 0;
    virtual int getDamage() = 0;
};

class Person : public virtual Damage {
    void addDamage(int d) {
        // ...
        damage += d * 2;
    }

    int getDamage() {
        return damage;
    }
};

class Car : public virtual Damage {
    void addDamage(int d) {
        // ...
        damage += d;
    }

    int getDamage() {
        return damage;
    }
};

Jetzt sind sowohl Person als auch Auto ein Schaden, das heißt, sie implementieren die Schadensschnittstelle.Die Verwendung rein virtueller Klassen (so dass sie wie Schnittstellen sind) ist von entscheidender Bedeutung und sollte häufig verwendet werden.Es verhindert, dass zukünftige Änderungen das gesamte System verändern.Weitere Informationen finden Sie im Open-Closed-Prinzip.

Andere Tipps

Ich denke, Sie sollten Schnittstellen implementieren, um Ihre durchsetzen zu können hat ein Beziehungen (ich mache das in C#):

public interface IDamageable
{
    void AddDamage(int i);
    int DamageCount {get;}
}

Sie könnten dies in Ihren Objekten implementieren:

public class Person : IDamageable

public class House : IDamageable

Und Sie können sicher sein, dass die Eigenschaft DamageCount über eine Methode verfügt, mit der Sie Schaden hinzufügen können, ohne zu implizieren, dass eine Person und ein Haus in einer Art Hierarchie miteinander verbunden sind.

Ich stimme Jon zu, aber vorausgesetzt, Sie benötigen immer noch eine separate Schadenszählerklasse, können Sie Folgendes tun:

class IDamageable {
  virtual DamageCounter* damage_counter() = 0;
};
class DamageCounter {
  ...
};

Jede beschädigbare Klasse muss dann ihre eigene Mitgliedsfunktion Damage_counter() bereitstellen.Der Nachteil dabei ist, dass für jede Schadensklasse eine Vtable erstellt wird.Sie können stattdessen Folgendes verwenden:

class Damageable {
 public:
  DamageCounter damage_counter() { return damage_counter_; }
 private:
  DamageCounter damage_counter_;
};

Aber viele Leute sind es Nicht cool mit Mehrfachvererbung, wenn mehrere übergeordnete Elemente Mitgliedsvariablen haben.

Manchmal lohnt es sich, das Ideal zugunsten des Realistischen aufzugeben.Wenn es ein massives Problem darstellt, es „richtig“ zu machen, ohne wirklichen Nutzen, dann würde ich es falsch machen.Vor diesem Hintergrund denke ich oft, dass es sich lohnt, sich die Zeit zu nehmen, es richtig zu machen, da unnötige Mehrfachvererbung die Komplexität erhöht, und zwar dürfen tragen dazu bei, dass das System weniger wartbar ist.Sie müssen wirklich entscheiden, was für Ihre Situation am besten ist.

Eine Möglichkeit wäre, diese Objekte a implementieren zu lassen Damageable Schnittstelle, anstatt von zu erben DamageCounter.Auf diese Weise eine Person hat ein Schadenszähler, aber Ist zerstörbar.(Ich finde oft, dass Schnittstellen als Adjektive viel sinnvoller sind als Substantive.) Dann könnte man eine konsistente Schadensschnittstelle verwenden Damageable Objekte und nicht offenlegen, dass ein Schadenszähler die zugrunde liegende Implementierung ist (es sei denn, Sie müssen dies tun).

Wenn Sie den Template-Weg gehen möchten (unter der Annahme von C++ oder ähnlichem), können Sie dies mit Mixins tun, aber das kann bei schlechter Umsetzung sehr schnell hässlich werden.

Diese Frage ist wirklich verwirrend :/

Ihre Frage in Fettdruck ist sehr offen und die Antwort lautet „es kommt darauf an“, aber Ihr Beispiel gibt nicht wirklich viele Informationen über den Kontext, aus dem Sie fragen.Diese Zeilen verwirren mich;

Datensätze, die alle auf ähnliche Weise verarbeitet werden sollten

Welcher weg?Werden die Mengen von einer Funktion verarbeitet?Eine andere Klasse?Über eine virtuelle Funktion auf die Daten?

Insbesondere würden sich unterschiedliche Objekte im Idealfall gleich verhalten, was mit Polymorphismus sehr einfach zu erreichen wäre

Das Ideal des „gleich handelns“ und Polymorphismus haben absolut nichts miteinander zu tun.Wie lässt sich Polymorphismus leicht erreichen?

@Kevin

Wenn wir über „ist ein“ vs. „hat ein“ sprechen, sprechen wir normalerweise über Vererbung vs. Zusammensetzung.

Ähm ... der Schadenszähler wäre nur ein Attribut einer Ihrer abgeleiteten Klassen und würde in Bezug auf Ihre Frage nicht wirklich im Sinne von „Eine Person ist ein Schadenszähler“ diskutiert.

Da er die Schadensmarke als Attribut hat, ist es ihm nicht möglich, Objekte mit Schadensmarken in einer Sammlung zusammenzustellen.Beispielsweise könnten sowohl eine Person als auch ein Auto Schadensmarken haben, Sie jedoch nicht vector<Person|Car> oder ein vector<with::getDamage()> oder etwas Ähnliches in den meisten Sprachen.Wenn Sie eine gemeinsame Object-Basisklasse haben, können Sie diese auf diese Weise verschieben, haben dann aber keinen Zugriff darauf getDamage() Methode allgemein.

Das war der Kern seiner Frage, als ich sie las.„Sollte ich verletzen? is-a Und has-a um bestimmte Objekte so zu behandeln, als wären sie gleich, auch wenn sie es nicht sind?“

Wenn wir über „ist ein“ vs. „hat ein“ sprechen, sprechen wir normalerweise über Vererbung vs. Zusammensetzung.

Ähm ... der Schadenszähler wäre nur ein Attribut einer Ihrer abgeleiteten Klassen und würde in Bezug auf Ihre Frage nicht wirklich im Sinne von „Eine Person ist ein Schadenszähler“ diskutiert.

Sieh dir das an:

http://www.artima.com/designtechniques/compoinh.html

Was Ihnen auf dem Weg helfen könnte.

@Derek:Vom Wortlaut her bin ich davon ausgegangen War ein Basissatz, nachdem ich die Frage noch einmal gelesen habe, verstehe ich jetzt irgendwie, worauf er hinaus will.

„Es richtig zu machen“ wird auf lange Sicht Vorteile haben, schon allein deshalb, weil jemand, der das System später wartet, leichter nachvollziehen kann, ob es von Anfang an richtig gemacht wurde.

Abhängig von der Sprache besteht durchaus die Möglichkeit einer Mehrfachvererbung, in der Regel sind jedoch einfache Schnittstellen am sinnvollsten.Mit „einfach“ meine ich, eine Schnittstelle zu erstellen, die nicht zu viel sein soll.Besser viele einfache Schnittstellen und ein paar monolithische.Natürlich gibt es immer einen Kompromiss, und zu viele Schnittstellen würden wahrscheinlich dazu führen, dass einige davon „vergessen“ werden ...

@Andrew

Das Ideal des „gleich handelns“ und Polymorphismus haben absolut nichts miteinander zu tun.Wie lässt sich Polymorphismus leicht erreichen?

Sie alle haben z. B. eine Funktion gemeinsam.Nennen wir es addDamage().Wenn Sie so etwas tun möchten:

foreach (obj in mylist)
    obj.addDamage(1)

Dann benötigen Sie entweder eine dynamische Sprache oder Sie benötigen eine Erweiterung aus einer gemeinsamen übergeordneten Klasse (oder Schnittstelle).z.B.:

class Person : DamageCounter {}
class Car : DamageCounter {}

foreach (DamageCounter d in mylist)
    d.addDamage(1)

Dann können Sie behandeln Person Und Car das Gleiche gilt unter bestimmten, sehr nützlichen Umständen.

Polymorphismus erfordert keine Vererbung.Polymorphismus entsteht, wenn mehrere Objekte dieselbe Nachrichtensignatur (Methode) implementieren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top