Kcko
3/21/2020 - 2:15 PM

17. Bridge

BAD: GOOD:

<?php

interface Formatter
{
    public function format(string $text): string;
}


class PlainTextFormatter implements Formatter
{
    public function format(string $text): string
    {
        return $text;
    }
}


class HtmlFormatter implements Formatter
{
    public function format(string $text): string
    {
        return sprintf('<p>%s</p>', $text);
    }
}

abstract class Service
{
    protected Formatter $implementation;

    public function __construct(Formatter $printer)
    {
        $this->implementation = $printer;
    }

    public function setImplementation(Formatter $printer)
    {
        $this->implementation = $printer;
    }

    abstract public function get(): string;
}



class HelloWorldService extends Service
{
    public function get(): string
    {
        return $this->implementation->format('Hello World');
    }
}


class PingService extends Service
{
    public function get(): string
    {
        return $this->implementation->format('pong');
    }
}

// USAGE:
$service = new HelloWorldService(new PlainTextFormatter());
$service = new HelloWorldService(new HtmlFormatter());
<?php

/**
 * 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->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": "' . $title . '", "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: Execution result
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": "Add to cart", "title": "Add to cart""}
}

*/
<?php

interface DeviceInterface { 
    public function setSender(MessagingInterface $sender);
 
    public function send($body);
}
 
abstract class Device implements DeviceInterface {
    protected $sender;
 
    public function setSender(MessagingInterface $sender) {
        $this->sender = $sender;
    }
}
 
class Phone extends Device {
    public function send($body) {
        $body .= "\n\n Sent from a phone.";
 
        return $this->sender->send($body);
    }
}
 
class Tablet extends Device {
    public function send($body) {
        $body .= "\n\n Sent from a Tablet.";
 
        return $this->sender->send($body);
    }
}
 
interface MessagingInterface { 
    public function send($body);
}
 
class Skype implements MessagingInterface {
    public function send($body) {
        // Send a message through the Skype API
    }
}
 
class Whatsapp implements MessagingInterface {
    public function send($body) {
        // Send a message through the Whatsapp API
    }
}
 
$phone = new Phone;
$phone->setSender(new Skype);
 
$phone->send("Hey Buddy");
<?php

abstract class Content {

    protected $filePath;

    public function __construct (string $filePath) {
        $this->filePath = $filePath;
    }

    abstract public function getHtml (): string;

    abstract public function getCss (): string;

}


class Image extends Content {

    public function getCss (): string {
        return <<<IMG
		.content {
			width: 100%;
			border-style: dotted dashed solid double; 
			border-radius: 50px;
		}
IMG;
    }

    public function getHtml (): string {
        return "<img class='content' src='$this->filePath' />";
    }
}


class Video extends Content {

    public function getCss (): string {
        return <<<VID
		.content {
			width: 100%; 
		}
		video::-webkit-media-controls-start-playback-button {
            display: none;
		}
VID;
    }

    public function getHtml (): string {
        return "<video class='content' src='$this->filePath' autoplay muted loop></video>";
    }
}


abstract class Display {

    protected $content;

    public function __construct (Content $content) {
        $this->content = $content;
    }

    abstract public function render (): string;

}


class Standard extends Display {

    public function render (): string {

        $html = $this->content->getHtml();
        $css = $this->content->getCss();

        return <<<CONTENT
			$html;
			
			<style>
				$css	
			</style>
CONTENT;
    }
}


class Blurred extends Display {

    private $blurredRules = <<<CSS
		.content {
			filter: blur(5px);
		  	-webkit-filter: blur(5px);
		}
CSS;


public function render (): string 
{
        $html = $this->content->getHtml();
        $css = $this->content->getCss();

        return <<<CONTENT
			$html;
			
			<style>
				$css	
				$this->blurredRules
			</style>
CONTENT;
    }
}


$vid = new Video("demo.mp4");
$img = new Image("demo.png");

$displayVid = new Standard($vid);
$blurredDisplayVid = new Blurred($vid);

$displayImg = new Standard($img);
$blurredDisplayImg = new Blurred($img);

echo($displayVid->render() . PHP_EOL);
echo($blurredDisplayVid->render() . PHP_EOL);

echo($displayImg->render() . PHP_EOL);
echo($blurredDisplayImg->render() . PHP_EOL);
// USAGE