我正在编写一个组成音乐freize的应用程序,但我有点担心代码中的类和接口的结构。这里有我写的一些类的签名:
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代表一个muscial短语,一个完整的freize。它公开为属性Key,Tempo和Meter,它们与它们的类型是同音。更多的短语放在一起创造了一个由Fret类提出的freize。单个短语中的每个元素必须符合GUIDO标准符号的格式化,因此必须实现IGuidoFormattable接口。 在另一个名称空间中,定义了一些变异类,它们都继承自两个抽象类之一:
Class FretMutation
Class LambdaFretMutation : FretMutation
最后,存在一个名为FretMutationGenerator的类,其唯一的任务是将选定的突变应用于音乐主题,并将整个freize输出为Fret类的实例。
FretPiece必须能够包含几个不同的元素(在这种情况下是音符,暂停和和弦),但它们必须满足两个约束条件:它们必须具有GUIDO表示法的格式化,因此转换为有意义的字符串;他们必须是可复制的。在现在的代码中,每个类都实现ICloneable,但是当前代码的语法和语义不允许该集合的所有成员都是可克隆的。我需要找到一种方法来表达两种约束而不将继承应用于IGuidoFormattable,最好不要在IGuidoFormattable接口中定义Clone方法。
第二,也是最重要的问题。 FretMutation定义了一个抽象方法“Apply”,必须在每个派生类中重写。因此,任何变异类都定义了自己的此方法版本,该方法具有以下签名:
FretPiece Apply(FretPiece originalTheme)
它接受FretPiece作为输入并输出该对象的副本,根据指定为类成员的所有其他参数进行变异。我认为这是战略模式的实施。但是,只是因为此方法创建了输入的副本,这意味着参数本身(以及因此其所有成员)必须是可克隆的。 另外,FretPiece被声明为IGuidoFormattable的列表,但是每个突变类的行为都与其他突变类不同,因此可能会对音符,暂停或和弦起作用:这意味着我需要检查每个元素的类型,并为其编写不同的代码每种类型都有“很多”(实际上,最多3个)if语句。在我看来这很少面向对象。
如何以一种更加面向对象且更少依赖于假设和类型检查的方式来安排类和接口?
答案 0 :(得分:3)
我需要找到一种方法来表达两者 没有申请的约束 继承IGuidoFormattable和 最好不要定义克隆 IGuidoFormattable中的方法 接口
第三种选择呢?
public interface ICloneableAndGuidoFormattable : IGuidoFormattable, ICloneable { }
然后你的FretPiece是ICloneableAndGuidoFormattable列表
如果不是这样,你可以试试这样的结构:
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()))
};
}
}
以及其他地方,例如在你的mutator上:
public T Apply<T>(T fretPiece) where T : IEnumerable<IFormattable>, ICloneable<T> ( ...)
这将确保您只能添加实现两个接口的项目。枚举仅假定返回IFormattables。这将允许你在演员表内安全地转换为ICloneable,因为它必须已经通过“添加”类型约束。你可以看到克隆的实现。即使你有一个演员也是安全的,除非有人根据反思摆弄items
;)
答案 1 :(得分:2)
我会让其中几个不可变。这样你就不需要再克隆它们了。无论出现在何处,音符始终都是相同的音符。所以让它变得不可变是很自然的。
例如Note,Pause,Chord似乎可以作为不可变类型使用。因为我对音乐理论知之甚少,所以你的大多数其他班级名字都是胡言乱语,所以我不知道它们中的哪一个也是不可改变的。