2011-02-09 2 views
3

J'écris une application pour composer des freizes musicaux, mais je m'inquiète un peu de la structure des classes et des interfaces au sein du code. Ici, il y a les signatures de quelques classes I écrivaient:Comment écrire un code clair et élégant pour une application basée sur un modèle de stratégie?

Interface IGuidoFormattable 
Class Note : IGuidoFormattable, ICloneable 
Class Pause : IGuidoFormattable, ICloneable 
Class Chord : List<Note>, IGuidoFormattable, ICloneable 
Class Key : IGuidoFormattable, ICloneable 
Class Tempo : IGuidoFormattable, ICloneable 
Class Meter : IGuidoFormattable, ICloneable 
Class FretPiece : List<IGuidoFormattable>, ICloneable 
Class Fret : List<FretPiece> 

FretPiece représente une phrase muscial, un morceau de la freize complète. Il expose comme propriétés Key, Tempo et Meter, qui sont homonymes avec leurs types. Plus de phrases réunies créent un freize, représenté par la classe Fret. Chaque élément dans une seule expression doit être formatable conformément à la notation standard GUIDO, par conséquent, il doit implémenter l'interface IGuidoFormattable. Dans un autre espace de noms, certaines classes de mutation sont définis et ils héritent tous d'une des deux classes abstraites:

Class FretMutation 
Class LambdaFretMutation : FretMutation 

Enfin, il existe une classe appelée FretMutationGenerator qui a la seule tâche d'appliquer des mutations sélectionnées sur un thème musical et affiche la totalité de freize en tant qu'instance de la classe Fret. FretPiece doit pouvoir contenir plusieurs éléments différents (notes, pauses et accords dans ce cas), qui doivent néanmoins satisfaire à deux contraintes: ils doivent être formatables avec la notation GUIDO et donc transformés en chaînes significatives; ils doivent être clonables. Dans le code tel qu'il est maintenant, chaque classe implémente ICloneable, mais la syntaxe et la sémantique du code actuel ne permettent pas de cloner tous les membres de la collection. J'ai besoin de trouver un moyen d'exprimer les deux contraintes sans appliquer d'héritage à IGuidoFormattable et de préférence sans définir de méthode Clone dans l'interface IGuidoFormattable.

Deuxièmement, et le plus important, le problème. FretMutation définit une méthode abstraite, "Apply", qui doit être surchargée dans chaque classe dérivée. Par conséquent, toute classe de mutation définit sa propre version de cette méthode, qui a la signature suivante:

FretPiece Apply(FretPiece originalTheme) 

Il accepte en entrée un FretPiece et envoie une copie de cet objet, muté selon tous les autres paramètres spécifiés en tant que membres de la classe. Je pense que c'est une implémentation du modèle de stratégie. Cependant, seulement du fait que cette méthode crée une copie de l'entrée, cela signifie que l'argument lui-même (et donc tous ses membres) doit être clonable. En outre, FretPiece est déclaré comme une liste de IGuidoFormattable, mais chaque classe de mutation se comporte différemment des autres et peut agir sur des notes, des pauses ou des accords en conséquence: cela signifie que je dois vérifier le type de chaque élément et écrire un code différent pour chaque type avec "beaucoup" (en effet, 3 au plus) si déclarations. Et cela me semble très peu orienté objet.

Comment puis-je organiser les classes et l'interface de sorte que tous deviennent plus orientés objet et moins dépendants des hypothèses et des vérifications de type?

+0

Répondre correctement à cette question nécessite de comprendre comment ces types sont tous connectés (c'est-à-dire, un accord est simplement un ensemble de notes, etc.). Avez-vous regardé l'utilisation de [GUIDOLib] (http://guidolib.sourceforge.net/) (C++)? À tout le moins, vous pourriez être capable de l'utiliser pour voir comment organiser vos classes. – Justin

+0

Je ne peux pas lire de musique mais je pense qu'il y a d'énormes différences conceptuelles entre les notes, la pause, les accords et le tempo (je n'ai aucune idée de ce qu'une clé pourrait être). ce contenu. Donc un nœud peut être influencé par le tempo mais pas le tempo lui-même ne peut pas être influencé par une note. Je crois que vous devriez repenser un peu la structure de votre classe pour refléter cela. – Glenner003

+0

Vous avez peut-être mal compris ce que je voulais dire, puisque j'ai intentionnellement fourni presque aucun code. Tempo spécifie le nombre de battements par minute et la durée digure qui représente un temps. La clé est la tonalité de la composition (ex: C Major, G Minor, ...). Meter indique la signature temporelle. Meter, Tempo et Key sont des propriétés de la classe FretPiece, représentant une phrase musicale. Les notes, les pauses et les accords sont des éléments de cette phrase. Un accord est ici représenté comme une simple liste de notes. Toutes les fonctions de formatage en notation GUIDO et de conversion en fichier MIDI sont déjà écrites. – Totem

Répondre

3

Je dois trouver un moyen d'exprimer à la fois contrainte sans appliquer l'héritage à IGuidoFormattable et de préférence sans définir une méthode Clone dans l'interface IGuidoFormattable

Et la troisième option?

public interface ICloneableAndGuidoFormattable : IGuidoFormattable, ICloneable { } 

Ensuite, votre FretPiece est Liste des ICloneableAndGuidoFormattable

Sinon, vous pouvez essayer une telle construction:

public interface ICloneable<T> 
{ 
    T Clone(); 
} 

public class FretPiece : IEnumerable<IFormattable>, ICloneable<FretPiece> 
{ 
    private List<IFormattable> items = new List<IFormattable>(); 

    public void Add<T>(T value) where T : IFormattable, ICloneable<IFormattable> 
    { 
     items.Add(value); 
    } 

    public IEnumerator<IFormattable> GetEnumerator() 
    { 
     items.GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 

    public FretPiece Clone() 
    { 
     return new FretPiece { items = new List<IFormattable>(
      items.Cast<ICloneable<IFormattable>>().Select(c=>c.Clone())) 
     }; 
    } 
} 

Et ailleurs par exemple sur votre mutateur:

public T Apply<T>(T fretPiece) where T : IEnumerable<IFormattable>, ICloneable<T> (...) 

Ceci vous assurerait que vous ne pouvez ajouter que des éléments implémentant les deux interfaces. l'énumération suppose uniquement que les IFormattables sont renvoyés. Cela vous permet de transtyper en ICloneable en toute sécurité, car il doit avoir passé la contrainte de type sur "Ajouter". Vous pouvez voir l'implémentation de clone. Même si vous avez un casting là-bas, il est sûr à moins que quelqu'un fout avec items basé sur la réflexion;)

+0

J'ai écrit "J'ai besoin de trouver un moyen d'exprimer les deux contraintes sans appliquer d'héritage à IGuidoFormattable", mais votre deuxième solution semble fonctionner assez bien comme je l'ai voulu. Que pourriez-vous dire à propos de la question la plus importante des mutations? C'est à dire. comment puis-je écrire un code pour appliquer la méthode sans vérifier le type de chaque élément de la collection (supposé qu'il est clonable et formatable, de toute façon)? – Totem

+0

Non sûr mais Apply peut également être défini générique et définir des contraintes de paramètres. Par conséquent, la classe ci-dessus peut également implémenter ICloneable , voir mise à jour de ma réponse, renommé Foo, et laissez-le implémenter l'interface – flq

+0

In ne fonctionnera pas. Seulement à l'exécution, je connais le type d'élément sur lequel j'utilise Apply, donc je ne peux pas déclarer le type de génériques avant la compilation. Mais je pense que j'ai trouvé une solution discrète entre-temps. Merci – Totem

2

Je voudrais rendre plusieurs d'entre eux immuables. De cette façon, vous n'avez plus besoin de les cloner. Une note est toujours la même note, peu importe où elle apparaît. Il est donc naturel de le rendre immuable. Par exemple Note, Pause, Chord semblent fonctionner comme des types immuables. La plupart de vos autres noms de classe sont charabia pour moi puisque je ne connais pas assez la théorie musicale, donc je ne sais pas lequel d'entre eux pourrait être immuable aussi.

+0

Non. Les objets immuables sont exclus.Par exemple, vous pourriez avoir une feuille de musique avec plusieurs notes imprimées dessus: on peut dire que la première et la cinquième notes sont toutes deux B d'octave centrale, mais l'une est la première note de la pièce et dure 1/4 (et peut-être c'est staccato), et le dernier est le cinquième et dure, disons, 1/8 et est pointillé. Les noms de notes sont immuables, mais les notes ne le sont pas. Merci pour la réponse, de toute façon. – Totem

+1

@Totem Qu'est-ce qui vous empêche de faire une note composée de plusieurs propriétés, une pour la longueur et une pour la fréquence et peut-être une pour l'instrument? OMI la note n'a pas besoin de savoir si c'est la cinquième note de la pièce, ou la première ou les deux en même temps. – CodesInChaos

+0

Enfait la note ne sait pas et n'a pas besoin de savoir (et j'ajouterais qu'elle ne doit pas non plus savoir). C'est sa position dans la pièce qui fait la différence. En effet, vous avez raison, je pourrais incapsuler l'information de nom de note, accidentals et octave dans une classe immutable comme elle-même. Mais une note peut toujours avoir une durée, des points, des expressions (staccato, legato, marcato, etc ...) et beaucoup plus de propriétés qui lui sont propres et ne dépendent pas de sa hauteur. Une classe Note immuable est donc logiquement impossible. – Totem

Questions connexes