Je crée un modèle d'objet pour un périphérique contenant plusieurs canaux. Les noms utilisés entre le client et moi sommes Channel
et ChannelSet
. ("Set" n'est pas sémantiquement précis, car il est commandé et un ensemble approprié n'est pas. Mais c'est un problème pour une heure différente.)
J'utilise c #. Voici un exemple d'utilisation 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
Tout est dandy. Toutefois, les clients ne sont pas des programmeurs et ils absolument seront confondus par une indexation zéro - la première chaîne est une chaîne 1 à elles. Mais, Pour des raisons de cohérence avec C #, je veux garder le ChannelSet
indexé de zéro.
Ceci est sûr de causer des déconnexes entre mon équipe de développement et les clients lorsqu'ils interagissent. Mais pire, toute incohérence dans la manière dont cela est traité dans le codeBase est un problème potentiel. Par exemple, voici un écran d'interface utilisateur où l'utilisateur final ( qui pense en termes d'indexation ) est la modification du canal 13:
Ce bouton Save
va éventuellement entraîner un code. Si ChannelSet
est 1 indexé:
channels.GetChannel(13).SomeProperty = newValue; // notice: 13
ou Ceci s'il est zéro indexé:
channels.GetChannel(12).SomeProperty = newValue; // notice: 12
Je ne suis pas vraiment sûr comment gérer cela. J'ai l'impression que c'est une bonne pratique de conserver une liste de choses commandée et indexée entier (le ChannelSet
) compatible avec toutes les autres interfaces de tableau et de liste de l'univers C # (par zéro indexation ChannelSet
). Mais ensuite, chaque morceau de code entre l'interface utilisateur et le backend aura besoin d'une traduction (soustraire de 1), et nous savons tous à quel point les erreurs insidieuses et communes sont déjà insidieuses et communes.
Donc, a une décision comme celle-ci jamais mordue? Devrais-je indexer ou un index?
Il se sent comme si vous vous confondez l'identifiant pour le Channel
avec sa position dans un ChannelSet
. Ce qui suit est ma visualisation de la façon dont votre code/commentaires examinerait le moment:
public sealed class ChannelSet
{
private Channel[] channels;
/// <summary>Retrieves the specified channel</summary>
/// <param name="channelId">The id of the channel to return</param>
Channel GetChannel(int channelId)
{
return channels[channelId-1];
}
}
Il se sent comme si vous avez décidé que parce que Channel
s dans un ChannelSet
sont identifiés par des chiffres qui ont une majuscule et limité inférieure, ils doivent être des index et donc comme il est basé sur C #, 0. Si le moyen naturel de faire référence à chacun des canaux est d'un nombre compris entre 1 et X, reportez-vous à celui d'un nombre compris entre 1 et X. N'essayez pas de les forcer à être indexés.
Si vous voulez vraiment fournir un moyen d'y accéder par l'index basé sur 0 (quel avantage cela donne-le à votre utilisateur final ou de développeurs qui consomment le code?) Puis implémentez un indexer :
public sealed class ChannelSet
{
private Channel[] channels;
/// <summary>Retrieves the specified channel</summary>
/// <param name="channelId">The id of the channel to return</param>
public Channel GetChannel(int channelId)
{
return channels[channelId-1];
}
/// <summary>Return the channel at the specified index</summary>
public Channel this[int index]
{
return channels[index];
}
}
Affiche l'interface utilisateur avec l'index 1, utilisez index 0 en code.
Cela dit, j'ai travaillé avec des périphériques audio tels que celui-ci et utilisé index 1 pour les canaux et conçu le code de ne jamais utiliser "index" ou indexeur pour éviter les frustrations. Certains programmeurs se sont toujours plaints, alors nous l'avons changé. Ensuite, d'autres programmeurs se sont plaints.
Il suffit de prendre un et de coller avec elle. Il y a de plus gros problèmes à résoudre dans le grand schéma d'obtenir des logiciels sur la porte.
Utilise les deux.
Ne mélangez pas l'interface utilisateur avec votre code de base. En interne (en tant que bibliothèque), vous devez coder "sans savoir" comment chaque élément de la matrice sera appelé par l'utilisateur final. Utilisez les tableaux et collections "naturels" à 0.
La partie du programme qui rejoint les données avec l'interface utilisateur, la vue doit veiller à traduire correctement les données entre le modèle mental de l'utilisateur et la "bibliothèque" qui effectue le travail réel.
Pourquoi est-ce mieux?
Vous pouvez voir la collection de deux angles différents.
(1) C'est en premier lieu une collection régulière séquentielle, comme une matrice ou une liste. Index de 0
est évidemment la bonne solution alors, à la suite de la convention. Vous allouez suffisamment d'entrées et de numéro de canal de carte aux indices, ce qui est trivial (il suffit de soustraire 1
).
(2) Votre collection est essentiellement un mappage entre les identificateurs de canaux et les objets d'information de canal. Il arrive simplement que les identificateurs de canaux constituent une plage séquentielle d'entiers; Demain, ce pourrait être quelque chose comme [1, 2, 3, 3a, 4, 4.1, 6, 8, 13]
. Vous utilisez cet ensemble commandé comme touches de mappage.
Choisissez une des approches, de le documenter et de le coller. Du point de vue de la flexibilité, j'irais avec (2), car la représentation du numéro de canal (au moins le nom affichée) est relativement susceptible de changer à l'avenir.
Vous mélangez deux concepts: indexation et identité. Ils ne sont pas la même chose et ne devraient pas être confondus.
Quel est le but de l'indexation? Accès aléatoire rapide. Si la performance n'est pas un problème, et étant donné votre description, vous n'utilisez pas une collection qui a un indice est inutile et probablement accidentelle.
Si vous (ou votre équipe) est confondu par l'index vs l'identité, vous pouvez alors résoudre ce problème en ne présentant pas d'index. Utilisez un dictionnaire ou une énorme et obtenez le canal par la valeur.
channels.First(c=> c.Number == identity).SomeProperty = newValue;
channels[identity].SomeProperty = newValue;
Le premier est plus clair, le second est plus court - ils peuvent être traduits ou non dans le même IL, mais de toute façon, étant donné que vous utilisez cela pour une liste déroulante, il devrait être assez rapide.
Vous exposez une interface via votre classe ChannelSet
que vous vous attendez à ce que vos utilisateurs interagissent. Je le ferais aussi naturel pour eux d'utiliser le plus possible. Si vous attendez que vos utilisateurs commencent à compter de 1 au lieu de 0, exposez cette classe et son utilisation avec cette attente à l'esprit.
L'indexation basée sur 0 était populaire et défendue par des personnes importantes, car la déclaration indique la taille de l'objet.
Avec des ordinateurs modernes, avec les ordinateurs que vos utilisateurs utiliseront, c'est la taille de l'objet important du tout?
Si votre languge (C #) ne prend pas en charge une manière propre de déclarer le premier et dernier élément d'un tableau, vous pouvez simplement déclarer un tableau plus grand et utiliser des constantes déclarées (ou une variable dynamique) à identifier au début et à la fin de la zone active du tableau.
Pour les objets d'interface utilisateur, vous pouvez presque certainement vous permettre d'utiliser un objet de dictionnaire pour votre mappage (si vous avez 10 millions d'objets, vous avez un problème d'interface utilisateur) et si le meilleur objet dictionnaire s'avère être une liste avec Espace gaspillé à l'autre extrémité, vous n'avez rien perdu important.