自注册组件/插件的设计模式


Design pattern for self-registering components/plugins

我有一个单例类World。(它不需要是单例的,但它在我的项目中是这样定义的。)

然后我有Component接口,然后由ConcreteComponent1, ConcreteComponent2等实现。这些都实现了一些很好的方法,比如decorateTheWorld

在某个时刻,World实例将遍历它的所有子Compontent,并要求它们通过调用decorateTheWorld来修饰自己。

这种方法的问题是World,或者World之外的东西需要知道World可以拥有的任何类型的Component,因为Component实例需要在某个时候以某种方式创建。

关键是我不想做一些愚蠢的事情,比如100行重复的代码,比如
(new ConcreteComponent1())->registerInTheWorld()
(new ConcreteComponent2())->registerInTheWorld()
(new ConcreteComponent3())->registerInTheWorld()

…和我想求助于反射。

那么,是否有任何设计模式可以使注册部分自动开箱?还是说我的要求是不可能的?

如果组件实现了一个公共接口,你的世界就不需要知道具体的组件,只需要知道接口。

请看这个例子(c#):

public interface IComponent
{
    void decorateTheWorld();
}
public class ComponentA : IComponent
{
    public void decorateTheWorld() { /* ... */ }
}
public class ComponentB : IComponent { /* ... */ }

在你的World class中,假设_componentsIComponent s的集合:

foreach(IComponent comp in _components)
    comp.decorateTheWorld();

现在,如果您不想手动"查找"组件,您可以从已加载的程序集中获取所有类型,并找到实现IComponent的组件,然后使用Activator.CreateInstance实例化它们。

由于World是单例的,我建议World将持有一组组件,并且在其构造函数中的任何组件都将在单例中注册。所以单例会在不知道子类型的情况下进行迭代

当遇到类似的问题时,我采用了这种方法,如下所示:

创建一个包含所有组件的数组(不是作为具体的实现)。

Array<Components> componentsAttributeInWorld

然后使用您自己创建的创建方法来一次创建一个或多个组件。使用for迭代方法一次创建多个

void createObjectType:(String)nameOfConcreteObjectClass AndCount:(int)numberOfObjects {
    for (int i=0; i<numberOfObjects; i++) {
        Component *comp = [[Class getClassWithName:nameOfConcreteObjectClass] new];
        [componentsAttributeInWorld addObject:comp];
    }
}

之后,对象在数组/列表中,您可以再次使用foreach对它们进行迭代。在Objective-C中,你可以动态测试组件真正是哪个类,然后为那个具体实现做一些特殊的方法。在Java中,你甚至可以为字符串使用switch。

…
for (Component *c in componentsAttributeInWorld) {
    if ([c isClass:@"ConcreteComponent1"]) {
        c.color = ccRED;
    else if ([c isClass:@"ConcreteComponent2"]) {
        c.size = ccsize(1,2,1);
    }
}
…

所以你只需要处理数组,然后排序是不相关的(我用它来知道哪个对象是最老的),然后你可以使用一个集合,这可能会更快!