Komut Kalıbı
Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 96 / 104.
Komut deseni (Command pattern), bir isteği bir nesne olarak kapsüller; bu da istemcileri farklı isteklerle parametrelendirmenize, işlemleri kuyruğa almanıza veya geri alma işlevini desteklemenize olanak tanır. Bir işlemi tetikleyen nesne ile o işlemin nasıl gerçekleştirileceğini bilen nesneyi birbirinden ayırır.
Desen dört ana bileşene sahiptir: execute() metoduna sahip bir Command arayüzü, belirli eylemleri uygulayan Concrete Commands, asıl işi gerçekleştiren bir Receiver ve komutları tetikleyen bir Invoker:
#include <iostream>
#include <memory>
#include <vector>
// Alıcı - işlemlerin nasıl gerçekleştirileceğini bilir
class Light {
public:
void turnOn() { std::cout << "Light is ON\n"; }
void turnOff() { std::cout << "Light is OFF\n"; }
};
// Komut arayüzü
class Command {
public:
virtual void execute() = 0;
virtual void undo() = 0;
virtual ~Command() = default;
};
// Somut Komutlar
class LightOnCommand : public Command {
Light& light;
public:
LightOnCommand(Light& l) : light(l) {}
void execute() override { light.turnOn(); }
void undo() override { light.turnOff(); }
};
class LightOffCommand : public Command {
Light& light;
public:
LightOffCommand(Light& l) : light(l) {}
void execute() override { light.turnOff(); }
void undo() override { light.turnOn(); }
};
// Çağırıcı (Invoker)
class RemoteControl {
std::vector<std::unique_ptr<Command>> history;
public:
void pressButton(std::unique_ptr<Command> cmd) {
cmd->execute();
history.push_back(std::move(cmd));
}
void pressUndo() {
if (!history.empty()) {
history.back()->undo();
history.pop_back();
}
}
};Her komut, alıcısına bir referans saklar ve yürütüldüğünde uygun metodu çağırır. Çağırıcı, hangi eylemin gerçekleşeceğini bilmez; sadece execute() metodunu çağırır. Komutları bir geçmişte saklayarak, her komutun undo() metodunu çağırıp geri alma işlemini kolayca uygulayabilirsiniz.
İşlemleri kuyruğa almanız, geri al/yinele (undo/redo) özelliğini uygulamanız veya bir isteğin göndericisini işleyicisinden ayırmanız gerektiğinde Command desenini kullanın.
Görev
KolayKomut (Command) desenini kullanarak geri alma (undo) özelliğine sahip bir Metin Düzenleyici inşa edelim. Düzenleme işlemlerinin (metin ekleme ve silme gibi) komut nesneleri olarak kapsüllendiği, kullanıcıların eylemleri yürütmesine ve ardından bunları ters sırada geri almasına olanak tanıyan bir sistem oluşturacaksınız; tıpkı gerçek bir metin düzenleyici gibi.
Kodunuzu dört dosya halinde düzenleyeceksiniz:
TextDocument.h: Gerçek metin içeriğini tutan ve işleyen alıcı (receiver) sınıfını oluşturun.TextDocumentsınıfınız, belgenin içeriğini bir dize (string) olarak saklamalıdır. Şu metotları uygulayın:insertText(const std::string& text)— metni içeriğin sonuna eklerdeleteText(int count)— içeriğin sonundancountkadar karakteri siler (eğer count içerik uzunluğunu aşarsa hiçbir şey yapmaz)getContent()— mevcut içeriği bir sabit referans (const reference) olarak döndürür
Command.h: Komut arayüzünü ve somut komut sınıflarını tanımlayın.Saf sanal (pure virtual)
execute()veundo()metotlarına ve bir sanal yıkıcıya (virtual destructor) sahip soyut birCommandsınıfı oluşturun.Ardından iki somut komut uygulayın:
InsertCommand— birTextDocumentreferansı ve eklenecek bir dize alır. Yürütüldüğünde metni ekler. Geri alındığında, eklenen karakter sayısı kadar karakteri siler.DeleteCommand— birTextDocumentreferansı ve silinecek karakter sayısını alır. Geri alma işleminde geri yükleyebilmek için silinen metni saklaması gerekir. Yürütüldüğünde, sondaki karakterleri kaldırır. Geri alındığında, saklanan metni tekrar içeri ekler.
TextEditor.h: Komut yürütmeyi ve geçmişi yöneten çağırıcıyı (invoker) oluşturun.TextEditorsınıfınız birTextDocumentreferansı tutmalı veunique_ptr<Command>türünde bir vektör kullanarak yürütülen komutların geçmişini tutmalıdır. Şunları uygulayın:executeCommand(std::unique_ptr<Command> cmd)— komutu yürütür ve geçmişe eklerundo()— en son komutu geri alır ve geçmişten kaldırır (geçmiş boşsa hiçbir şey yapmaz)showContent()— mevcut belge içeriğini yazdırır veya içerik boşsa[empty]yazdırır
main.cpp: Geri alma özelliğine sahip Komut desenini gösterin.Üç girdi okuyun:
- Eklenecek ilk metin (string)
- Eklenecek ikinci metin (string)
- Silinecek karakter sayısı (integer)
Bir
TextDocumentve birTextEditoroluşturun. Ardından her adımdan sonra içeriği göstererek şu işlemleri sırasıyla gerçekleştirin:- İlk metni ekleyin, ardından içeriği gösterin
- İkinci metni ekleyin, ardından içeriği gösterin
- Belirtilen sayıda karakteri silin, ardından içeriği gösterin
- Bir kez geri alın, ardından içeriği gösterin
- Bir kez daha geri alın, ardından içeriği gösterin
Örneğin, Hello, World ve 3 girdileriyle:
Hello
Hello World
Hello Wo
Hello World
HelloCode, Editor ve 6 girdileriyle:
Code
CodeEditor
Code
CodeEditor
CodeHer komutun kendisini nasıl tersine çevireceğini bildiğine dikkat edin. DeleteCommand neyi sildiğini hatırlar, böylece onu geri yükleyebilir ve InsertCommand geri alırken tam olarak kaç karakteri kaldıracağını bilir. Komut deseninin gücü budur; işlemler saklanabilen, yürütülebilen ve tersine çevrilebilen birinci sınıf nesneler haline gelir.
Kopya kağıdı
Command deseni, bir isteği bir nesne olarak kapsüller; bu da istemcileri farklı isteklerle parametreleştirmenize, işlemleri kuyruğa almanıza veya geri alma (undo) işlevini desteklemenize olanak tanır. Bir işlemi başlatan nesne ile işlemin nasıl gerçekleştirileceğini bilen nesneyi birbirinden ayırır.
Desenin dört ana bileşeni vardır:
execute()metoduna sahip Komut arayüzü (Command interface)- Belirli eylemleri uygulayan Somut Komutlar (Concrete Commands)
- Gerçek işi gerçekleştiren Alıcı (Receiver)
- Komutları tetikleyen Çağırıcı (Invoker)
Temel yapı:
// Alıcı - işlemlerin nasıl gerçekleştirileceğini bilir
class Light {
public:
void turnOn() { std::cout << "Light is ON\n"; }
void turnOff() { std::cout << "Light is OFF\n"; }
};
// Komut arayüzü
class Command {
public:
virtual void execute() = 0;
virtual void undo() = 0;
virtual ~Command() = default;
};
// Somut Komut
class LightOnCommand : public Command {
Light& light;
public:
LightOnCommand(Light& l) : light(l) {}
void execute() override { light.turnOn(); }
void undo() override { light.turnOff(); }
};
// Çağırıcı
class RemoteControl {
std::vector<std::unique_ptr<Command>> history;
public:
void pressButton(std::unique_ptr<Command> cmd) {
cmd->execute();
history.push_back(std::move(cmd));
}
void pressUndo() {
if (!history.empty()) {
history.back()->undo();
history.pop_back();
}
}
};Her komut, alıcısına bir referans saklar ve yürütüldüğünde uygun metodu çağırır. Çağırıcı hangi eylemin gerçekleşeceğini bilmez; sadece execute() metodunu çağırır. Komutları bir geçmişte saklayarak, her komutun undo() metodunu çağırıp geri alma işlemini uygulayabilirsiniz.
İşlemleri kuyruğa almanız, geri al/yinele (undo/redo) özelliğini uygulamanız veya bir isteğin göndericisi ile işleyicisini birbirinden ayırmanız gerektiğinde Komut desenini kullanın.
Kendin dene
#include <iostream>
#include <string>
#include <memory>
#include "TextDocument.h"
#include "Command.h"
#include "TextEditor.h"
int main() {
// Girdileri oku
std::string firstText;
std::string secondText;
int deleteCount;
std::getline(std::cin, firstText);
std::getline(std::cin, secondText);
std::cin >> deleteCount;
// TODO: Bir TextDocument ve bir TextEditor oluşturun
// TODO: İşlemleri sırasıyla gerçekleştirin ve her birinden sonra içeriği gösterin:
// 1. İlk metni ekleyin, ardından içeriği gösterin
// 2. İkinci metni ekleyin, ardından içeriği gösterin
// 3. Belirtilen sayıda karakteri silin, ardından içeriği gösterin
// 4. Bir kez geri alın, ardından içeriği gösterin
// 5. Bir kez daha geri alın, ardından içeriği gösterin
// İpucu: Komutları oluşturmak için std::make_unique<InsertCommand>(...) ve
// std::make_unique<DeleteCommand>(...) kullanın
return 0;
}
Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Nesne Yönelimli Programlama bölümündeki tüm dersler
1OOP Temelleri
Harici DosyalarC++ Build ve DerlemeBaşlık Dosyaları ve Kaynak DosyalarıAd Alanları ve KapsamC++'ta OOP'ye GirişSınıflar ve Nesneler'this' İşaretçisiMetotlar (Üye Fonksiyonlar)Öznitelikler (Veri Üyeleri)Ctor ve Dtor TemelleriÖzet - Basit Hesap Makinesi4Sınıf Özellikleri
Örnek ve Statik ÜyelerGetter ve Setter MetotlarıConst Üye FonksiyonlarMutable Anahtar KelimesiStatik Metotlar ve DeğişkenlerFriend Fonksiyonlar ve SınıflarÖzet - Banka Hesabı Yöneticisi7Kalıtım
Temel KalıtımKalıtım Erişim SeviyeleriCtor ve Dtor Çağrılma SırasıMetot Geçersiz KılmaSanal Fonksiyonlar ve VTableÇoklu KalıtımSanal KalıtımÖzet - Çalışan Hiyerarşisi10STL Genel Bakış
STL Genel Bakış ve FelsefesiSTL KonteynerleriİteratörlerSTL AlgoritmalarıFunctor'lar ve Lambda İfadeleriÖzet - Kelime Frekansı13Tasarım Kalıpları 1. Bölüm
Tasarım Kalıplarına GirişSingleton KalıbıFactory ve Abstract FactoryBuilder KalıbıObserver KalıbıStrategy Kalıbı2Bellek Yönetimi
Stack ve Heap Bellekİşaretçiler ve ReferanslarDinamik Bellek (new/delete)C++'ta Akıllı İşaretçilerC++'ta RAIIÖzet - Dinamik Dizi Yöneticisi5Kapsülleme
C++'da Erişim BelirleyicilerDerinlemesine Erişim BelirleyicilerBilgi GizlemeStruct vs Classİç İçe ve Dahili SınıflarÖzet - Öğrenci Kayıt Sistemi8Çok Biçimlilik
Derleme ve Çalışma Zamanı Çok BiçimliliğiFonksiyon Aşırı YüklemeSanal Fonksiyonlara Yeniden BakışSaf Sanal FonksiyonlarSoyut SınıflarC++'ta Arayüz TasarımıDynamic Casting ve RTTIÖzet - Şekil Hesaplayıcı11İleri Düzey OOP Kavramları
Kompozisyon ve Kalıtım KarşılaştırmasıCRTP ile Mixin YapılarıPimpl İdiyomuTür Silme (Type Erasure)Enum Sınıfları ve Güçlü TiplendirmeOOP'de İstisna YönetimiÖzel İstisna Hiyerarşileri14Tasarım Kalıpları Bölüm 2
Komut KalıbıAdaptör KalıbıDekoratör KalıbıŞablon Metot KalıbıDurum KalıbıKompozit KalıbıBir Kalıp Olarak RAII3Yapıcılar ve Yıkıcılar
Varsayılan YapıcıParametreli YapıcıKopya YapıcıTaşıma YapıcısıYapıcı İlklendirme ListeleriTemsilci YapıcılarYıkıcılara Derinlemesine BakışÜç / Beş / Sıfır KuralıÖzet - String Sınıfı6Operatör Aşırı Yükleme
Operatör Aşırı Yüklemeye GirişAritmetik Operatör Aşırı YüklemeKarşılaştırma Operatörü Aşırı YüklemeStream OperatörleriAtama Operatörü Aşırı Yükleme[] ve () Operatör Aşırı YüklemeTip Dönüşüm OperatörleriÖzet - Matris Sınıfı9Şablonlar
Fonksiyon ŞablonlarıSınıf ŞablonlarıŞablon ÖzelleştirmeVariadic ŞablonlarSFINAE ve Type Traits TemelleriÖzet - Generic Konteyner