首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
设计模式概述
工厂方法
抽象工厂
生成器
原型
单例
适配器
桥接
组合
装饰
外观
享元
代理
责任链
命令
迭代器
中介者
备忘录
观察者
状态
策略
模板方法
访问者
当前位置:
首页>>
技术小册>>
经典设计模式PHP版
小册名称:经典设计模式PHP版
**抽象工厂模式** 是一种创建型设计模式,它能创建一系列相关的对象,而无需指定其具体类。 ***** 假设你正在开发一款家具商店模拟器。你的代码中包括一些 类,用于表示: 1. 一系列相关产品, 例如 椅子 Chair 、 沙发 Sofa 和 咖啡桌 CoffeeTable 。 2. 系列产品的不同变体。 例如, 你可以使用 现代 Modern 、 维多利亚 Victorian 、 装饰风艺术 ArtDeco 等风格生成 椅子 、 沙发 和 咖啡桌 。 你需要设法单独生成每件家具对象,这样才能确保其风格一 致。如果顾客收到的家具风格不一样,他们可不会开心。 此外, 你也不希望在添加新产品或新风格时修改已有代码。 家具供应商对于产品目录的更新非常频繁,你不会想在每次 更新时都去修改核心代码的。 ***** **解决方案** 首先,抽象工厂模式建议为系列中的每件产品明确声明接口 (例如椅子、沙发或咖啡桌)。然后,确保所有产品变体都继 承这些接口。例如,所有风格的椅子都实现 椅子 接口;所 有风格的咖啡桌都实现 咖啡桌 接口,以此类推。 接下来,我们需要声明抽象工厂——包含系列中所有产品构 造方法的接口。例如 createChair 创建椅子 、 createSofa 创建沙发 和 createCoffeeTable 创建咖啡桌 。 这些方法必 须返回抽象产品类型,即我们之前抽取的那些接口: 椅子 , 沙发 和 咖啡桌 等等。 那 么 该 如 何 处 理 产 品 变 体 呢? 对 于 系 列 产 品 的 每 个 变 体, 我 们 都 将 基 于 抽象工厂 接 口 创 建 不 同 的 工 厂 类。 每 个 工 厂 类 都 只 能 返 回 特 定 类 别 的 产 品, 例 如, 现代家具工厂 ModernFurnitureFactory 只 能 创 建 现代椅子 ModernChair 、 现代沙发 ModernSofa 和 现代咖啡桌 ModernCoffeeTable 对象。 客户端代码可以通过相应的抽象接口调用工厂和产品类。你 无需修改实际客户端代码,就能更改传递给客户端的工厂类, 也能更改客户端代码接收的产品变体。 假设客户端想要工厂创建一把椅子。客户端无需了解工厂类, 也不用管工厂类创建出的椅子类型。无论是现代风格,还是 维多利亚风格的椅子,对于客户端来说没有分别,它只需调 用抽象 椅子 接口就可以了。这样一来,客户端只需知道椅 子以某种方式实现了 sitOn 坐下 方法就足够了。此外,无 论工厂返回的是何种椅子变体,它都会和由同一工厂对象创 建的沙发或咖啡桌风格一致。 最后一点说明:如果客户端仅接触抽象接口,那么谁来创建 实际的工厂对象呢?一般情况下,应用程序会在初始化阶段 创建具体工厂对象。而在此之前,应用程序必须根据配置文 件或环境设定选择工厂类别。 ***** 1. 抽象产品(Abstract Product)为构成系列产品的一组不同但 相关的产品声明接口。 2. 具体产品(Concrete Product)是抽象产品的多种不同类型实 现。所有变体(维多利亚/现代)都必须实现相应的抽象产品 (椅子/沙发)。 3. 抽象工厂(Abstract Factory)接口声明了一组创建各种抽象 产品的方法。 4. 具体工厂(Concrete Factory)实现抽象工厂的构建方法。每 个具体工厂都对应特定产品变体,且仅创建此种产品变体。 5. 尽管具体工厂会对具体产品进行初始化,其构建方法签名必 须返回相应的抽象产品。这样,使用工厂类的客户端代码就 不会与工厂创建的特定产品变体耦合。客户端(Client)只 需通过抽象接口调用工厂和产品对象,就能与任何具体工厂/ 产品变体交互。 ***** ### **代码示例** ``` <?php /** * Abstract Factory Design Pattern * * Intent: Lets you produce families of related objects without specifying their * concrete classes. * * Example: In this example, the Abstract Factory pattern provides an * infrastructure for creating various types of templates for different elements * of a web page. * * A web application can support different rendering engines at the same time, * but only if its classes are independent of the concrete classes of rendering * engines. Hence, the application's objects must communicate with template * objects only via their abstract interfaces. Your code should not create the * template objects directly, but delegate their creation to special factory * objects. Finally, your code should not depend on the factory objects either * but, instead, should work with them via the abstract factory interface. * * As a result, you will be able to provide the app with the factory object that * corresponds to one of the rendering engines. All templates, created in the * app, will be created by that factory and their type will match the type of * the factory. If you decide to change the rendering engine, you'll be able to * pass a new factory to the client code, without breaking any existing code. */ /** * The Abstract Factory interface declares creation methods for each distinct * product type. */ interface TemplateFactory { public function createTitleTemplate(): TitleTemplate; public function createPageTemplate(): PageTemplate; public function getRenderer(): TemplateRenderer; } /** * Each Concrete Factory corresponds to a specific variant (or family) of * products. * * This Concrete Factory creates Twig templates. */ class TwigTemplateFactory implements TemplateFactory { public function createTitleTemplate(): TitleTemplate { return new TwigTitleTemplate(); } public function createPageTemplate(): PageTemplate { return new TwigPageTemplate($this->createTitleTemplate()); } public function getRenderer(): TemplateRenderer { return new TwigRenderer(); } } /** * And this Concrete Factory creates PHPTemplate templates. */ class PHPTemplateFactory implements TemplateFactory { public function createTitleTemplate(): TitleTemplate { return new PHPTemplateTitleTemplate(); } public function createPageTemplate(): PageTemplate { return new PHPTemplatePageTemplate($this->createTitleTemplate()); } public function getRenderer(): TemplateRenderer { return new PHPTemplateRenderer(); } } /** * Each distinct product type should have a separate interface. All variants of * the product must follow the same interface. * * For instance, this Abstract Product interface describes the behavior of page * title templates. */ interface TitleTemplate { public function getTemplateString(): string; } /** * This Concrete Product provides Twig page title templates. */ class TwigTitleTemplate implements TitleTemplate { public function getTemplateString(): string { return "<h1>{{ title }}</h1>"; } } /** * And this Concrete Product provides PHPTemplate page title templates. */ class PHPTemplateTitleTemplate implements TitleTemplate { public function getTemplateString(): string { return "<h1><?= \$title; ?></h1>"; } } /** * This is another Abstract Product type, which describes whole page templates. */ interface PageTemplate { public function getTemplateString(): string; } /** * The page template uses the title sub-template, so we have to provide the way * to set it in the sub-template object. The abstract factory will link the page * template with a title template of the same variant. */ abstract class BasePageTemplate implements PageTemplate { protected $titleTemplate; public function __construct(TitleTemplate $titleTemplate) { $this->titleTemplate = $titleTemplate; } } /** * The Twig variant of the whole page templates. */ class TwigPageTemplate extends BasePageTemplate { public function getTemplateString(): string { $renderedTitle = $this->titleTemplate->getTemplateString(); return <<<HTML <div class="page"> $renderedTitle <article class="content">{{ content }}</article> </div> HTML; } } /** * The PHPTemplate variant of the whole page templates. */ class PHPTemplatePageTemplate extends BasePageTemplate { public function getTemplateString(): string { $renderedTitle = $this->titleTemplate->getTemplateString(); return <<<HTML <div class="page"> $renderedTitle <article class="content"><?= \$content; ?></article> </div> HTML; } } /** * The renderer is responsible for converting a template string into the actual * HTML code. Each renderer behaves differently and expects its own type of * template strings passed to it. Baking templates with the factory let you pass * proper types of templates to proper renders. */ interface TemplateRenderer { public function render(string $templateString, array $arguments = []): string; } /** * The renderer for Twig templates. */ class TwigRenderer implements TemplateRenderer { public function render(string $templateString, array $arguments = []): string { return \Twig::render($templateString, $arguments); } } /** * The renderer for PHPTemplate templates. Note that this implementation is very * basic, if not crude. Using the `eval` function has many security * implications, so use it with caution in real projects. */ class PHPTemplateRenderer implements TemplateRenderer { public function render(string $templateString, array $arguments = []): string { extract($arguments); ob_start(); eval(' ?>' . $templateString . '<?php '); $result = ob_get_contents(); ob_end_clean(); return $result; } } /** * The client code. Note that it accepts the Abstract Factory class as the * parameter, which allows the client to work with any concrete factory type. */ class Page { public $title; public $content; public function __construct($title, $content) { $this->title = $title; $this->content = $content; } // Here's how would you use the template further in real life. Note that the // page class does not depend on any concrete template classes. public function render(TemplateFactory $factory): string { $pageTemplate = $factory->createPageTemplate(); $renderer = $factory->getRenderer(); return $renderer->render($pageTemplate->getTemplateString(), [ 'title' => $this->title, 'content' => $this->content ]); } } /** * Now, in other parts of the app, the client code can accept factory objects of * any type. */ $page = new Page('Sample page', 'This it the body.'); echo "Testing actual rendering with the PHPTemplate factory:\n"; echo $page->render(new PHPTemplateFactory()); // Uncomment the following if you have Twig installed. // echo "Testing rendering with the Twig factory:\n"; echo $page->render(new // TwigTemplateFactory()); ```
上一篇:
工厂方法
下一篇:
生成器
该分类下的相关小册推荐:
全面构建Magento2电商系统
Magento2主题开发高级实战
PHP8入门与项目实战(6)
Laravel(10.x)从入门到精通(八)
Magento2后端开发高级实战
PHP程序员面试算法宝典
Laravel(10.x)从入门到精通(十八)
Laravel(10.x)从入门到精通(七)
Laravel(10.x)从入门到精通(十六)
Laravel(10.x)从入门到精通(十五)
Magento中文全栈二次开发
Magento零基础到架构师(目录管理)