抽象工厂(Abstract Factory)
抽象工厂模式是创建型设计模式的一种,抽象工厂模式和工厂方法模式的区别就在于需要创建对象的复杂程度上。而且抽象工厂模式是三个里面最为抽象、最具一般性的。抽象工厂模式的用意为:给客户端提供一个接口,可以创建多个产品族中的产品对象,而使用抽象工厂还需满足一些条件;
1. 系统中有多个产品族,而系统一次只可能消费其中一族产品。
2. 同属于同一个产品族的产品以其使用。
什么是产品族:
假如现在我们要开发一个家具类的商城。这个商城有以下一系列产品例如 椅子Chair 、沙发Sofa和 咖啡桌CoffeeTable。也有不同的风格,例如, 你可以使用 现代Modern 、 维多利亚Victorian 、 装饰风艺术ArtDeco。
图中的 Chair、Sofa和CoffeeTable 就是三个产品树(产品层次结构);而如图所示的 ModernChair 、 ModernSofa和 ModernCoffeeTable就是一个产品族。他们都可以放到现代风格家族中,因此功能有所关联。同理 VictorianChair 、 VictorianSofa和 VictorianCoffeeTable 也是一个产品族。
抽象工厂模式的结构
1. 抽象产品 (Abstract Product) 为构成系列产品的一组不同但相关的产品声明接口。
2. 具体产品 (Concrete Product) 是抽象产品的多种不同类型实现。 所有变体 (维多利亚/现代) 都必须实现相应的抽象产品 (椅子/沙发)。
3. 抽象工厂 (Abstract Factory) 接口声明了一组创建各种抽象产品的方法。
4. 具体工厂 (Concrete Factory) 实现抽象工厂的构建方法。 每个具体工厂都对应特定产品变体, 且仅创建此种产品变体。
5. 尽管具体工厂会对具体产品进行初始化, 其构建方法签名必须返回相应的抽象产品。 这样, 使用工厂类的客户端代码就不会与工厂创建的特定产品变体耦合。 客户端 (Client) 只需通过抽象接口调用工厂和产品对象, 就能与任何具体工厂/产品变体交互。
示例:
抽象产品: 椅子 Chair.php
/** * 椅子产品接口 * 抽象产品角色 * Interface Chair */ interface Chair{ /** * 生产 * @return mixed */ public function produce(); }
沙发 Sofa.php
/** * 沙发产品接口 * 抽象产品角色 * Interface Sofa */ interface Sofa{ /** * 生产 */ public function produce(); }
咖啡桌 CoffeeTable.php
/** * 咖啡桌产品接口 * 抽象产品角色 * Interface CoffeeTable */ interface CoffeeTable{ /** * 生产 */ public function produce(); }
具体产品 每个产品都有不同的风格,例如椅子有现代风格、维多利亚风格以及装饰风艺术风格。我们要创建与之对应的具体产品:
现代风格的椅子 MordernChair.php
/** * 现代风格的椅子 * 具体产品角色 * Class ModernChair */ class ModernChair implements Chair{ public function produce() { echo '生产现代风格的椅子'; } }
维多利亚风格的椅子 VictorianChair.php
/** * 维多利亚风格的椅子 * 具体产品角色 * Class VictorianChair */ class VictorianChair implements Chair{ public function produce() { echo '生产维多利亚风格的椅子'; } }
装饰风艺术风格的椅子 ArtDecoChair.php
/** * 装饰风艺术风格的椅子 * Class ArtDecoChair */ class ArtDecoChair implements Chair{ public function produce() { echo '生产装饰风艺术风格的椅子'; } }
沙发,咖啡桌的具体产品代码略
接下来我们需要创建工厂
抽象工厂 Factory.php
/** * 抽象工厂角色 * Interface Factory */ interface Factory{ /** * 创建椅子产品 * @return mixed */ public function createChair(); }
具体工厂
现代风格工厂MordernFactory.php
/** * 现代风格工厂 * 具体工厂角色 * Class ModernFactory */ class ModernFactory implements Factory{ public function createChair() { return new ModernChair(); } public function createSofa() { return new ModernSofa(); } public function createCoffeeTable() { return new ModernCoffeeTable(); } }
维多利亚风格工厂 VictorianFactory.php
/** * 维多利亚风格工厂 * 具体工厂角色 * Class VictorianFactory */ class VictorianFactory implements Factory{ public function createChair() { return new VictorianChair(); } public function createSofa() { return new VictorianSofa(); } public function createCoffeeTable() { return new VictorianCoffeeTable(); } }
装饰风艺术风格的工厂 ArtDecoFactory.php
/** * 装饰风艺术风格的工厂 * 具体工厂角色 * Class ArtDecoFactory */ class ArtDecoFactory implements Factory{ public function createChair() { return new ArtDecoChair(); } public function createSofa() { return new ArtDecoSofa(); } public function createCoffeeTable() { return new ArtDecoCoffeeTable(); } }
运行 client.php
//client class Client{ public function run(){ //创建现代风格的产品 $factory = new ModernFactory(); $chair = $factory->createChair(); $chair->produce(); echo '<br>'; $sofa = $factory->createSofa(); $sofa->produce(); echo '<br>'; $coffeetable = $factory->createCoffeeTable(); $coffeetable->produce(); } } $client = new Client(); $client->run();
总结抽象工厂模式优缺点:
优点:你可以确保同一工厂生成的产品相互匹配。
你可以避免客户端和具体产品代码的耦合。(产品跟客户端完全分离,我们只需要调用工厂,产品的类名我们不需要知道)
单一职责原则。 你可以将产品生成代码抽取到同一位置, 使得代码易于维护。
开闭原则。 向应用程序中引入新产品变体时, 你无需修改客户端代码。
缺点: 由于采用该模式需要向应用中引入众多接口和类, 代码可能会比之前更加复杂。
例如:我们如果新增了床Bed那么我们就需要新增Bed接口,ModernBed,VictorianBed,ArtDecoBed等具体产品角色,还需要在Factory抽象工厂,ModernFactory,VictorianFactory,ArtDecoFactory等具体工厂中增加createBed方法。使我们的系统愈加复杂。