
PHP 桥接模式讲解和代码示例
桥接是一种结构型设计模式, 可将业务逻辑或一个大类拆分为不同的层次结构, 从而能独立地进行开发。
层次结构中的第一层 (通常称为抽象部分) 将包含对第二层 (实现部分) 对象的引用。 抽象部分将能将一些 (有时是绝大部分) 对自己的调用委派给实现部分的对象。 所有的实现部分都有一个通用接口, 因此它们能在抽象部分内部相互替换。
复杂度:
流行度:
使用示例: 桥接模式在支持多种类型的数据库服务器或与多个特定种类 (例如云平台和社交网络等) 的 API 供应商协作时会特别有用。
识别方法: 桥接可以通过一些控制实体及其所依赖的多个不同平台之间的明确区别来进行识别。
概念示例
本例说明了桥接设计模式的结构并重点回答了下面的问题:
- 它由哪些类组成?
- 这些类扮演了哪些角色?
- 模式中的各个元素会以何种方式相互关联?
了解该模式的结构后, 你可以更容易地理解下面基于真实世界的 PHP 应用案例。
index.php: 概念示例
<?php namespace RefactoringGuru\Bridge\Conceptual; /** * The Abstraction defines the interface for the "control" part of the two class * hierarchies. It maintains a reference to an object of the Implementation * hierarchy and delegates all of the real work to this object. */ class Abstraction { /** * @var Implementation */ protected $implementation; public function __construct(Implementation $implementation) { $this->implementation = $implementation; } public function operation(): string { return "Abstraction: Base operation with:\n" . $this->implementation->operationImplementation(); } } /** * You can extend the Abstraction without changing the Implementation classes. */ class ExtendedAbstraction extends Abstraction { public function operation(): string { return "ExtendedAbstraction: Extended operation with:\n" . $this->implementation->operationImplementation(); } } /** * The Implementation defines the interface for all implementation classes. It * doesn't have to match the Abstraction's interface. In fact, the two * interfaces can be entirely different. Typically the Implementation interface * provides only primitive operations, while the Abstraction defines higher- * level operations based on those primitives. */ interface Implementation { public function operationImplementation(): string; } /** * Each Concrete Implementation corresponds to a specific platform and * implements the Implementation interface using that platform's API. */ class ConcreteImplementationA implements Implementation { public function operationImplementation(): string { return "ConcreteImplementationA: Here's the result on the platform A.\n"; } } class ConcreteImplementationB implements Implementation { public function operationImplementation(): string { return "ConcreteImplementationB: Here's the result on the platform B.\n"; } } /** * Except for the initialization phase, where an Abstraction object gets linked * with a specific Implementation object, the client code should only depend on * the Abstraction class. This way the client code can support any abstraction- * implementation combination. */ function clientCode(Abstraction $abstraction) { // ... echo $abstraction->operation(); // ... } /** * The client code should be able to work with any pre-configured abstraction- * implementation combination. */ $implementation = new ConcreteImplementationA(); $abstraction = new Abstraction($implementation); clientCode($abstraction); echo "\n"; $implementation = new ConcreteImplementationB(); $abstraction = new ExtendedAbstraction($implementation); clientCode($abstraction);
Output.txt: 执行结果
Abstraction: Base operation with: ConcreteImplementationA: Here's the result on the platform A. ExtendedAbstraction: Extended operation with: ConcreteImplementationB: Here's the result on the platform B.
真实世界示例
在本例中, 页面层作为抽象部分, 而渲染层则作为实现部分。 页面类的对象可用附加在该页面的渲染对象提供的基础元素合成特定种类的网页。 由于两个层次相互独立, 你可以在不修改页面层的前提下添加一个新的渲染层类, 反之亦然。
index.php: 真实世界示例
<?php namespace RefactoringGuru\Bridge\RealWorld; /** * The Abstraction. */ abstract class Page { /** * @var Renderer */ protected $renderer; /** * The Abstraction is usually initialized with one of the Implementation * objects. */ public function __construct(Renderer $renderer) { $this->renderer = $renderer; } /** * The Bridge pattern allows replacing the attached Implementation object * dynamically. */ public function changeRenderer(Renderer $renderer): void { $this->renderer = $renderer; } /** * The "view" behavior stays abstract since it can only be provided by * Concrete Abstraction classes. */ abstract public function view(): string; } /** * This Concrete Abstraction represents a simple page. */ class SimplePage extends Page { protected $title; protected $content; public function __construct(Renderer $renderer, string $title, string $content) { parent::__construct($renderer); $this->title = $title; $this->content = $content; } public function view(): string { return $this->renderer->renderParts([ $this->renderer->renderHeader(), $this->renderer->renderTitle($this->title), $this->renderer->renderTextBlock($this->content), $this->renderer->renderFooter() ]); } } /** * This Concrete Abstraction represents a more complex page. */ class ProductPage extends Page { protected $product; public function __construct(Renderer $renderer, Product $product) { parent::__construct($renderer); $this->product = $product; } public function view(): string { return $this->renderer->renderParts([ $this->renderer->renderHeader(), $this->renderer->renderTitle($this->product->getTitle()), $this->renderer->renderTextBlock($this->product->getDescription()), $this->renderer->renderImage($this->product->getImage()), $this->renderer->renderTextBlock('$' . number_format($this->product->getPrice(), 2)), $this->renderer->renderLink("/cart/add/" . $this->product->getId(), "Add to cart"), $this->renderer->renderFooter() ]); } } /** * A helper class for the ProductPage class. */ class Product { private $id, $title, $description, $image, $price; public function __construct( string $id, string $title, string $description, string $image, float $price ) { $this->id = $id; $this->title = $title; $this->description = $description; $this->image = $image; $this->price = $price; } public function getId(): string { return $this->id; } public function getTitle(): string { return $this->title; } public function getDescription(): string { return $this->description; } public function getImage(): string { return $this->image; } public function getPrice(): float { return $this->price; } } /** * The Implementation declares a set of "real", "under-the-hood", "platform" * methods. * * In this case, the Implementation lists rendering methods that can be used to * compose any web page. Different Abstractions may use different methods of the * Implementation. */ interface Renderer { public function renderTitle(string $title): string; public function renderTextBlock(string $text): string; public function renderImage(string $url): string; public function renderLink(string $url, string $title): string; public function renderHeader(): string; public function renderFooter(): string; public function renderParts(array $parts): string; } /** * This Concrete Implementation renders a web page as HTML. */ class HTMLRenderer implements Renderer { public function renderTitle(string $title): string { return "<h1>$title</h1>"; } public function renderTextBlock(string $text): string { return "<div class='text'>$text</div>"; } public function renderImage(string $url): string { return "<img src='$url'>"; } public function renderLink(string $url, string $title): string { return "<a href='$url'>$title</a>"; } public function renderHeader(): string { return "<html><body>"; } public function renderFooter(): string { return "</body></html>"; } public function renderParts(array $parts): string { return implode("\n", $parts); } } /** * This Concrete Implementation renders a web page as JSON strings. */ class JsonRenderer implements Renderer { public function renderTitle(string $title): string { return '"title": "' . $title . '"'; } public function renderTextBlock(string $text): string { return '"text": "' . $text . '"'; } public function renderImage(string $url): string { return '"img": "' . $url . '"'; } public function renderLink(string $url, string $title): string { return '"link": {"href": "' . $url . '", "title": "' . $title . '"}'; } public function renderHeader(): string { return ''; } public function renderFooter(): string { return ''; } public function renderParts(array $parts): string { return "{\n" . implode(",\n", array_filter($parts)) . "\n}"; } } /** * The client code usually deals only with the Abstraction objects. */ function clientCode(Page $page) { // ... echo $page->view(); // ... } /** * The client code can be executed with any pre-configured combination of the * Abstraction+Implementation. */ $HTMLRenderer = new HTMLRenderer(); $JSONRenderer = new JsonRenderer(); $page = new SimplePage($HTMLRenderer, "Home", "Welcome to our website!"); echo "HTML view of a simple content page:\n"; clientCode($page); echo "\n\n"; /** * The Abstraction can change the linked Implementation at runtime if needed. */ $page->changeRenderer($JSONRenderer); echo "JSON view of a simple content page, rendered with the same client code:\n"; clientCode($page); echo "\n\n"; $product = new Product( "123", "Star Wars, episode1", "A long time ago in a galaxy far, far away...", "/images/star-wars.jpeg", 39.95 ); $page = new ProductPage($HTMLRenderer, $product); echo "HTML view of a product page, same client code:\n"; clientCode($page); echo "\n\n"; $page->changeRenderer($JSONRenderer); echo "JSON view of a simple content page, with the same client code:\n"; clientCode($page);
Output.txt: 执行结果
HTML view of a simple content page: <html><body> <h1>Home</h1> <div class='text'>Welcome to our website!</div> </body></html> JSON view of a simple content page, rendered with the same client code: { "title": "Home", "text": "Welcome to our website!" } HTML view of a product page, same client code: <html><body> <h1>Star Wars, episode1</h1> <div class='text'>A long time ago in a galaxy far, far away...</div> <img src='/images/star-wars.jpeg'> <a href='/cart/add/123'>Add to cart</a> </body></html> JSON view of a simple content page, with the same client code: { "title": "Star Wars, episode1", "text": "A long time ago in a galaxy far, far away...", "img": "/images/star-wars.jpeg", "link": {"href": "/cart/add/123", "title": "Add to cart"} }