如何为基于策略模式的应用程序编写清晰、优雅的代码?

发布于 2024-10-16 13:29:00 字数 1594 浏览 1 评论 0原文

我正在编写一个应用程序来创作音乐作品,但我有点担心代码中类和接口的结构。这里有我写的一些类的签名:

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 代表一个音乐短语,完整的 freize 的一个片段。它公开了 Key、Tempo 和 Meter 属性,这些属性与其类型同音。更多的短语组合在一起创建一个 freize,以 Fret 类为代表。单个短语中的每个元素都必须根据 GUIDO 标准表示法进行格式化,因此它必须实现 IGuidoFormattable 接口。 在另一个命名空间中,定义了一些突变类,它们都继承自两个抽象类之一:

Class FretMutation
Class LambdaFretMutation : FretMutation

最后,存在一个名为 FretMutationGenerator 的类,它的唯一任务是将选定的突变应用到音乐主题并将整个 freize 作为实例输出烦恼类。

FretPiece 必须能够包含多个不同的元素(在本例中为音符、停顿和和弦),但它们必须满足两个约束:它们必须可以使用 GUIDO 表示法进行格式化,从而转换为有意义的字符串;它们必须是可克隆的。在现在的代码中,每个类都实现了 ICloneable,但当前代码的语法和语义并不保证集合的所有成员都是可克隆的。我需要找到一种方法来表达这两个约束,而不将继承应用于 IGuidoFormattable,并且最好不在 IGuidoFormattable 接口中定义 Clone 方法。

第二,也是最重要的一个问题。 FretMutation 定义了一个抽象方法“Apply”,必须在每个派生类中重写该方法。因此,任何突变类都定义了该方法自己的版本,该方法具有以下签名:

FretPiece Apply(FretPiece originalTheme)

它接受 FretPiece 作为输入,并输出该对象的副本,并根据指定为该类成员的所有其他参数进行突变。我认为这是战略模式的一种实现。然而,仅因为此方法创建输入的副本,这意味着参数本身(及其所有成员)必须是可克隆的。 此外,FretPiece 被声明为 IGuidoFormattable 列表,但每个突变类的行为都与其他类不同,并且可能相应地作用于音符、停顿或和弦:这意味着我需要检查每个元素的类型,并为它们编写不同的代码每种类型都有“很多”(实际上,最多 3 个)if 语句。在我看来,这似乎很少是面向对象的。

如何以一种更加面向对象并且更少依赖于假设和类型检查的方式来安排类和接口?

I am writing an application to compose musical freizes, but I am a little worried about the structure of classes and interfaces within the code. Here there are the signatures of some classes I wrote:

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 represents a muscial phrase, a piece of the complete freize. It exposes as properties Key, Tempo and Meter, which are homonym with their types. More phrases put together create a freize, respresented by the Fret class. Every element within a single phrase must be formattable in accordance with the GUIDO standard notation, hence it has to implement the IGuidoFormattable interface.
In another namespace, some mutation classes are defined and they all inherit from one of the two abstract classes:

Class FretMutation
Class LambdaFretMutation : FretMutation

Finally, there exists a class called FretMutationGenerator which has the only task of applying selected mutations to a music theme and output the entire freize as an instance of Fret class.

FretPiece must be able to contain several different elements (notes, pauses and chords in this case), which nonetheless must satisfy two constraints: they have to be formattable with GUIDO notation and therefore transformed into meaningful strings; they have to be cloneable. In the code as it is now, every class implements ICloneable, but syntax and semantics of current code do not grant that all members of the collection are cloneable. I need to find a way to express both constraint without applying inheritance to IGuidoFormattable and preferably without defining a Clone method in the IGuidoFormattable interface.

Second, and most important, problem. FretMutation defines an abstract method, "Apply", that has to be overridden in every derived class. Therefore, any mutation class defines its own version of this method, which has the following signature:

FretPiece Apply(FretPiece originalTheme)

It accepts as input a FretPiece and outputs a copy of that object, mutated according to all other parameters specified as members of the class. I think that this is an implementation of the strategy pattern. However, only becase of the fact that this method creats a copy of the input, it means that the argument itself (and therefore all its members) must be cloneable.
In addition, FretPiece is declared as a list of IGuidoFormattable, but every mutation class behaves differently from the others and may act on notes, pauses or chord, accordingly: this means that I need to check every element's type, and write a different code for each type with "a lot" (indeed, 3 at most) if statements. And this seems to me very little object oriented.

How can I arrange classes and interface in a way that all become more object oriented and less dependent on assumptions and type-checking?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

想你的星星会说话 2024-10-23 13:29:00

我需要找到一种方式来表达两者
不应用约束
继承到 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()))
        };
    }
}

在其他地方,例如在你的变异器上:

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

这将确保你只能添加实现两个接口的项目。该枚举仅假设返回 IFormattables。这将允许您在转换内部安全地转换为 ICloneable,因为它必须已通过“Add”的类型约束。可以看到clone的实现。即使你在那里有一个演员,它也是安全的,除非有人基于反射来摆弄 items ;)

I need to find a way to express both
constraint without applying
inheritance to IGuidoFormattable and
preferably without defining a Clone
method in the IGuidoFormattable
interface

What about the third option?

public interface ICloneableAndGuidoFormattable : IGuidoFormattable, ICloneable { }

Then your FretPiece is List Of ICloneableAndGuidoFormattable

If not that, you could try such a construct:

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()))
        };
    }
}

And somewhere else e.g. on your mutator:

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

This would ensure that you can only add items implementing both interfaces. the enumeration only assumes that IFormattables are returned. This would allow you inside the cast to safely cast to ICloneable since it must have passed the type constraint on "Add". You can see the implementation of clone. Even though you have a cast there it is safe unless somebody fiddles with items based on reflection ;)

花开雨落又逢春i 2024-10-23 13:29:00

我会让其中几个不可变。这样您就不再需要克隆它们了。注释始终是相同的注释,无论它出现在何处。所以让它不可变感觉很自然。

例如,Note、Pause、Chord 似乎作为不可变类型可以很好地工作。你的大多数其他类名对我来说都是胡言乱语,因为我对音乐理论了解不够,所以我不知道它们中的哪些也是不可变的。

I would make several of them immutable. That way you don't need to clone them anymore. A note is always the same note, regardless of where it appears. So it feels natural to make it immutable.

For example Note,Pause,Chord seem like they would work well as immutable types. Most of your other class names are gibberish to me since I don't know enough about musical theory, so I don't know which of them could be immutable too.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文