抽象工厂

设计模式 / 创建型模式
image

//C has no interfaces: a struct of pointers to functions plays one

//abstract product A
typedef struct ProductA {
    void (*testA)(const struct ProductA *self);
} ProductA;

//abstract product B
typedef struct ProductB {
    void (*testB)(const struct ProductB *self);
} ProductB;

//concrete product A1
void testA1(const ProductA *) {
    printf("test A1\n");
}

//concrete product A2
void testA2(const ProductA *) {
    printf("test A2\n");
}

//concrete product B1
void testB1(const ProductB *) {
    printf("test B1\n");
}

//concrete product B2
void testB2(const ProductB *) {
    printf("test B2\n");
}

ProductA *productANew(void (*testA)(const ProductA *)) {
    ProductA *product = malloc(sizeof *product);
    product->testA = testA;
    return product;
}

ProductB *productBNew(void (*testB)(const ProductB *)) {
    ProductB *product = malloc(sizeof *product);
    product->testB = testB;
    return product;
}

//abstract factory
typedef struct {
    ProductA *(*createA)(void);
    ProductB *(*createB)(void);
} Factory;

//concrete factory 1
ProductA *createA1(void) {
    return productANew(testA1);
}

ProductB *createB1(void) {
    return productBNew(testB1);
}

//concrete factory 2
ProductA *createA2(void) {
    return productANew(testA2);
}

ProductB *createB2(void) {
    return productBNew(testB2);
}

//client code
void testFactory(const Factory *factory) {
    ProductA *productA = factory->createA();
    ProductB *productB = factory->createB();
    productA->testA(productA);
    productB->testB(productB);
    free(productA);
    free(productB);
}

Factory factory1 = {createA1, createB1};
Factory factory2 = {createA2, createB2};

testFactory(&factory1);
//printed: test A1
//         test B1
testFactory(&factory2);
//printed: test A2
//         test B2


Provides an interface for creating families of objects whose interfaces are known but concrete classes are not.
Read more

问题。程序用浅色和深色两种风格绘制窗口。每种风格都需要自己的按钮和菜单。如果代码直接创建它们,就可能误把浅色按钮和深色菜单混在一起。增加第三种风格时,还得找出所有创建按钮或菜单的地方。

思路。抽象工厂是一个接口,为每一种产品提供一个创建方法。每个具体工厂为一个产品族实现这个接口:一个工厂创建浅色按钮和浅色菜单,另一个创建深色的。客户端拿到一个工厂,所有产品都通过它来创建。客户端从不指明具体产品,因此得到的产品总是属于同一个产品族。要更换整个产品族,只需给客户端换一个工厂。

如何阅读示例。示例中有 A、B 两种产品。一个具体工厂生产一个产品族:每种产品各一个,标有相同的编号。客户端拿到一个工厂,向它各要一种产品,并使用这两个产品。客户端不知道自己拿到的是哪个产品族,但输出会显示出来。有些语言根本不声明接口,但角色不变。

何时使用。当产品必须配合使用、而且有多个产品族时,例如界面风格或操作系统,使用抽象工厂模式。只有一个产品族时不要使用它。产品之间互不依赖时也不需要它:构造函数或工厂方法就够了。

常见错误。在产品种类还经常变化时就选用这个模式。每增加一种产品,都要在抽象工厂和每个具体工厂中增加一个方法。