Я пишу приложение для создания музыкальных фризов, но меня немного беспокоит структура классов и интерфейсов в коде. Здесь есть подписи некоторых классов, которые я написал:
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 представляет собой музыкальную фразу, часть полного фриза. Он выставляет в качестве свойств Key, Tempo и Meter, которые являются одноименными с их типами. Больше фраз, взятых вместе, создают хаос, представленный классом Fret. Каждый элемент в одной фразе должен быть форматируемым в соответствии со стандартной нотацией GUIDO, поэтому он должен реализовывать интерфейс IGuidoFormattable.
В другом пространстве имен определены некоторые классы мутаций, и все они наследуются от одного из двух абстрактных классов:
Class FretMutation
Class LambdaFretMutation : FretMutation
Наконец, существует класс с именем FretMutationGenerator, который имеет единственную задачу - применять выбранные мутации к музыкальной теме и выводить весь фриз в качестве экземпляра класса Fret.
FretPiece должен содержать несколько различных элементов (в данном случае ноты, паузы и аккорды), которые, тем не менее, должны удовлетворять двум ограничениям: они должны быть форматируемыми с помощью записи GUIDO и, следовательно, преобразовываться в значащие строки; они должны быть клонируемыми. В текущем коде каждый класс реализует ICloneable, но синтаксис и семантика текущего кода не гарантируют, что все члены коллекции являются клонируемыми. Мне нужно найти способ выразить оба ограничения без применения наследования к IGuidoFormattable и предпочтительно без определения метода Clone в интерфейсе IGuidoFormattable.
Вторая и самая важная проблема. FretMutation определяет абстрактный метод «Apply», который должен быть переопределен в каждом производном классе. Поэтому любой класс мутаций определяет свою собственную версию этого метода, которая имеет следующую сигнатуру:
FretPiece Apply(FretPiece originalTheme)
Он принимает в качестве входных данных FretPiece и выводит копию этого объекта, мутировавшего в соответствии со всеми другими параметрами, указанными в качестве членов класса. Я думаю, что это реализация модели стратегии. Однако, только потому, что этот метод создает копию входных данных, это означает, что сам аргумент (и, следовательно, все его члены) должен быть клонируемым.
Кроме того, FretPiece объявлен как список IGuidoFormattable, но каждый класс мутаций ведет себя иначе, чем другие, и может соответственно действовать на ноты, паузы или аккорды: это означает, что мне нужно проверить тип каждого элемента и написать другой код для каждый тип с «много» (на самом деле, не более 3), если заявления. И это кажется мне очень мало объектно-ориентированным.
Как я могу расположить классы и интерфейс таким образом, чтобы все они стали более объектно-ориентированными и менее зависимыми от предположений и проверки типов?