Pergunta

A pergunta que quero fazer é a seguinte:

Está derrubando a árvore de herança (ou seja,em direção a uma classe mais especializada) de dentro de uma classe abstrata desculpável, ou até mesmo uma coisa boa, ou é sempre uma má escolha com melhores opções disponíveis?

Agora, o exemplo de por que acho que pode ser usado para o bem.

Eu implementei recentemente Bencoding do protocolo BitTorrent em C#.Um problema bastante simples, como representar os dados.Eu escolhi fazer assim,

Nós temos um abstract BItem classe, que fornece algumas funcionalidades básicas, incluindo o static BItem Decode(string) que é usado para decodificar uma string Bencoded na estrutura necessária.

Existem também quatro classes derivadas, BString, BInteger, BList e BDictionary, representando os quatro tipos de dados diferentes que serão codificados.Agora, aqui está a parte complicada. BList e BDictionary ter this[int] e this[string] acessadores respectivamente para permitir acesso às qualidades semelhantes a array desses tipos de dados.

A parte potencialmente horrível está chegando agora:

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

Bem, você entendeu...Ai, isso é difícil para os olhos, sem falar no cérebro.Então, introduzi algo extra na classe abstrata:

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

Agora poderíamos reescrever esse código antigo como:

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

Uau, ei pronto, código MUITO mais legível.Mas acabei de vender parte da minha alma por implicar conhecimento de subclasses na classe abstrata?

EDITAR:Em resposta a algumas das respostas recebidas, você está completamente fora do caminho para esta questão específica, uma vez que a estrutura é variável, por exemplo, meu exemplo de torrent["info"]["files"][0]["length"] é válido, mas também é torrent["announce-list"][0][0], e ambos estariam em 90% dos arquivos torrent existentes.Genéricos não são a melhor opção, pelo menos com esse problema :(.Clique na especificação que vinculei, tem apenas 4 pequenos pontos grandes.

Foi útil?

Solução

Eu acho que eu faria o presente [int] e isso [cadeia] acessores virtual e substituí-los em BList / BDictionary. Classes onde os assessores não faz sentido deve lançar um NotSupportedException () (talvez por ter uma implementação padrão em Bitem).

Isso faz com que o seu código de trabalho da mesma forma e dá-lhe um erro mais legível no caso de você deve escrever

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

por engano.

Outras dicas

Você realmente não deve acessar quaisquer classes derivadas da classe base como É muito bonito quebra a idéia de OOP. Readibility certamente vai um longo caminho, mas eu não trocaria isso por reutilização. Considere o caso quando você vai precisar adicionar outra subclasse -. Você também vai precisar atualizar a classe base em conformidade

Se tamanho do arquivo é algo que você recuperar, muitas vezes, por que não implementar uma propriedade na classe BDictionary ... assim que você código torna-se (?):

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

Dessa forma, os detalhes de implementação são escondidos do usuário.

A forma como eu vejo, nem todos os BItems são coleções, assim, nem todos os BItems têm indexadores, para que o indexador não deve estar em Bitem. Eu iria derivar outra classe abstrata de Bitem, vamos nomeá-la BCollection, e colocar os indexadores lá, algo como:

abstract class BCollection : BItem {

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

e fazer BList e BDictionary Herdar do BCollection. Ou você poderia ir a milha e fazer BCollection extra de uma classe genérica.

A minha recomendação seria a introdução de mais abstrações. Acho que é confuso que uma Bitem tem uma DecodeFile () que retorna um BDictionary. Esta pode ser uma coisa razoável a fazer no domínio torrent, eu não sei.

No entanto, eu iria encontrar uma API como o seguinte mais razoável:

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

Você considerou a análise de um simples "caminho" para que você poderia escrevê-lo desta maneira:

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

Talvez não seja a melhor maneira, mas a legibilidade aumenta (um pouco)

  • Se você tem o controle completo de sua base de código e o seu processo de pensamento, por todos os meios fazer.
  • Se não, você vai se arrepender disso o dia algumas novas pessoa injeta uma derivação Bitem que você não viu entrando em o BList ou BDictionary.

Se você tiver que fazer isso, pelo menos envolvê-la (controle de acesso à lista) em uma classe que tem digitado fortemente assinaturas de método.

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

aceitar e retornar BStrings mesmo que você armazená-lo internamente em um BList de BItems. (deixe-me dividir antes de eu fazer o meu 2 B ou não 2 B)

Hmm. Eu realmente argumentar que a primeira linha de código é mais legível do que o segundo - é preciso um pouco mais de tempo para descobrir o que está acontecendo, mas é mais aparente que você está tratando os objetos como BList ou BDictionary. Aplicando os métodos para as peles classe abstrata que detalhe, o que pode tornar mais difícil para descobrir o que seu método está realmente fazendo.

Se você introduzir os genéricos, você pode evitar casting.

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

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

Hmm, mas que provavelmente não vai funcionar, como os tipos podem depender do caminho que você tomar através da estrutura.

É só eu

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

Você não precisa do BDictionary elenco 'torrent' é declarado como um 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]; }}

Estes não acheive o resultado desejado como o tipo de retorno ainda é a versão abstrat, assim você ainda tem que elenco.

O código reescrito teria que ser

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

Qual é a tão mau como o primeiro lote

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top