Pregunta

La pregunta que quiero hacer es por lo tanto:

Se derriba el árbol de herencia (es decir.hacia una más specialiased clase) desde el interior de una clase abstracta es excusable, o incluso una buena cosa, o es siempre una mala elección con la mejor de las opciones disponibles?

Ahora, el ejemplo de por qué creo que puede ser utilizado para el bien.

Recientemente he implementado Bencoding del protocolo BitTorrent en C#.Un simple problema bastante, la forma de representar los datos.Elegí hacerlo de esta manera,

Tenemos una abstract BItem clase que proporciona la funcionalidad básica, incluyendo la static BItem Decode(string) que se utiliza para decodificar un Bencoded cadena en la estructura necesaria.

También hay cuatro clases derivadas, BString, BInteger, BList y BDictionary, que representan los cuatro tipos de datos diferentes que ser codificados.Ahora, aquí está la parte difícil. BList y BDictionary han this[int] y this[string] descriptores de acceso, respectivamente, para permitir el acceso a la matriz como de las cualidades de estos tipos de datos.

La potencialmente terribles parte que viene ahora:

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

Así, se obtiene la imagen...Ouch, eso es difícil en los ojos, por no mencionar el cerebro.Así que, me introdujo algo extra en el resumen de la clase:

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

Ahora podemos reescribir ese viejo código como:

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

Wow, hey presto, MUCHO más legible el código.Pero acabo de vender una parte de mi alma por lo que implica el conocimiento de las subclases en la clase abstracta?

EDITAR:En respuesta a algunas de las respuestas que vienen en usted está completamente fuera de la pista para esta pregunta en particular, ya que la estructura es variable, por ejemplo, mi ejemplo de torrent["info"]["files"][0]["length"] es válido, pero así es torrent["announce-list"][0][0], y ambos estarían en el 90% de los archivos torrent por ahí.Los medicamentos genéricos no es el camino a seguir, con este problema al menos :(.Tienen un clic a través de la especificación he enlazado, es sólo de 4 pequeños puntos-puntos grandes.

¿Fue útil?

Solución

Creo que me gustaría hacer la de este[int], y esta[string] descriptores de acceso virtual y los reemplace en BList/BDictionary.Clases donde los descriptores de acceso no tiene sentido, debería dar un NotSupportedException() (tal vez por tener una implementación predeterminada en BItem).

Que hace el código de trabajo de la misma manera y le da una más legible de error en caso de que usted debe de escribir

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

por error.

Otros consejos

Usted realmente no debe de acceso a cualquier clase derivada de la clase base como casi se rompe la idea de la programación orientada a objetos.Legibilidad sin duda va un largo camino, pero yo no los cambiaría por la reutilización.Considere el caso en que usted tendrá que agregar otra subclase - usted también tendrá que actualizar la base de la clase en consecuencia.

Si la longitud del archivo es algo que se puede recuperar a menudo, ¿por qué no implementar una propiedad en la BDictionary (?) clase...para que el código se convierte en:

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

De esa manera los detalles de implementación están ocultas para el usuario.

A mi modo de ver, no todos los BItems son colecciones, por lo tanto no todos los BItems han indizadores, por lo que el indizador no debería estar en BItem.Yo derivaría otra clase abstracta de BItem, pongamos nombre es BCollection, y poner los indexadores allí, algo como:

abstract class BCollection : BItem {

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

y hacer BList y BDictionary heredar de BCollection.O usted podría ir la milla extra y hacer BCollection una clase genérica.

Mi recomendación sería la de introducir más abstracciones.Me resulta confuso que una BItem tiene un DecodeFile() que devuelve un BDictionary.Esto puede ser una cosa razonable para hacer en el torrente de dominio, no sé.

Sin embargo, me gustaría encontrar a una api como la siguiente más razonable:

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

¿Concider análisis de un simple "camino" por lo que podría escribir de esta manera:

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

Tal vez no de la mejor manera, pero la legibilidad aumenta(un poco)

  • Si usted tiene el control completo de su base y su pensamiento, por todos los medios.
  • Si no, te arrepentirás el día alguna nueva persona inyecta una BItem derivación que usted no vio venir en su BList o BDictionary.

Si usted tiene que hacer esto, al menos se envuelve (control de acceso a la lista) en una clase que ha inflexible de las firmas de método.

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

Aceptar y volver BStrings aunque almacenar internamente en un BList de BItems. (déjame split antes de hacer mi 2 B o no 2 B)

Hmm.De hecho, me diría que la primera línea de código es más legible que la segunda se tarda un poco más de tiempo para averiguar lo que está pasando, pero su más aparente que usted está tratando de objetos como BList o BDictionary.La aplicación de los métodos de la clase abstracta oculta de ese detalle, que puede hacer que sea más difícil de averiguar lo que su método está haciendo en realidad.

Si usted introducir los medicamentos genéricos, usted puede evitar la proyección.

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

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

Hmm, pero que probablemente no funcionará, ya que los tipos pueden depender de la ruta que toma a través de la estructura.

Es que sólo me

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

Usted no necesita la BDictionary elenco de 'torrente' está declarada como una 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]; }}

Estos no obtener el resultado deseado como el tipo de retorno es todavía el abstrat versión, por lo que aún tiene que echar.

La reescribió el código tendría que ser

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

Que es tan malo como el primer lote

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top