Frage

Die Frage, die ich stellen möchte, ist also:

Ist das Gießen der Vererbungsbaum nach unten (dh. In Richtung einer specialiased Klasse) aus dem Inneren einer abstrakten Klasse entschuldbar, oder sogar eine gute Sache, oder ist es immer eine schlechte Wahl mit besseren Möglichkeiten zur Verfügung?

Nun wird das Beispiel dafür, warum ich denke, kann es für gut verwendet werden.

Ich habe vor kurzem Bencoding aus dem BitTorrent-Protokoll implementiert in C #. Ein einfaches genug Problem, wie die Daten zu repräsentieren. Ich wählte es auf diese Art und Weise zu tun,

Wir haben eine abstract BItem-Klasse, die einige grundlegende Funktionalität, einschließlich der static BItem Decode(string), die eine Bencoded Zeichenfolge in die notwendige Struktur zu entschlüsseln verwendet wird.

Darüber hinaus gibt es vier abgeleiteten Klassen, BString, BInteger, BList und BDictionary, die vier verschiedenen Datentypen darstellen, die codiert werden. Nun, hier ist der schwierige Teil. BList und BDictionary haben this[int] und this[string] Accessoren jeweils Zugriff auf die Array-ähnliche Qualitäten dieser Datentypen zu ermöglichen.

Der potenziell schreckliche Teil kommt jetzt:

BDictionary torrent = (BDictionary) BItem.DecodeFile("my.torrent");
int filelength = (BInteger)((BDictionary)((BList)((BDictionary)
             torrent["info"])["files"])[0])["length"];

Nun, erhalten Sie das Bild ... Autsch, das auf den Augen schwer, nicht das Gehirn zu erwähnen. So stellte ich etwas extra in die abstrakte Klasse:

public BItem this[int index]
{
    get { return ((BList)this)[index]; }
}
public BItem this[string index]
{
    get { return ((BDictionary)this)[index]; }
}

Jetzt können wir den alten Code umschreiben als:

BDictionary torrent = (BDictionary)BItem.DecodeFile("my.torrent");
int filelength = (BInteger)torrent["info"]["files"][0]["length"];

Wow, schwupps, viel besser lesbar Code. Aber habe ich nur ein Teil meiner Seele verkaufen für was bedeutet, Wissen von Unterklassen in der abstrakten Klasse?

EDIT: Als Antwort auf einige der Antworten auf sich warten, Sie völlig aus der Bahn für diese Frage sind, da die Struktur variabel ist, zum Beispiel mein Beispiel torrent["info"]["files"][0]["length"] gültig ist, aber so ist torrent["announce-list"][0][0], und beide würden in 90% der Torrent-Dateien gibt. Generics ist nicht der Weg zu gehen, um mit diesem Problem atleast :(. Haben Sie einen Klick durch die spec I verbunden ist, ist es nur 4 kleine Punkt-Punkte groß.

War es hilfreich?

Lösung

Ich glaube, ich würde das diese [int] machen und diese [string] Accessoren virtuellen und überschreiben sie in blist / BDictionary. Klassen, in denen die Accessoren keinen Sinn machen, sollte eine NotSupportedException werfen () (vielleicht durch eine Standardimplementierung in BItem hat).

Das macht Ihren Code Arbeit in der gleichen Art und Weise und gibt Ihnen einen besser lesbaren Fehler, wenn Sie schreiben sollen

 (BInteger)torrent["info"][0]["files"]["length"];

aus Versehen.

Andere Tipps

Sie sollten wirklich keine abgeleiteten Klassen von der Basisklasse zugreifen, wie es der Idee des OOP ziemlich bricht. Lesbarkeit geht sicherlich einen langen Weg, aber ich würde es nicht für Wiederverwertbarkeit handeln. Betrachten wir den Fall, wenn Sie benötigen eine andere Unterklasse hinzufügen -. Sie werden auch entsprechend die Basisklasse aktualisieren müssen

Wenn die Dateilänge ist etwas, das man oft abgerufen werden, warum nicht eine Eigenschaft in der BDictionary implementieren Klasse ... so, dass Sie Code wird (?):

BDictionary torrent = BItem.DecodeFile("my.torrent");
int filelength = torrent.FileLength;

So wird die Implementierungsdetails vor dem Benutzer verborgen werden.

So wie ich es sehe, nicht alle BItems sind Sammlungen, so dass nicht alle BItems haben Indexer, so sein der Indexer nicht in BItem. Ich würde eine andere abstrakte Klasse von BItem ableiten, lassen Sie uns es BCollection nennen, und legte die Indexer gibt, so etwas wie:

abstract class BCollection : BItem {

      public BItem this[int index] {get;}
      public BItem this[string index] {get;}
}

und machen blist und BDictionary von BCollection erben. Oder man könnte die extra Meile gehen und eine generische Klasse macht BCollection.

Meine Empfehlung wäre, mehr Abstraktionen einzuführen. Ich finde es verwirrend, dass ein BItem hat eine DecodeFile (), die eine BDictionary zurückgibt. Dies kann eine vernünftige Sache, in der Flut Domain zu tun, ich weiß es nicht.

Allerdings würde ich eine api wie folgt aus vernünftigen finden:

BFile torrent = BFile.DecodeFile("my.torrent");
int filelength = torrent.Length;

Bitte benutzen Sie bitte einen einfachen „Weg“ Parsen, so dass Sie es auf diese Weise schreiben konnte:

BDictionary torrent = BItem.DecodeFile("my.torrent");
int filelength = (int)torrent.Fetch("info.files.0.length");

Vielleicht nicht der beste Weg, aber die Lesbarkeit erhöht (ein wenig)

  • Wenn Sie die vollständige Kontrolle über Ihre Codebasis und Ihren Denkprozess, mit allen Mitteln zu tun.
  • Wenn nicht, werden Sie an diesem Tag einige neue Person bereuen spritzt eine BItem Ableitung, dass Sie nicht kommen in nicht sehen Ihre blist oder BDictionary.

Wenn Sie dies tun müssen, atleast wickeln Sie es (Kontrolle Zugriff auf die Liste) in einer Klasse, die Methodensignaturen stark typisierte hat.

BString GetString(BInteger);
SetString(BInteger, BString);

Akzeptieren und zurückkehren BStrings obwohl Sie es intern speichern in einem blist von BItems. (lassen Sie mich gespalten, bevor ich meine 2 B oder nicht 2 B machen)

Hmm. Ich würde behaupten, tatsächlich, dass die erste Zeile der codierten besser lesbar als die zweite ist - es dauert ein wenig länger, um herauszufinden, was auf es geht, aber es ist mehr apparant, die Sie Objekte als blist oder BDictionary sind zu behandeln. Die Anwendung der Methoden der abstrakten Klasse verbirgt dieses Detail, das es schwieriger, herauszufinden, machen kann, was Ihre Methode tatsächlich tut.

Wenn Sie Generika einführen, können Sie Gießen vermeiden.

class DecodedTorrent : BDictionary<BDictionary<BList<BDictionary<BInteger>>>>
{
}

DecodedTorrent torrent = BItem.DecodeFile("mytorrent");
int x = torrent["info"]["files"][0]["length"];

Hmm, aber das wird wahrscheinlich nicht funktionieren, da die Typen auf dem Weg abhängen können Sie die Struktur nehmen durch.

Ist es nur ich

BDictionary torrent = BItem.DecodeFile("my.torrent");int filelength = (BInteger)((BDictionary)((BList)((BDictionary)             torrent["info"])["files"])[0])["length"];

Sie brauchen nicht die BDictionary werfen ‚torrent‘ als BDictionary erklärt

public BItem this[int index]{&nbsp; &nbsp; get { return ((BList)this)[index]; }}public BItem this[string index]{&nbsp; &nbsp; get { return ((BDictionary)this)[index]; }}

Diese müssen acheive nicht das gewünschte Ergebnis, wie der Rückgabetyp noch die abstrat Version, so dass Sie immer noch werfen müssen.

Der neu geschriebene Code müßte sein

BDictionary torrent = BItem.DecodeFile("my.torrent");int filelength = (BInteger)((BList)((BDictionary)torrent["info"]["files"])[0])["length"];

Welches ist das genauso schlimm wie die erste Partie ist

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