Estou criando um modelo de objeto para um dispositivo que possui vários canais. Os substantivos usados entre o cliente e eu são Channel
e ChannelSet
. ("Conjunto" não é semanticamente preciso, porque é pedido e um conjunto adequado não é. Mas isso é um problema para um tempo diferente.)
Eu estou usando c #. Aqui está um exemplo de uso de ChannelSet
:
// load a 5-channel ChannelSet
ChannelSet channels = ChannelSetFactory.FromFile("some_5_channel_set.json");
Console.Write(channels.Count);
// -> 5
foreach (Channel channel in channels) {
Console.Write(channel.Average);
Console.Write(", ");
}
// -> 0.3, 0.3, 0.9, 0.1, 0.2
Tudo é dândi. No entanto, os clientes não são programadores e absolutamente ficarão confusos com a indexação zero - o primeiro canal é o canal 1 para eles. Mas, por uma questão de consistência com o C #, quero manter o ChannelSet
indexado do zero .
Isso certamente causará algumas desconexões entre minha equipe de desenvolvimento e os clientes quando eles interagem. Porém, pior ainda, qualquer inconsistência em como isso é tratado na base de código é um problema em potencial. Por exemplo, aqui está uma tela da interface do usuário em que o usuário final ( que pensa em termos de 1 indexação ) está editando o canal 13:
Esse Save
botão acabará resultando em algum código. Se ChannelSet
for 1 indexado:
channels.GetChannel(13).SomeProperty = newValue; // notice: 13
ou isso se for zero indexado:
channels.GetChannel(12).SomeProperty = newValue; // notice: 12
Não tenho muita certeza de como lidar com isso. Eu sinto que é uma boa prática manter uma lista ordenada e indexada por números inteiros ( ChannelSet
consistente) com todas as outras interfaces de matriz e lista no universo C # (por indexação zero ChannelSet
). Mas todo código entre a interface do usuário e o back-end precisará de uma tradução (subtrair por 1), e todos sabemos como os erros off-by-one comuns e insidiosos já são.
Então, uma decisão como essa já te mordeu? Devo zerar o índice ou um índice?
fonte
Respostas:
Parece que você está confundindo o Identificador
Channel
com a sua posição dentro de aChannelSet
. A seguir, é minha visualização de como seu código / comentários ficaria no momento:Parece que você decidiu que, como
Channel
s dentro deChannelSet
são identificados por números que têm um limite superior e inferior, eles devem ser índices e, portanto, como é baseado em C #, 0. Se a maneira natural de se referir a cada um dos canais for por um número entre 1 e X, consulte-os por um número entre 1 e X. Não tente forçá-los a serem índices.Se você realmente deseja fornecer uma maneira de acessá-los por índice baseado em 0 (que benefício isso oferece ao usuário final ou aos desenvolvedores que consomem o código?), Implemente um Indexador :
fonte
Mostre a interface do usuário com o índice 1, use o índice 0 no código.
Dito isso, trabalhei com dispositivos de áudio como esse e usei o índice 1 para os canais e projetei o código para nunca usar "Index" ou indexadores para ajudar a evitar frustrações. Alguns programadores ainda reclamaram, então nós mudamos. Outros programadores reclamaram.
Basta escolher um e ficar com ele. Existem problemas maiores a serem resolvidos no grande esquema de obter software pela porta.
fonte
Option Base
bobo, eu gostei do recurso de alguns idiomas oferecidos para permitir que as matrizes especifiquem individualmente limites inferiores arbitrários. Se alguém possui uma matriz de dados por ano, por exemplo, além da capacidade de dimensionar uma matriz[firstYear..lastYear]
pode ser melhor do que ter que sempre acessar o elemento[thisYear-firstYear]
.Use ambos.
Não misture a interface do usuário com seu código principal. Internamente (como uma biblioteca), você deve codificar "sem saber" como cada elemento na matriz será chamado pelo usuário final. Use as matrizes e coleções "naturais" indexadas em 0.
A parte do programa que une os dados à interface do usuário, a visualização, deve ter o cuidado de traduzir corretamente os dados entre o Modelo Mental do Usuário e a 'Biblioteca' que faz o trabalho real.
Por que isso é melhor?
fonte
Você pode ver a coleção de dois ângulos diferentes.
(1) É, em primeiro lugar, uma coleção seqüencial regular , como uma matriz ou uma lista. O índice de
0
é obviamente a solução certa, seguindo a convenção. Você aloca entradas suficientes e mapeia o número do canal para índices, o que é trivial (apenas subtraia1
).(2) Sua coleção é essencialmente um mapeamento entre identificadores de canal e objetos de informação de canal. Acontece que os identificadores de canal são um intervalo seqüencial de números inteiros; amanhã pode ser algo parecido
[1, 2, 3, 3a, 4, 4.1, 6, 8, 13]
. Você usa esse conjunto ordenado como chaves de mapeamento.Escolha uma das abordagens, documente-a e cumpra-a. Do ponto de vista da flexibilidade, eu usaria (2), porque a representação do número do canal (pelo menos o nome exibido) é relativamente provável de mudar no futuro.
fonte
channels[int]
) será zero de inteiros indexados, e os assessores ObterGetChannelByFrequency
,GetChannelByName
,GetChannelByNumber
será flexível.Dictionary
ouSortedDictionary
.operator [] (int index)
deveria ser baseado em 0, enquantooperator [] (IOrdered index)
não seria. (Desculpas para a sintaxe aproximada, o meu C # é muito enferrujado.)[]
em uma linguagem que usa essa sobrecarga para pesquisa por chave arbitrária, os programadores não devem assumir que as chaves iniciam0
e são contíguas e devem abandonar esse hábito. Eles podem começar a partir de0
, ou1
, ou1000
, ou a cadeia de caracteres"Channel1"
, esse é o objetivo de usar o operador[]
com chaves arbitrárias. OTOH, se esse fosse C e alguém estivesse dizendo "devo deixar um elemento0
em uma matriz sem uso para começar1
", então eu não diria "obviamente, sim", seria por um triz, mas eu preferiria "não".Todos e seus cães usam índices baseados em zero. O uso de índices baseados em um aplicativo só causará problemas de manutenção para sempre.
Agora, o que você exibe em uma interface do usuário depende inteiramente de você e seu cliente. Apenas exiba i + 1 como o número do canal, junto com o canal #i, se isso deixar seu cliente feliz.
Se você expõe uma classe, expõe-a aos programadores. As pessoas confundidas com um índice baseado em zero não são programadores, portanto, há uma desconexão aqui.
Você parece se preocupar com a conversão entre interface do usuário e código. Se isso lhe preocupa, crie uma classe com a representação da interface do usuário de um número de canal. Uma chamada transformando o número 12 na representação da interface do usuário "Canal 13" e uma chamada transformando "Canal 13" no número 12. Você deve fazer isso de qualquer maneira, caso o cliente mude de idéia, para que haja apenas duas linhas de código mudar. E funciona se o cliente solicitar números romanos ou letras de A a Z.
fonte
Você está misturando dois conceitos: indexação e identidade. Eles não são a mesma coisa e não devem ser confundidos.
Qual é o objetivo da indexação? Acesso aleatório rápido. Se o desempenho não for um problema e, dada a sua descrição, não for necessário, usar uma coleção com um índice é desnecessário e provavelmente acidental.
Se você (ou sua equipe) estiver confuso com o índice versus a identidade, poderá resolver esse problema sem ter um índice. Use um dicionário ou um enumerável e obtenha o canal por valor.
O primeiro é mais claro, o segundo é mais curto - eles podem ou não ser traduzidos para a mesma IL, mas, de qualquer forma, dado que você está usando isso para uma lista suspensa, deve ser rápido o suficiente.
fonte
Você está expondo uma interface através de sua
ChannelSet
classe com a qual espera que seus usuários interajam. Eu faria o mais natural possível para eles usarem. Se você espera que seus usuários comecem a contar de 1 em vez de 0, exponha essa classe e seu uso com essa expectativa em mente.fonte
A indexação baseada em 0 era popular e defendida por pessoas importantes, porque a declaração mostra o tamanho do objeto.
Nos computadores modernos, com os computadores que seus usuários estarão usando, o tamanho do objeto é importante?
Se o seu idioma (C #) não suportar uma maneira limpa de declarar o primeiro e o último elemento de uma matriz, você poderá declarar uma matriz maior e usar constantes declaradas (ou variáveis dinâmicas) para identificar o início e o fim lógicos de uma matriz. a área ativa da matriz.
Para objetos de interface do usuário, você quase certamente pode se dar ao luxo de usar um objeto de dicionário para seu mapeamento (se você tiver 10 milhões de objetos, um problema de interface do usuário) e se o melhor objeto de dicionário for uma lista com espaço desperdiçado em cada extremidade, você não perdeu nada importante.
fonte