
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.
실제 사례 예시
이 예시에서 Page
(페이지) 계층 구조는 추상화 역할을 하고 Renderer
(렌더러) 계층 구조는 구현 역할을 합니다. 페이지
클래스의 객체들은 해당 페이지에 부착된 렌더러
객체에서 제공하는 기본 요소들을 사용하여 특정 종류의 웹 페이지를 조합할 수 있습니다. 두 클래스 계층 구조들이 서로 분리되어 있으므로 페이지
클래스들을 전혀 변경하지 않고 새 렌더러
클래스를 추가할 수 있으며 그 반대의 경우도 가능합니다.
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"} }