Wann soll ein gemeinsames Feld in eine Basisklasse verschoben werden?

16

Derzeit habe ich zwei abgeleitete Klassen, Aund Bbeide haben ein gemeinsames Feld, und ich versuche zu bestimmen, ob es in die Basisklasse aufgenommen werden soll.

Es wird nie von der Basisklasse aus referenziert, und sagen Sie, wenn irgendwann auf der Straße eine andere Klasse abgeleitet wird C, die kein a hat _field1, dann würde das Prinzip der "am wenigsten privilegierten" (oder etwas) nicht verletzt, wenn dies der Fall ist war?

public abstract class Base
{
    // Should _field1 be brought up to Base?
    //protected int Field1 { get; set; }
}

public class A : Base
{
    private int _field1;
}

public class B : Base
{
    private int _field1;
}

public class C : Base
{
    // Doesn't have/reference _field1
}
Sam ist
quelle
18
Ich denke , diese Frage nicht klar ist , weil Sie nicht haben uns eine Ahnung von dem, was Base, A, B, C, und _field1sind. Das sind wichtige Details, die man nicht auslassen sollte; Ich denke, Sie sollten die Frage bearbeiten, um darüber zu sprechen, was das ist.
Tanner Swett
Basierend auf den Antworten würde ich: Fahrzeug umbauen, um eine virtuelle Federung zu haben, dann könnten Auto und Fahrrad Räder haben und Boot könnte Auftrieb haben, was die Abstraktion nach oben bewegen würde. Ich finde, wenn meine Abstraktion direkt zu bestimmten Einstellungen führt, habe ich das Konzept nicht weit genug in der Kette verschoben.
Patrick Hughes
6
Verwenden Sie keine Klassenvererbung, um das Duplizieren von Code zu vermeiden. Verwenden Sie es zum Vererben und Erweitern des Verhaltens , dh des Polymorphismus. Verschieben Sie ein gemeinsames Feld genau dann in eine Basisklasse, wenn es sich logischerweise um dasselbe Feld handelt, und nicht um zwei nicht zusammenhängende Informationen, die in ihren jeweiligen Kontexten zufällig denselben Namen haben.
Brandon
Warum hast du zuerst eine Basisklasse?
jpmc26

Antworten:

34

Es hängt alles von dem genauen Problem ab, das Sie zu lösen versuchen.

Betrachten Sie ein konkretes Beispiel: Ihre abstrakte Basisklasse ist Vehicleund Sie haben derzeit die konkreten Implementierungen Bicycleund Car. Sie erwägen, numberOfWheelsvon Bicycleund Carzu einem Fahrzeug zu wechseln. Solltest du das tun? Nein! Weil nicht alle Fahrzeuge Räder haben. Wenn Sie versuchen, eine BoatKlasse hinzuzufügen, können Sie bereits erkennen, dass sie sich auflösen wird.

Wenn es sich bei Ihrer abstrakten Basisklasse um eine abstrakte Basisklasse handelte, WheeledVehicleist es logisch, die numberOfWheelsElementvariable dort zu haben.

Sie müssen die gleiche Logik auf Ihr Problem anwenden, da dies, wie Sie sehen, keine einfache Ja- oder Nein-Antwort ist.

Pete
quelle
3
Man könnte vorübergehend akzeptieren, dass 0 eine gültige Anzahl von Rädern ist. Schließlich können Sie jedoch eine roll()Methode hinzufügen , bei der die Idee der Unterklasse vorausschauend ist.
user949300
11
Ein Boot hat 0 Räder. Was macht das kaputt?
D Drmmr
14
@DDrmmr Es ist nicht so, dass ein Boot 0 Räder hat, es ist so, dass Räder nicht einmal als Konzept für ein Boot existieren - daher sollten Ihre Modelle dies nicht zulassen.
Peter M
10
Mein Punkt ist, dass das Beispiel schlecht ist. Es ist konzeptionell nichts Falsches an einem Fahrzeug (das zufällig ein Boot ist), das angibt, dass es 0 Räder hat.
D Drmmr
10
Dann geht alles zur Hölle, wenn Sie ein Paddelboot verstauen müssen.
IllusiveBrian
13

Logischerweise gibt es neben der Platzierung des Feldes, das in Unterklassen repliziert wurde, in der Basisklasse eine dritte Option: eine neue Unterklasse in die Hierarchie einzuführen, die die gemeinsamen Eigenschaften zwischen den beiden hat. @Pete deutet darauf hin, ohne vollständig dorthin zu gehen.

Am Beispiel von @ Pete würden wir eine (möglicherweise abstrakte) Unterklasse für Wheeled Vehicle einführen, die von der ursprünglichen Basisklasse abstammt - während die beiden Unterklassen davon abstammen. Somit ist die ursprüngliche Basisklasse nicht mit Rädern verschmutzt, die Gemeinsamkeit von Rädern ist jedoch TROCKEN (wird bei Unterklassen mit Rädern nicht wiederholt).

Dies kann für Ihre Zwecke natürlich übertrieben sein, wird jedoch vom Mechanismus der Klassenhierarchie unterstützt.

Erik Eidt
quelle
Dies ist eigentlich das, wozu ich mich entschlossen habe (A und B überlappen sich größtenteils, sodass B von A abgeleitet wird).
Samis
9

Ich werde hier Devil's Advocate spielen.

Im Moment solltest du nichts tun .

Ist es trocken? Nein. Aber es ist besser, ein wenig zu duplizieren als eine vorzeitige Abstraktion, von der Sie später nicht so einfach zurückkehren können. Der Refactor zum Verschieben einer Eigenschaft in eine allgemeine Basisklasse ist einfach. Den anderen Weg zu gehen, ist es nicht. Warten wir es ab.

Wenn ich eine solche Entscheidung treffe, tendiere ich dazu, eine "Regel von 3" zu verwenden: Wenn ich dasselbe z. B. an drei verschiedenen Stellen wiederholt habe, denke ich erst dann darüber nach, es die Kette hochzuschieben. NB du bist erst um 2.

Jared Smith
quelle
2
Eine weise Beobachtung, die ich sagen würde, um die Entscheidung treffen zu können.
Radarbob
1
Ich stimme größtenteils zu, aber "Im Moment" (ohne zusätzlichen Code wie den, den wir sehen) ist das Refactoring in beide Richtungen trivial. Wenn es jedoch zusätzlichen Code gibt, der das Feld verwendet, kann das Refactoring in beide Richtungen erheblich schwieriger werden.
Doc Brown
2

Im Allgemeinen würde ich es in die Basisklasse verschieben. Ich glaube nicht, dass es ein Ja / Nein-Ziel gibt, weil es hier einen Kompromiss gibt - nicht genutzte Felder zu tragen und Komplexität zu reduzieren.

Normalerweise bevorzuge ich 'schwere' Basisklassen, die alles enthalten, was geteilt werden kann. Dies vereinfacht die Serialisierung in Dateien, da Sie nicht in jeder abgeleiteten Klasse untergeordnete Serialisierungsmethoden benötigen. Aber wenn Sie das oder ein ähnliches Problem nicht haben oder alles tun müssen, um die Speichernutzung zu reduzieren, sollten Sie die Felder nur dort belassen, wo Sie sie benötigen.

Eine 'Intermediary'-Klasse, die die allgemeinen Felder einführt, ist in Ordnung, wenn Sie eine sehr begrenzte Anzahl von Feldern haben. Beachten Sie jedoch, dass der Ansatz die Komplexität erheblich erhöhen kann, wenn Sie Dutzende von Feldern in verschiedenen Kombinationen verwenden, was dazu führt, dass viele Zwischenklassen jeweils einen bestimmten Satz von Feldern einführen. Das kann zu einem Wartungsproblem werden.

GroßmeisterB
quelle
Mein Beispiel ist trivial, obwohl es immer noch Produktionscode ist. Sie machen ein gutes Argument dafür, die Hierarchie mit einer "schweren" Basisklasse "zu kompromittieren", zu der auch ich mich in einem solchen Fall neigen würde.
Samis
1
@samis: Verwenden Sie Klassen mit den Namen "A, B, C" und "Base" mit nur einem Feld und ohne Methode im Produktionscode? Ich frage das.
Doc Brown