Bu yazı Design Patterns/Tasarım Desenleri nedir? başlıklı yazı dizisinin bir parçasıdır.

Bu içerik ağırlıklı olarak refactoring.guru sitesindeki içeriğin tercümesi ve derlenmesinden oluşturulmuştur.

Tüm tasarım desenleri ya da diğer adıyla tasarım kalıplarına yönelik ayrıntılı içeriklere yazının sonundaki bağlantılardan ulaşabilirsiniz.

Facade tasarım deseninin amacı

Facade bir kütüphane, framework ya da başka bir kompleks seri için basitleştirilmiş bir arayüz sunan yapısal bir tasarım desenidir.

Sorun

Kodunuzun komplike bir kütüphane ya da framework içerisindeki bazı nesneler ile çalışacak şekilde oluşturmanız gerektiğini düşünün. Normalde tüm bu nesneleri başlatmanız (initalize), bağımlılıklarını (dependency) takip etmeniz, metodları doğru sırada yürütmeniz vs. vs. bir sürü şey gerekecektir.

Böyle olunca sizin kendi sınıflarınızın mantığı üçüncü parti sınıflarla sıkı sıkıya bağlı olacak, bu da kodun anlaşılması yönetilmesini zorlaştıracaktır.

Çözüm

Facade çok fazla değişken parça içeren kompleks bir sisteme basit bir arayüz sunan bir sınıftır. Facade doğrudan ilgili sınıflarla çalışmaya göre daha sınırlı bir fonksiyonellik sağlar, fakat en çok ihtiyaç duyulan özellikleri içinde barındırır.

Facade uygulamanıza karmaşık ama aslında çok az özelliğini kullanacağınız bir kütüphane entegre edeceğiniz durumlarda harika iş görür. Örneğin, sosyal medyaya komik kedi videoları yükleyen basit bir uygulama yaptığınızı düşünelim. Muhtemelen bu videolar için sayısız özelliği ve parametresi olan profesyonel bir video dönüştürme kütüphanesi kullanacaksınız. Fakat aslında ihtiyacınız encode(filename, format) şeklinde tek bir metod içeren tek bir sınıf. Böyle bir sınıf oluşturup bunu video dönüştürme kütüphanesine bağladığınızda facade’nizi oluşturmuş olacaksınız.

Uygulanabilirlik

Kompleks bir sistemin sınırlı bir parçasına basit bir arayüzle ulaşmak istiyorsanız Facade desenini kullanın.

Bir alt sistemi katmanlara ayırmak istediğinzide Facade kullanın.

Diğer tasarım desenleri/kalıpları ile ilişkisi

  • Facade mevcut nesneler için yeni bir arayüz oluştururken, Adapter mevcut bir arayüzü kullanılabilir hale getirmeye çalışır. Adapter genellikle sadece tek bir nesneyi sararken, Facade geniş bir nesne alt sistemi ile çalışabilir.
  • Sadece alt sistemdeki nesnelerin oluşturulmalarını istemci koddan saklamak istiyorsanız Abstract Factory Facade‘ye bir alternatif olabilir.
  • Flyweight bir çok küçük nesne oluştururken, Facade geniş bir alt sistemi tek nesne ile yönetilebilir hale getirir.
  • Facade ve Mediator‘ün benzer görevleri vardır; her ikiside birbiriyle sıkı sıkıya bağlı bir çok nesnenin bir arada çalışmasını organize eder.
    • Facade bir nesne sistemi için basitleştirilmiş bir arayüz sunar, fakat yeni bir işlevsellik kazandırmaz. Alt sistem Facade’den haberdar değildir. Alt sistem içerisindeki nesneler birbirleriyle direkt iletişimde olurlar.
    • Mediator bir sistemde bileşenler arası iletişimi merkezileştirir. Bileşenler sadece Mediator nesnesinden haberdardır ve birbirleri arasında iletişim yoktur.
  • Çoğu durumda tek bir Facade nesnesi yeterli olduğundan Facade‘ler genellikle Singleton‘a dönüştürülebilirler
  • Facade‘ler kompleks öğelerle bir ara tampon görevi görmeleri nedeniyle Proxy‘lere benzerler. Proxy Facade‘den farklı olarak sevis nesnesi ile aynı arayüze sahiptir, bu da birbirlerinin yerine geçebilmelerini sağlar.

Facade Tasarım Deseni Kod Örnekleri

Örnek PHP Kodu

<?php

namespace RefactoringGuru\Facade\Conceptual;

/**
 * The Facade class provides a simple interface to the complex logic of one or
 * several subsystems. The Facade delegates the client requests to the
 * appropriate objects within the subsystem. The Facade is also responsible for
 * managing their lifecycle. All of this shields the client from the undesired
 * complexity of the subsystem.
 */
class Facade
{
    protected $subsystem1;

    protected $subsystem2;

    /**
     * Depending on your application's needs, you can provide the Facade with
     * existing subsystem objects or force the Facade to create them on its own.
     */
    public function __construct(
        Subsystem1 $subsystem1 = null,
        Subsystem2 $subsystem2 = null
    ) {
        $this->subsystem1 = $subsystem1 ?: new Subsystem1();
        $this->subsystem2 = $subsystem2 ?: new Subsystem2();
    }

    /**
     * The Facade's methods are convenient shortcuts to the sophisticated
     * functionality of the subsystems. However, clients get only to a fraction
     * of a subsystem's capabilities.
     */
    public function operation(): string
    {
        $result = "Facade initializes subsystems:\n";
        $result .= $this->subsystem1->operation1();
        $result .= $this->subsystem2->operation1();
        $result .= "Facade orders subsystems to perform the action:\n";
        $result .= $this->subsystem1->operationN();
        $result .= $this->subsystem2->operationZ();

        return $result;
    }
}

/**
 * The Subsystem can accept requests either from the facade or client directly.
 * In any case, to the Subsystem, the Facade is yet another client, and it's not
 * a part of the Subsystem.
 */
class Subsystem1
{
    public function operation1(): string
    {
        return "Subsystem1: Ready!\n";
    }

    // ...

    public function operationN(): string
    {
        return "Subsystem1: Go!\n";
    }
}

/**
 * Some facades can work with multiple subsystems at the same time.
 */
class Subsystem2
{
    public function operation1(): string
    {
        return "Subsystem2: Get ready!\n";
    }

    // ...

    public function operationZ(): string
    {
        return "Subsystem2: Fire!\n";
    }
}

/**
 * The client code works with complex subsystems through a simple interface
 * provided by the Facade. When a facade manages the lifecycle of the subsystem,
 * the client might not even know about the existence of the subsystem. This
 * approach lets you keep the complexity under control.
 */
function clientCode(Facade $facade)
{
    // ...

    echo $facade->operation();

    // ...
}

/**
 * The client code may have some of the subsystem's objects already created. In
 * this case, it might be worthwhile to initialize the Facade with these objects
 * instead of letting the Facade create new instances.
 */
$subsystem1 = new Subsystem1();
$subsystem2 = new Subsystem2();
$facade = new Facade($subsystem1, $subsystem2);
clientCode($facade);

Örnek Python Kodu

from __future__ import annotations


class Facade:
    """
    The Facade class provides a simple interface to the complex logic of one or
    several subsystems. The Facade delegates the client requests to the
    appropriate objects within the subsystem. The Facade is also responsible for
    managing their lifecycle. All of this shields the client from the undesired
    complexity of the subsystem.
    """

    def __init__(self, subsystem1: Subsystem1, subsystem2: Subsystem2) -> None:
        """
        Depending on your application's needs, you can provide the Facade with
        existing subsystem objects or force the Facade to create them on its
        own.
        """

        self._subsystem1 = subsystem1 or Subsystem1()
        self._subsystem2 = subsystem2 or Subsystem2()

    def operation(self) -> str:
        """
        The Facade's methods are convenient shortcuts to the sophisticated
        functionality of the subsystems. However, clients get only to a fraction
        of a subsystem's capabilities.
        """

        results = []
        results.append("Facade initializes subsystems:")
        results.append(self._subsystem1.operation1())
        results.append(self._subsystem2.operation1())
        results.append("Facade orders subsystems to perform the action:")
        results.append(self._subsystem1.operation_n())
        results.append(self._subsystem2.operation_z())
        return "\n".join(results)


class Subsystem1:
    """
    The Subsystem can accept requests either from the facade or client directly.
    In any case, to the Subsystem, the Facade is yet another client, and it's
    not a part of the Subsystem.
    """

    def operation1(self) -> str:
        return "Subsystem1: Ready!"

    # ...

    def operation_n(self) -> str:
        return "Subsystem1: Go!"


class Subsystem2:
    """
    Some facades can work with multiple subsystems at the same time.
    """

    def operation1(self) -> str:
        return "Subsystem2: Get ready!"

    # ...

    def operation_z(self) -> str:
        return "Subsystem2: Fire!"


def client_code(facade: Facade) -> None:
    """
    The client code works with complex subsystems through a simple interface
    provided by the Facade. When a facade manages the lifecycle of the
    subsystem, the client might not even know about the existence of the
    subsystem. This approach lets you keep the complexity under control.
    """

    print(facade.operation(), end="")


if __name__ == "__main__":
    # The client code may have some of the subsystem's objects already created.
    # In this case, it might be worthwhile to initialize the Facade with these
    # objects instead of letting the Facade create new instances.
    subsystem1 = Subsystem1()
    subsystem2 = Subsystem2()
    facade = Facade(subsystem1, subsystem2)
    client_code(facade)

Diğer Tasarım Kalıpları/Design Patterns

Yaratımsal Kalıplar (Creational Patterns)

Yapısal Kalıplar (Structural Patterns)

Davranışsal Kalıplar (Behavioral Patterns)