使用继承来封装组合

Using inheritance for encapsulating composition

这是good/acceptable从对象派生的做法只是为了隐藏所有与 创建该对象。

例如(虽然不是完美的例子)我有一个场景class。我想用一些实体、一些系统、设置背景颜色等来初始化它。 我可以为此创建工厂,但我选择在派生 class.

的构造函数中完成所有这些工作

而不是这个:

var scene = new Scene();
scene.Systems (new ColisionSystem);
scene.Systems (new MovementSystem);
scene.SceneGraph = new MyCustomSceneGraph();

我这样做:

class MyScene : Scene
{
    void MyScene(SceneGraph sceneGraph)
    {
        this.Systems (new ColisionSystem);
        this.Systems (new MovementSystem);

        this.SceneGraph = sceneGraph;
    }
}

var scene = new MyScene(new MyCustomSceneGraph());

所以我将所有创建的东西隐藏在构造函数中,作为奖励,我为我将创建的每个场景都获得了单独的文件。

在另一条路上。如果我曾经创建一个场景编辑器,这可能是个问题。因为在那种情况下,场景对象的客户端应该负责准备场景,而不是场景本身。

那么这是可以接受的做法还是违反了 SRP?

您已经确定了 subclass-per-composition 方法的确切问题:它必然会占用编译时间,使 运行-time 组合变得更加困难。

此外,将组合逻辑硬编码到 subclass 构造函数中需要您在每次需要更改逻辑时重新编译,即使唯一更改的是 运行 -系统内的时间实例图。

您可以通过添加描述连接而不创建连接的 class 来解决在 运行 时组合对象的要求,然后添加接受描述并处理它们的构造函数变成一个完整的 Scene 对象。

为了让这个不那么抽象,考虑一个您最喜欢的人类可读格式的配置文件,例如

{"Systems":["ColisionSystem", "MovementSystem"], "SceneGraph":"MyCustomSceneGraph"}

以及允许您访问此数据的界面:

interface SceneConfiguration {
    IList<String> Systems {get;}
    String SceneGraph {get;}
    ... // More configuration items
}

现在考虑一个从该表示中生成 SceneConfiguration 实例的解析器,以及一个将 SceneConfiguration 作为其参数的 Scene 的构造函数:

public Scene(SceneConfiguration config)

这种安排可以让你"soft-code"通过一个配置文件来组合。该方法也适用于场景编辑器:您的场景编辑器创建 SceneConfiguration 的实例,而 Scene 仍然控制创建自身。