Domanda

La domanda che voglio porre è così:

È colata giù l'eredità albero (ie.verso una più specialiased classe) dall'interno di una classe astratta scusabile, o anche una buona cosa, o è sempre una pessima scelta di opzioni migliori disponibili?

Ora, l'esempio del perché penso che possa essere usato per il bene.

Ho recentemente implementato Bencoding dal protocollo BitTorrent in C#.Un semplice problema abbastanza, come rappresentare i dati.Ho scelto di farlo in questo modo,

Abbiamo un' abstract BItem la classe, che fornisce alcune funzionalità di base, tra cui il static BItem Decode(string) che viene utilizzato per decodificare un Bencoded stringa in una struttura necessaria.

Ci sono anche quattro classi derivate, BString, BInteger, BList e BDictionary, che rappresentano i quattro diversi tipi di dati da codificare.Ora, qui è la parte difficile. BList e BDictionary sono this[int] e this[string] funzioni di accesso, rispettivamente, per consentire l'accesso all'array, come qualità di questi tipi di dati.

Potenzialmente terribili parte è venuta ora:

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

Beh, si ottiene l'immagine...Ouch, difficile per gli occhi, per non parlare del cervello.Così, ho introdotto qualcosa in più in classe astratta:

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

Ora potremmo riscrivere il vecchio codice:

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

Wow, oplà, MOLTO più leggibile il codice.Ma mi ha fatto solo per vendere parte della mia anima, per il che implica la conoscenza delle sottoclassi in classe astratta?

EDIT:In risposta ad alcune delle risposte che arrivano, sei completamente fuori pista per questa particolare questione, poiché la struttura è variabile, per esempio il mio esempio di torrent["info"]["files"][0]["length"] è valido, ma così è torrent["announce-list"][0][0], ed entrambi sarebbe nel 90% dei file torrent là fuori.Generics non è il modo per andare, con questo problema almeno :(.Un clic per la spec che ho collegato solo 4 piccolo punto-punti di grandi dimensioni.

È stato utile?

Soluzione

Penso che vorrei fare in questo[int] e questo[string] funzioni di accesso virtuale ed eseguire l'override in Vesciche/BDictionary.Le classi in cui le funzioni di accesso non ha senso dovrebbe lanciare un NotSupportedException() (forse avendo una implementazione di default in BItem).

Che rende il lavoro di codice nello stesso modo e ti dà una più leggibile di errore nel caso in cui si dovrebbe scrivere

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

per errore.

Altri suggerimenti

Si dovrebbe davvero non accedere a qualsiasi classi derivate dalla classe base come praticamente si rompe l'idea di OOP.Readibility certamente va un lungo cammino, ma io non lo cambierei per il riutilizzo.Consideriamo il caso in cui avrete bisogno di aggiungere un'altra sottoclasse avrete anche bisogno di aggiornare la classe base di conseguenza.

Se la lunghezza del file è qualcosa di recuperare spesso, perché non implementare una proprietà in BDictionary (?) classe...in modo che il codice diventa:

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

Che modo l'attuazione dettagli sono nascosti all'utente.

A mio modo di vedere, non tutte BItems sono raccolte, quindi non tutti i BItems hanno indicizzatori, così l'indicizzatore non dovrebbe essere in BItem.Vorrei ricavare un'altra classe astratta da BItem, diamo il nome BCollection, e mettere gli indicizzatori c'è, qualcosa come:

abstract class BCollection : BItem {

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

e fare Vesciche e BDictionary ereditare da BCollection.Oppure si potrebbe andare il miglio supplementare e fare BCollection una classe generica.

Il mio consiglio sarebbe quello di introdurre ulteriori astrazioni.Io la trovo confusionaria e un BItem ha un DecodeFile() che restituisce un BDictionary.Questo può essere una cosa ragionevole da fare nel torrente dominio, non so.

Tuttavia, vorrei trovare un'api come la più ragionevole:

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

Hai concider l'analisi di un semplice "percorso", così si potrebbe scrivere in questo modo:

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

Forse non è il modo migliore, ma la leggibilità aumenta(un po')

  • Se hai il controllo completo del vostro codice e il tuo pensiero-processo, con tutti i mezzi farlo.
  • Se non ti dispiace questo giorno una persona nuova si inietta una BItem derivazione che non vede l'entrata in il Vesciche o BDictionary.

Se devi fare questo, almeno per avvolgere (controllo di accesso per l'elenco) in una classe che ha fortemente tipizzato metodo di firme.

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

Accettare e restituire BStrings anche se internamente il negozio è in Vesciche di BItems. (mi permetta di dividere prima di fare il mio 2 B o non B 2)

Hmm.Vorrei discutere del fatto che la prima riga di codice è più leggibile rispetto al secondo - ci vuole un po ' di più per capire che cosa sta andando su di esso, ma è più apparente che si sta trattando oggetti come Vesciche o BDictionary.Applicare i metodi per la classe astratta che nasconde dettagli che possono rendere più difficile capire che cosa il vostro metodo è in realtà facendo.

Se si introducono i generics, si può evitare di casting.

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

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

Hmm, ma che probabilmente non funzionerà, come i tipi, che può dipendere dal percorso che si prende attraverso la struttura.

È solo a me

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

Non avete bisogno di un BDictionary cast 'torrent' è dichiarato come un BDictionary

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

Questi non ottenere il risultato desiderato, in quanto il tipo di ritorno è ancora il abstrat versione, così da avere ancora il cast.

Riscritto il codice dovrebbe essere

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

Che è proprio così male come il primo lotto

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top