Behavioral — Davranışsal Desenler

Command

Komut Deseni

Amaç: Bir isteği, tüm bilgisiyle birlikte bağımsız bir nesneye dönüştürmek; böylece istekleri kuyruğa almak, kaydetmek ve geri almak (undo) mümkün olur.

1

Basit Anlatım

Teknik terim yok — günlük hayattan bir benzetme

Bir restoranda garson siparişinizi alır ve bir sipariş fişine yazar. Fiş mutfağa gider, sırayla asılır ve aşçı sırası gelince yemeği hazırlar.

Burada önemli olan şu: garsonun yemek yapmayı bilmesine gerek yok, aşçının da sizi tanımasına gerek yok. Aradaki her şeyi fiş taşır: ne istendiği, kaç adet, hangi masa. Fiş beklemeye alınabilir, iptal edilebilir, gün sonunda kaç sipariş verildiği fişlerden sayılabilir.

Bir de "Geri Al" düğmesi

Bir yazı programında Ctrl+Z'ye bastığınızda son yaptığınız işlem geri alınır. Bu mümkündür çünkü program yaptığınız her işlemi bir "fiş" gibi kaydetmiştir ve her fiş kendini nasıl geri alacağını bilir.

Command şunu der: "Bir isteği hemen yapmak yerine, onu bir nota çevir. Not saklanabilir, sıraya konabilir, tekrar edilebilir ya da geri alınabilir."
2

Teknik Anlamı

Bir yazılımcının bakış açısıyla

Command, bir işlemi (metot çağrısını) parametreleriyle birlikte bir nesne içine kapsüller. Bu nesnenin genellikle tek bir Execute() metodu (ve isteğe bağlı Undo()) vardır.

Katılımcılar

Kullanım alanları

Avantajlar

Dezavantajlar

3

C# ile Uygulama

Adım adım, çalışan kod örneği

Senaryo: Basit bir metin editörü. Metin ekleme ve silme işlemleri komut olarak tutulacak; böylece Geri Al ve Yinele desteklenecek.

Adım 1 Receiver: asıl işi bilen sınıf

public class TextDocument
{
    private readonly System.Text.StringBuilder _text = new();

    public void Insert(int position, string value) => _text.Insert(position, value);
    public void Delete(int position, int length) => _text.Remove(position, length);
    public string Substring(int position, int length) =>
        _text.ToString(position, length);
    public int Length => _text.Length;
    public override string ToString() => _text.ToString();
}

Adım 2 Command arayüzü ve somut komutlar

Her komut, işi geri almak için gereken bilgiyi kendi içinde saklar.

public interface ICommand
{
    string Name { get; }
    void Execute();
    void Undo();
}

public class AppendTextCommand : ICommand
{
    private readonly TextDocument _doc;
    private readonly string _text;
    private int _position;

    public AppendTextCommand(TextDocument doc, string text) { _doc = doc; _text = text; }
    public string Name => $"Ekle(\"{_text}\")";

    public void Execute() { _position = _doc.Length; _doc.Insert(_position, _text); }
    public void Undo() => _doc.Delete(_position, _text.Length);
}

public class DeleteLastCommand : ICommand
{
    private readonly TextDocument _doc;
    private readonly int _count;
    private string _deleted = "";

    public DeleteLastCommand(TextDocument doc, int count) { _doc = doc; _count = count; }
    public string Name => $"Sil({_count})";

    public void Execute()
    {
        int start = _doc.Length - _count;
        _deleted = _doc.Substring(start, _count);   // geri alabilmek için sakla
        _doc.Delete(start, _count);
    }
    public void Undo() => _doc.Insert(_doc.Length, _deleted);
}

Adım 3 Invoker: komutları çalıştıran ve geçmişi tutan sınıf

CommandManager komutların ne yaptığını bilmez; sadece Execute ve Undo çağırır.

public class CommandManager
{
    private readonly Stack<ICommand> _undo = new();
    private readonly Stack<ICommand> _redo = new();

    public void Run(ICommand command)
    {
        command.Execute();
        _undo.Push(command);
        _redo.Clear();      // yeni işlem yapılınca yinele geçmişi silinir
        Console.WriteLine($"  Çalıştı : {command.Name}");
    }

    public void Undo()
    {
        if (_undo.TryPop(out var command))
        {
            command.Undo();
            _redo.Push(command);
            Console.WriteLine($"  Geri al : {command.Name}");
        }
    }

    public void Redo()
    {
        if (_redo.TryPop(out var command))
        {
            command.Execute();
            _undo.Push(command);
            Console.WriteLine($"  Yinele  : {command.Name}");
        }
    }
}

Adım 4 Kullanım

Tüm parçaları bir araya getiren istemci (client) kodu:

var doc = new TextDocument();
var editor = new CommandManager();

editor.Run(new AppendTextCommand(doc, "Merhaba"));
editor.Run(new AppendTextCommand(doc, " dünya"));
editor.Run(new AppendTextCommand(doc, "!!!"));
Console.WriteLine($"Metin: \"{doc}\"");

editor.Run(new DeleteLastCommand(doc, 2));
Console.WriteLine($"Metin: \"{doc}\"");

editor.Undo();
editor.Undo();
Console.WriteLine($"Metin: \"{doc}\"");

editor.Redo();
Console.WriteLine($"Metin: \"{doc}\"");

Konsol çıktısı

  Çalıştı : Ekle("Merhaba")
  Çalıştı : Ekle(" dünya")
  Çalıştı : Ekle("!!!")
Metin: "Merhaba dünya!!!"
  Çalıştı : Sil(2)
Metin: "Merhaba dünya!"
  Geri al : Sil(2)
  Geri al : Ekle("!!!")
Metin: "Merhaba dünya"
  Yinele  : Ekle("!!!")
Metin: "Merhaba dünya!!!"

Kodda dikkat edilmesi gerekenler

Özetle: Command, işlemleri nesneye dönüştürür. Bu sayede işlemler saklanabilir, kuyruğa alınabilir, tekrarlanabilir ve geri alınabilir.
← Ana sayfaya dön