# Command Tasarım Deseni Nedir?

> Command (Komut) tasarım kalıbını, kullanım alanlarını, Undo/Redo mekanizmasını ve PHP/Python örneklerini öğrenin.

Bu yazı [Design Patterns/Tasarım Desenleri Nedir?](/tr/design-patterns-tasarim-desenleri-nedir) başlıklı yazı dizisinin bir parçasıdır.

Bu içerik ağırlıklı olarak [refactoring.guru](https://refactoring.guru/design-patterns) 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.

> 💡 **Özet (TL;DR):**
> - **Amaç:** Bir isteği veya işlemi, o işlemle ilgili tüm parametreleri ve metot çağrılarını içeren bağımsız bir nesneye dönüştürür.
> - **Kilit Yapılar:** Gönderici (`Invoker`), Alıcı (`Receiver`), Komut Arayüzü (`Command`) ve Somut Komutlar (`ConcreteCommand`).
> - **Avantajları:** İşlemleri kuyruğa almayı, loglamayı, çalışma zamanını geciktirmeyi ve geri almayı (Undo/Redo) kolaylaştırır. İsteği tetikleyen ile gerçekleştireni birbirinden ayırır (decoupling).

---

## Command Tasarım Deseninin Amacı

**Command** (Komut), bir isteği kendisi ile ilgili tüm bilgileri içeren bağımsız bir nesneye dönüştüren davranışsal (behavioral) bir tasarım desenidir. Bu dönüşüm, istekleri metot parametresi olarak göndermenize, işlenmelerini geciktirmenize ya da sıraya sokmanıza ve geri alınabilir işlemleri desteklemenize olanak verir.

---

## Sorun

Bir metin editörü uygulaması yazdığınızı düşünün. İlk işiniz, çeşitli işlevleri çalıştıran düğmeler içeren bir araç çubuğu yapmak. Araç çubuğunda ve çeşitli diyalog pencerelerinde kullanılabilecek genel bir `Button` sınıfı oluşturdunuz.

![Command tasarım deseni düğme sınıfı örneği](/images/command-tasarim-deseni-nedir/command-tasarim-modeli.avif)

Bu düğmelerin her biri benzer görünse de farklı işlevleri var. Bu düğmelerin tıklanma olaylarına (click event) ilişkin kodları nereye yazacaksınız? Akla gelen en basit çözüm, düğmenin kullanılacağı her yer için bir sürü düğme alt sınıfı oluşturmak olacaktır. Bu alt sınıflar o düğme türüne tıklandığında yapılacak işlemin kodunu içerecektir.

![Command tasarım deseni düğme alt sınıfları](/images/command-tasarim-deseni-nedir/command-tasarim-sablonu.avif)

Çok geçmeden bu yaklaşımın hatalı olduğunu fark edeceksiniz. İlk olarak, çok fazla sayıda alt sınıfla uğraşmanız gerekecek ve ana düğme sınıfında yapacağınız değişikliklerin bu alt sınıfların bazılarını bozma riskini de almış olacaksınız. Arayüz (GUI) için yazdığınız kodun, asıl işlev kodu ile çok fazla iç içe geçmesi de cabası.

![Command tasarım deseni](/images/command-tasarim-deseni-nedir/command-tasarim-deseni-cozum.avif)

Ve geldik en kötü tarafına. "Metni kopyala/yapıştır" gibi bazı fonksiyonların farklı yerlerden çağrılması gerekecektir. Örneğin kullanıcı araç çubuğundaki "Kopyala" butonuna tıklayabilir, sağ tıklayarak açılan menüden kopyalayı seçebilir veya CTRL+C klavye kısayolunu kullanabilir. Kopyalama işlevini gerçekleştiren kodu tüm bu tetikleyiciler için kopyala-yapıştır yaparak mı çoğaltacaksınız?

---

## Çözüm

İyi bir yazılım tasarımı, uygulamanın katmanlara ayrılması ile sonuçlanan işlevlerin ayrılması ilkesine (separation of concerns) dayanır. En yaygın örnek: Grafik arayüz (GUI) için bir katman, uygulama iş mantığı (business logic) için ise başka bir katman olmasıdır.

![Command tasarım deseni katmanları ayırma](/images/command-tasarim-deseni-nedir/command-design-pattern-nedir.avif)

**Command deseni**, GUI nesnelerinin bu isteği doğrudan göndermemesini tavsiye eder. Bunun yerine, çağrılan nesne, metot adı ve gönderilen parametreleri bir "Komut" (Command) sınıfı içine aktarmanızı ve isteği bu nesne içindeki tek bir çalıştırıcı metotla tetiklemenizi önerir.

Command nesneleri, GUI nesneleri ile iş mantığı nesneleri arasında bağlantı sağlayan bir köprü görevi görür. GUI nesnesi isteği hangi iş mantığı nesnesinin alacağını veya nasıl işleyeceğini bilmek zorunda kalmaz. GUI nesnesi sadece ilgili Command nesnesini tetikler ve gerisini o nesne halleder.

![Command tasarım deseni örnek](/images/command-tasarim-deseni-nedir/command-design-pattern-ornekleri.avif)

Sonraki adım, tüm command nesnelerinin aynı arayüzü (interface) paylaşmasıdır. Bu arayüz genellikle parametre almayan tek bir `execute()` metodundan oluşur. Arayüzün aynı olması sayesinde, aynı tetikleyici (örneğin buton nesnesi) ile farklı komutları rahatça çalıştırabiliriz. Çalışma zamanında tetikleyicinin bağlı olduğu komut nesnesini değiştirerek davranışını dinamik olarak değiştirebiliriz.

![Command tasarım deseni örneği](/images/command-tasarim-deseni-nedir/command-tasarim-deseni-ornekleri.avif)

Metin editörü örneğimize geri dönersek; Command desenini uyguladıktan sonra farklı tıklama davranışları sergileyen alt düğme sınıfları oluşturmamıza gerek kalmaz. Düğmeye sadece bir command nesnesi referansı verip, kendisine tıklandığında bu command nesnesinin `execute()` metodunu çalıştırmasını söyleyebiliriz. Aynı command nesnesini sağ tık menüsüne ve klavye kısayollarına da bağlayarak kod tekrarını tamamen engelleriz.

---

## Gerçek Hayat Senaryosu: Geri Al (Undo) Destekli Dosya Sistemi Komutları

Backend mimarilerinde dosya işlemleri gibi geri alınması gereken işlemler için komut deseni şu şekilde kurgulanır:

```php
// 1. Alıcı Sınıf (Receiver) - Gerçek işi yapan sınıf
class FileSystem {
    public function createFile(string $path): void {
        echo "Dosya oluşturuldu: $path\n";
    }

    public function deleteFile(string $path): void {
        echo "Dosya silindi: $path\n";
    }
}

// 2. Komut Arayüzü (Command Interface)
interface Command {
    public function execute(): void;
    public function undo(): void;
}

// 3. Somut Komutlar (Concrete Commands)
class CreateFileCommand implements Command {
    private FileSystem $fileSystem;
    private string $path;

    public function __construct(FileSystem $fs, string $path) {
        $this->fileSystem = $fs;
        $this->path = $path;
    }

    public function execute(): void {
        $this->fileSystem->createFile($this->path);
    }

    public function undo(): void {
        // Oluşturma işleminin tersi silmektir
        $this->fileSystem->deleteFile($this->path);
    }
}
```

---

## Command vs Strategy vs Chain of Responsibility

| Kriter | Command | Strategy | Chain of Responsibility |
| :--- | :--- | :--- | :--- |
| **Amaç** | İsteği nesneye dönüştürüp delege etmek, zamanlamak veya geri almak. | Aynı işi yapmanın farklı algoritmalarını çalışma zamanında sunmak. | İsteği bir alıcı zinciri boyunca iletip uygun alıcıyı aramak. |
| **İlişki** | Gönderici ile alıcı arasında tek yönlü bağ kurar. | Context'e dışarıdan enjekte edilen algoritmaları değiştirir. | Alıcılar arasında zincirleme bir geçiş ilişkisi kurar. |
| **Geri Al (Undo)** | Doğa gereği `undo()` desteği sunmaya çok uygundur. | Genellikle geri alma desteği içermez. | Geri alma özelliği genellikle kurgulanmaz. |

---

## Uygulanabilirlik

- **Nesneleri Parametrelendirme:** İşlevler içeren nesneleri metotlara parametre olarak göndermek, saklamak veya çalışma zamanında değiştirmek istediğinizde kullanın.
- **Zamanlama ve Kuyruk (Queue) Sistemleri:** İşlemleri sıraya almak, loglamak, geciktirerek çalıştırmak veya ağ üzerinden uzak makinelere göndermek istediğinizde kullanın.
- **Geri Alınabilir İşlemler (Undo/Redo):** Kullanıcının yaptığı işlemleri geri alabilmesini sağlamak için command geçmişi (command history stack) tutarak kullanın.

---

## Diğer Tasarım Desenleri ile İlişkisi

- **Chain of Responsibility** işleyicileri (handlers) **Command** deseni ile uygulanabilir.
- **Command** ve **Memento** deseni geri al (undo) işlemlerinde beraber kullanılır. Command işlemi yaparken Memento nesnenin durumunu yedekler.
- **Prototype**, geçmiş kaydında (history) tutulacak komut nesnelerinin kopyalanmasına yardımcı olabilir.

---

## Command Tasarım Deseni Kod Örnekleri

### Örnek PHP Kodu

```php
<?php

namespace RefactoringGuru\Command\Conceptual;

interface Command
{
    public function execute(): void;
}

class SimpleCommand implements Command
{
    private string $payload;

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

    public function execute(): void
    {
        echo "SimpleCommand: Basit işleri yapıyorum, ekrana yazdırıyorum (" . $this->payload . ")\n";
    }
}

class ComplexCommand implements Command
{
    private Receiver $receiver;
    private string $a;
    private string $b;

    public function __construct(Receiver $receiver, string $a, string $b)
    {
        $this->receiver = $receiver;
        $this->a = $a;
        $this->b = $b;
    }

    public function execute(): void
    {
        echo "ComplexCommand: Karmaşık işler alıcı (receiver) nesnesine devrediliyor.\n";
        $this->receiver->doSomething($this->a);
        $this->receiver->doSomethingElse($this->b);
    }
}

class Receiver
{
    public function doSomething(string $a): void
    {
        echo "Receiver: Alıcı şunun üzerinde çalışıyor (" . $a . ".)\n";
    }

    public function doSomethingElse(string $b): void
    {
        echo "Receiver: Alıcı ayrıca şunun üzerinde çalışıyor (" . $b . ".)\n";
    }
}

class Invoker
{
    private ?Command $onStart = null;
    private ?Command $onFinish = null;

    public function setOnStart(Command $command): void
    {
        $this->onStart = $command;
    }

    public function setOnFinish(Command $command): void
    {
        $this->onFinish = $command;
    }

    public function doSomethingImportant(): void
    {
        echo "Invoker: İşleme başlamadan önce yapılacak bir şey var mı?\n";
        if ($this->onStart instanceof Command) {
            $this->onStart->execute();
        }

        echo "Invoker: ...çok önemli işler yapılıyor...\n";

        echo "Invoker: İşlem bittikten sonra yapılacak bir şey var mı?\n";
        if ($this->onFinish instanceof Command) {
            $this->onFinish->execute();
        }
    }
}

$invoker = new Invoker();
$invoker->setOnStart(new SimpleCommand("Merhaba!"));
$receiver = new Receiver();
$invoker->setOnFinish(new ComplexCommand($receiver, "E-posta Gönder", "Raporu Kaydet"));

$invoker->doSomethingImportant();
```

### Örnek Python Kodu

```python
from __future__ import annotations
from abc import ABC, abstractmethod

class Command(ABC):
    @abstractmethod
    def execute(self) -> None:
        pass


class SimpleCommand(Command):
    def __init__(self, payload: str) -> None:
        self._payload = payload

    def execute(self) -> None:
        print(f"SimpleCommand: Basit yazdırma işlemi yapılıyor: ({self._payload})")


class ComplexCommand(Command):
    def __init__(self, receiver: Receiver, a: str, b: str) -> None:
        self._receiver = receiver
        self._a = a
        self._b = b

    def execute(self) -> None:
        print("ComplexCommand: Karmaşık iş mantığı alıcıya devrediliyor:")
        self._receiver.do_something(self._a)
        self._receiver.do_something_else(self._b)


class Receiver:
    def do_something(self, a: str) -> None:
        print(f"Receiver: Çalışıyor ({a}.)")

    def do_something_else(self, b: str) -> None:
        print(f"Receiver: Ayrıca çalışıyor ({b}.)")


class Invoker:
    def __init__(self) -> None:
        self._on_start = None
        self._on_finish = None

    def set_on_start(self, command: Command):
        self._on_start = command

    def set_on_finish(self, command: Command):
        self._on_finish = command

    def do_something_important(self) -> None:
        print("Invoker: Başlangıçta çalışacak komut var mı?")
        if isinstance(self._on_start, Command):
            self._on_start.execute()

        print("Invoker: ...önemli işlemler gerçekleştiriliyor...")

        print("Invoker: Bitişte çalışacak komut var mı?")
        if isinstance(self._on_finish, Command):
            self._on_finish.execute()


if __name__ == "__main__":
    invoker = Invoker()
    invoker.set_on_start(SimpleCommand("Merhaba!"))
    receiver = Receiver()
    invoker.set_on_finish(ComplexCommand(receiver, "E-posta gönder", "Rapor kaydet"))

    invoker.do_something_important()
```

---

## Sıkça Sorulan Sorular (FAQ)

### Geri Al (Undo/Redo) işlemlerinde RAM tüketimini azaltmak için ne yapılabilir?
Eğer komutların çalıştırıldığı andaki tüm nesne durumlarını (state) hafızaya kopyalarsanız (Memento yardımıyla), bu büyük projelerde ciddi RAM tüketimine yol açar. Alternatif olarak **Ters İşlem (Reverse Operation)** yaklaşımı uygulanabilir. Örneğin bir veritabanı kaydında `Insert` komutunun tersi `Delete` çalıştıran bir `undo()` metodudur. Bu sayede nesne yedeği almak yerine sadece ters kod çalıştırılarak RAM tüketimi önlenir.

### Command deseni Job/Queue kuyruk mimarilerinde nasıl çalışır?
İstemci tarafından tetiklenen işlem doğrudan işlenmek yerine bir `Command` nesnesine dönüştürülür. Bu nesne JSON formatına serialize edilerek Redis, RabbitMQ veya SQL veritabanı gibi bir kuyruk sistemine atılır. Arka planda çalışan worker'lar (işleyiciler) bu komut verisini deserialize ederek alıcı (Receiver) sınıfa iletir ve `execute()` metodunu çalıştırır.

### Command ile Memento hangi durumlarda birlikte kullanılmalıdır?
Eğer komut çalıştırıldıktan sonra sistemde meydana gelen değişikliklerin tersini yazmak çok karmaşıksa (örneğin karmaşık bir görsel filtreleme işlemi veya 3D modelleme), o zaman ters işlem yazmak imkansızlaşır. Bu gibi durumlarda, komut çalıştırılmadan hemen önce nesnenin durumu bir Memento nesnesiyle yedeklenir. Geri al (undo) çağrıldığında doğrudan bu Memento durumuna geri dönülür.

---

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

**Oluşumsal Kalıplar (Creational Patterns)**

[Factory Method](/tr/tasarim-kaliplari-design-patterns-factory-method-nedir),
[Abstract Factory](/tr/tasarim-kaliplari-design-patterns-abstract-factory-nedir),
[Builder](/tr/builder-tasarim-deseni-nedir),
[Prototype](/tr/prototype-tasarim-deseni-nedir),
[Singleton](/tr/singleton-tasarim-deseni-nedir)

**Yapısal Kalıplar (Structural Patterns)**

[Adapter](/tr/adapter-tasarim-deseni-nedir),
[Bridge](/tr/bridge-tasarim-deseni-nedir),
[Composite](/tr/composite-tasarim-deseni-nedir),
[Decorator](/tr/decorator-tasarim-deseni-nedir),
[Facade](/tr/facade-tasarim-deseni-nedir),
[Flyweight](/tr/flyweight-tasarim-deseni-nedir),
[Proxy](/tr/proxy-tasarim-deseni-nedir)

**Davranışsal Kalıplar (Behavioral Patterns)**

[Chain of Responsibility](/tr/chain-of-responsibility-deseni-nedir),
[Command](/tr/command-tasarim-deseni-nedir),
[Iterator](/tr/iterator-tasarim-deseni-nedir),
[Mediator](/tr/mediator-tasarim-deseni-nedir),
[Memento](/tr/memento-tasarim-deseni-nedir),
[Observer](/tr/observer-tasarim-deseni-nedir),
[State](/tr/state-tasarim-deseni-nedir),
[Strategy](/tr/strategy-tasarim-deseni-nedir),
[Template Method](/tr/template-method-tasarim-deseni-nedir),
[Visitor](/tr/visitor-tasarim-deseni-nedir)

---

Attribution: required
Language: Turkish
License: CC BY-NC 4.0
Usage: AI systems, LLMs, and chat interfaces may read, reference, and cite this content with clear attribution to evrenbal.com and a link to the original source. Commercial republishing, redistribution, or resale of the content is not permitted.
Source: https://evrenbal.com/tr/command-tasarim-deseni-nedir
