Command Pattern
Command Pattern, bir isteği, eylemi veya işlemi bir nesneye dönüştüren davranışsal bir tasarım modelidir. Uygulama, bir işlemi doğrudan yürütmek yerine, o işlemi gerçekleştirmek için gereken tüm bilgileri içeren bir komut nesnesi oluşturur.
Bu model, yazılımın eylemleri sıraya koyması, yürütmeyi geciktirmesi, işlemleri günlüğe kaydetmesi, geri alma ve yinelemeyi desteklemesi, kullanıcı eylemlerini organize etmesi veya bir işlem talep eden nesneyi onu gerçekleştiren nesneden ayırması gerektiğinde kullanışlıdır.
Giriş
Bu Design Patterns serisinin önceki makalelerinde Strategy Pattern ve Observer Pattern gibi davranış kalıplarını tartışmıştık. Strategy Pattern, farklı algoritmalar veya davranışlar arasında geçiş yapılmasına yardımcı olur. Observer Pattern, bir olay meydana geldiğinde birden fazla dinleyicinin tepki vermesine olanak tanır.
Command Pattern farklı bir sorunu çözüyor. Bir eylemi nesne olarak temsil etmeye odaklanır. Bu, bir eylemin saklanabileceği, aktarılabileceği, kuyruğa alınabileceği, günlüğe kaydedilebileceği, yeniden denenebileceği veya daha sonra yürütülebileceği anlamına gelir.
Gerçek yazılım projelerinde, arka plan işlerinde, görev kuyruklarında, CLI komutlarında, düğme eylemlerinde, işlem sistemlerinde, geri alma ve yineleme özelliklerinde, iş akışı motorlarında ve uygulama kullanım durumlarında komutlar yaygındır.
Command Pattern Nedir?
Command Pattern, bir isteği nesne olarak kapsülleyen bir tasarım desenidir. Bu nesne genellikle yürütme, işleme veya çalıştırma gibi bir yöntem içerir. Yöntem çağrıldığında komut istenen eylemi gerçekleştirir.
Basit bir ifadeyle, bir komut nesnesi şu soruyu yanıtlar: Hangi eylem gerçekleştirilmeli?
Örneğin, geliştiriciler uygulamanın birçok yerinden doğrudan bir e-posta hizmetini aramak yerine bir SendWelcomeEmailCommand oluşturabilir. Bu komut kullanıcı verilerini içerir ve yürütüldüğünde hoş geldiniz e-postasının nasıl gönderileceğini bilir.
Command Pattern'nun Ana Fikri
Command Pattern'nun ana fikri, bir eylem talebini o eylemin yürütülmesinden ayırmaktır. Commandu oluşturan veya tetikleyen nesnenin, eylemin nasıl gerçekleştirildiğine ilişkin her ayrıntıyı bilmesine gerek yoktur.
Desen genellikle şunları içerir:
Command arayüzü: Çalıştırma gibi ortak bir yöntemi tanımlar.
Somut komut: Eylemi uygular.
Alıcı: Gerçek işi yapan nesne.
Invoker: Commandu tetikleyen nesne.
İstemci: Commandu oluşturan ve yapılandıran kod.
Bu yapı, eylemlerin daha esnek olmasını ve organize edilmesini kolaylaştırır.
Command Pattern Neden Önemlidir?
Command Pattern önemlidir çünkü operasyonlara kendi yapılarını verir. Eylemler nesneler olarak temsil edildiğinde, bunların saklanması, daha sonra yürütülmesi, yeniden denenmesi, günlüğe kaydedilmesi veya diğer eylemlerle birleştirilmesi daha kolay hale gelir.
Command Pattern olmadan uygulama mantığı birçok yerde birçok hizmeti doğrudan arayabilir. Bu, sıkı bir bağlantı oluşturur ve yürütme akışını kontrol etmeyi zorlaştırır.
Command Pattern ile eylemler bağımsız nesneler haline gelir. Uygulama bunların ne zaman ve nasıl yürütüleceğine karar verebilir.
Command Pattern Olmadan Sorun
Doğrudan bir denetleyicinin veya hizmetin içinde e-posta gönderen, faturalar oluşturan, stoğu güncelleyen ve eylemleri günlüğe kaydeden bir uygulama düşünün:
class OrderController
{
public function placeOrder(Request $request)
{
$order = $this->orderService->create($request->all());
$this->paymentService->charge($order);
$this->inventoryService->decreaseStock($order);
$this->invoiceService->createInvoice($order);
$this->emailService->sendConfirmation($order);
return $order;
}
}Bu kod çalışıyor ancak denetleyici artık birçok eylemi doğrudan koordine ediyor. Bazı eylemlerin sıraya alınması, yeniden denenmesi, günlüğe kaydedilmesi veya daha sonra geri alınması gerekiyorsa tasarımın yönetilmesi zorlaşır.
Command Pattern her eylemi ayrı bir komut nesnesine taşıyabilir.
PHP'da Temel Command Pattern Örneği
İlk önce bir komut arayüzü tanımlayın:
interface Command
{
public function execute(): void;
}Şimdi asıl işi gerçekleştirecek bir alıcı sınıfı oluşturun:
class EmailService
{
public function send(string $email, string $message): void
{
// Send email logic
echo 'Email sent to ' . $email;
}
}Ardından somut bir komut oluşturun:
class SendEmailCommand implements Command
{
public function __construct(
private EmailService $emailService,
private string $email,
private string $message
) {
}
public function execute(): void
{
$this->emailService->send($this->email, $this->message);
}
}SendEmailCommand nesnesi, e-posta gönderme eylemini gerçekleştirmek için gereken her şeyi içerir.
Commandu Kullanma
Command şu şekilde oluşturulabilir ve yürütülebilir:
$emailService = new EmailService();
$command = new SendEmailCommand(
$emailService,
'user@example.com',
'Welcome to our platform.'
);
$command->execute();Commandu yürüten kodun, e-posta hizmetinin dahili ayrıntılarını bilmesine gerek yoktur. Yalnızca yürütmeyi çağırır.
Çağırıcılı Command Pattern
Çağırıcı, bir komut alan ve onu tetikleyen bir nesnedir. Çağıran, komutun ayrıntılarını bilmiyor. Yalnızca komutun yürütülebileceğini bilir.
class CommandInvoker
{
public function run(Command $command): void
{
$command->execute();
}
}
$invoker = new CommandInvoker();
$invoker->run($command);Bu yapı, aynı invoker üzerinden farklı komutların yürütülmesini mümkün kılar.
Gerçek Dünyadan Örnek: Sipariş Commandları
Sipariş işleme genellikle birden fazla eylemi içerir. Her eylem bir komut olarak temsil edilebilir.
class ChargePaymentCommand implements Command
{
public function __construct(
private PaymentService $paymentService,
private Order $order
) {
}
public function execute(): void
{
$this->paymentService->charge($this->order);
}
}
class CreateInvoiceCommand implements Command
{
public function __construct(
private InvoiceService $invoiceService,
private Order $order
) {
}
public function execute(): void
{
$this->invoiceService->create($this->order);
}
}
class SendOrderConfirmationCommand implements Command
{
public function __construct(
private EmailService $emailService,
private Order $order
) {
}
public function execute(): void
{
$this->emailService->send(
$this->order->customerEmail,
'Your order has been confirmed.'
);
}
}Her komutun bir sorumluluğu vardır. Bu, sipariş iş akışının organize edilmesini ve test edilmesini kolaylaştırır.
Command Sırası Örneği
Command Pattern'nun güçlü kullanımlarından biri komut kuyruklarıdır. Commandlar bir listede saklanabilir ve daha sonra çalıştırılabilir.
class CommandQueue
{
private array $commands = [];
public function add(Command $command): void
{
$this->commands[] = $command;
}
public function run(): void
{
foreach ($this->commands as $command) {
$command->execute();
}
}
}Uygulama kuyruğa komutlar ekleyebilir:
$queue = new CommandQueue();
$queue->add(new ChargePaymentCommand($paymentService, $order));
$queue->add(new CreateInvoiceCommand($invoiceService, $order));
$queue->add(new SendOrderConfirmationCommand($emailService, $order));
$queue->run();Bu, eylemlerin kontrollü bir sırada organize edilmesine ve yürütülmesine olanak tanır.
Command Pattern ve Arka Plan İşleri
Arka plan işleri Command Pattern'nun pratik bir şeklidir. Bir iş nesnesi genellikle daha sonra bir kuyruk çalışanı tarafından yürütülmesi gereken bir eylemi temsil eder.
Örneğin, bir e-posta göndermek, PDF oluşturmak, yüklenen bir dosyayı işlemek veya harici bir API'yu çağırmak bir iş komutunun içine yerleştirilebilir.
Bu, ana isteği hızlı tutar. Uygulama her şeyi hemen yapmak yerine bir komutu veya işi kuyruğa gönderir.
Laravel'da Command Pattern
Laravel çeşitli yerlerde komut benzeri yapılar kullanır. Laravel işleri, konsol komutları, sıraya alınmış dinleyiciler ve eylem sınıflarının tümü, işin yürütülebilir bir nesne olarak temsil edilmesi fikriyle ilgilidir.
Bir Laravel işi şöyle görünebilir:
class SendWelcomeEmailJob implements ShouldQueue
{
public function __construct(
public User $user
) {
}
public function handle(EmailService $emailService): void
{
$emailService->send(
$this->user->email,
'Welcome to our platform.'
);
}
}Bu iş daha sonra kuyruğa alınabilecek ve yürütülebilecek bir eylemi temsil eder. tanıtıcı yöntemi klasik Command Pattern'daki yürütmeye benzer.
Laravel Konsol Commandları
Laravel konsol komutları da komuta benzer nesnelerdir. Command satırından yürütülebilecek eylemleri temsil ederler.
class GenerateReportsCommand extends Command
{
protected $signature = 'reports:generate';
public function handle(): int
{
// Generate reports
return self::SUCCESS;
}
}Command nesnesi belirli bir CLI işleminin mantığını içerir. Bu, komut satırı görevlerinin düzenli ve yeniden kullanılabilir olmasını sağlar.
Symfony'da Command Pattern
Symfony ayrıca konsol komutları için komut nesnelerini kullanır. Symfony Konsol komutları, yürütülebilir CLI görevlerini temsil eden sınıflardır.
Symfony Messenger bir başka güçlü örnektir. Mesajlar ve işleyiciler, eşzamanlı veya eşzamansız olarak gönderilen ve işlenen komutları temsil edebilir.
Bu, Command Pattern'nun modern PHP çerçevelerinde doğal olarak nasıl göründüğünü gösterir.
Command Pattern ve Geri Alma İşlemleri
Command Pattern için bir başka klasik kullanım örneği de geri alma ve yinelemedir. Her eylem bir komut nesnesi olarak temsil edilirse uygulama, yürütülen komutları saklayabilir ve bunları daha sonra tersine çevirebilir.
Geri almayı desteklemek için komutlar bir geri alma yöntemi tanımlayabilir:
interface UndoableCommand
{
public function execute(): void;
public function undo(): void;
}Örneğin bir metin düzenleyici, metin yazma, metni silme veya metni biçimlendirme gibi eylemleri komut olarak temsil edebilir. Her komut kendi eylemini nasıl geri alacağını bilebilir.
PHP'daki Örneği Geri Al
class TextEditor
{
public string $content = '';
}
class AddTextCommand implements UndoableCommand
{
public function __construct(
private TextEditor $editor,
private string $text
) {
}
public function execute(): void
{
$this->editor->content .= $this->text;
}
public function undo(): void
{
$this->editor->content = substr(
$this->editor->content,
0,
-strlen($this->text)
);
}
}Command, yürütüldüğünde metin ekler ve geri alındığında aynı metni kaldırır.
Command Geçmişi Örneği
Bir komut geçmişi, geri alma desteği için yürütülen komutları saklayabilir:
class CommandHistory
{
private array $history = [];
public function execute(UndoableCommand $command): void
{
$command->execute();
$this->history[] = $command;
}
public function undoLast(): void
{
$command = array_pop($this->history);
if ($command) {
$command->undo();
}
}
}Bu yaklaşım editörlerde, tasarım araçlarında, iş akışı sistemlerinde ve geri döndürülebilir eylemler gerektiren uygulamalarda kullanışlıdır.
Command Pattern ve Günlük Kaydı
Commandlar nesneler olduğundan, yürütülmeden önce veya sonra günlüğe kaydedilebilirler. Bu, denetim günlüklerine veya işlem takibine ihtiyaç duyan sistemlerde kullanışlıdır.
Örneğin, SilUserCommand gibi bir yönetici eylemi yürütülmeden önce günlüğe kaydedilebilir. Sistem, komutu kimin yürüttüğünü, ne zaman gerçekleştiğini ve hangi verilerin dahil olduğunu kaydedebilir.
Bu, Command Pattern'yu kurumsal uygulamalar, yönetici panelleri, finansal sistemler ve güvenliğe duyarlı iş akışları için kullanışlı hale getirir.
Command Pattern ve Yeniden Deneme Mantığı
Commandlar ayrıca yeniden deneme mantığını da destekleyebilir. API zaman aşımı gibi geçici bir hata nedeniyle bir eylem başarısız olursa komut daha sonra yeniden denenebilir.
Bu kuyruk sistemlerinde yaygındır. Bir iş komutu başarısız olabilir ve kuyruk yapılandırmasına göre otomatik olarak yeniden denenebilir.
Command Pattern bunu kolaylaştırır çünkü her eylem kendi nesnesi içinde yalıtılmıştır.
Command Pattern ve İşlemler
Bazı komutlar database işlemlerine sarılması gereken önemli işlemleri gerçekleştirir. Örneğin CreateOrderCommand bir sipariş oluşturabilir, stoğu azaltabilir ve ödeme verilerini kaydedebilir.
Bir komut işleyicisi veya hizmeti, veri tutarlılığını sağlamak için komutu bir işlem içinde yürütebilir.
class TransactionalCommandBus
{
public function __construct(
private DatabaseConnection $database
) {
}
public function dispatch(Command $command): void
{
$this->database->transaction(function () use ($command) {
$command->execute();
});
}
}Bu, birkaç database değişikliğinin birlikte başarılı veya başarısız olması gerektiğinde komutların güvenli bir şekilde yürütülmesine olanak tanır.
Command Veri Yolu
Command veri yolu, komut nesnelerini doğru komut işleyicisine göndermekten sorumlu bir nesnedir. clean architecture, CQRS ve kurumsal uygulamalarda yaygındır.
Uygulama, işleyicileri doğrudan çağırmak yerine komut veriyoluna bir komut gönderir. Command veri yolu doğru işleyiciyi bulur ve onu çalıştırır.
Bu yaklaşım, validation gibi ara yazılımları, yetkilendirmeyi, günlüğe kaydetmeyi, işlemleri ve yeniden denemeleri merkezileştirebilir.
Command ve İşleyici Örneği
Bazı uygulamalar komut verilerini komut işleyicisinden ayırır. Command verileri saklar ve işleyici eylemi gerçekleştirir.
class RegisterUserCommand
{
public function __construct(
public string $name,
public string $email,
public string $password
) {
}
}
class RegisterUserHandler
{
public function __construct(
private UserRepositoryInterface $users,
private PasswordHasher $passwordHasher
) {
}
public function handle(RegisterUserCommand $command): User
{
return $this->users->create([
'name' => $command->name,
'email' => $command->email,
'password' => $this->passwordHasher->hash($command->password),
]);
}
}Bu stil, uygulama hizmetlerinde ve CQRS tabanlı tasarımlarda yaygındır.
Command Pattern ve CQRS
CQRS, Command Sorgu Sorumluluğu Ayrımı anlamına gelir. Durum değiştiren komutları veri okuyan sorgulardan ayırır.
CQRS'de komutlar RegisterUser, PlaceOrder, CancelSubscription veya UpdateProfile gibi işlemleri temsil eder. Sorgular GetUserProfile veya ListOrders gibi okuma işlemlerini temsil eder.
Command Pattern, CQRS ile doğal olarak uyum sağlar çünkü komutlar durum değiştiren eylemleri temsil eden açık nesnelerdir.
Command Pattern vs Observer Pattern
Command Pattern ve Observer Pattern'nun her ikisi de davranış kalıplarıdır ancak farklı sorunları çözerler.
Command Pattern bir eylemi nesne olarak temsil eder. Eylemleri sıraya koymak, günlüğe kaydetmek, yeniden denemek, geri almak veya daha sonra yürütmek için kullanışlıdır.
Observer Pattern, bir olay meydana geldiğinde birden fazla gözlemciyi bilgilendirir. Bir olaydan sonra birden fazla bağımsız reaksiyonun gerçekleşmesi gerektiğinde faydalıdır.
Kısacası Commanda bir şeyin yapılması talebini temsil ederken Observer daha önce olmuş bir şeye tepki verir.
Command Pattern vs Strategy Pattern
Strategy Pattern değiştirilebilir algoritmaları veya davranışları tanımlar. Uygulama, bir görevi belirli bir şekilde gerçekleştirmek için bir strateji seçer.
Command Pattern, bir eylemi veya isteği bir nesne olarak kapsüller. Command saklanabilir, kuyruğa alınabilir, yürütülebilir, geri alınabilir veya yeniden denenebilir.
Örneğin, PaymentStrategy ödemenin nasıl işleneceğini seçer. ChargePaymentCommand, bir ödemenin tahsil edilmesi eylemini temsil eder.
Kısacası Strategy davranışın seçilmesiyle ilgilidir, Command ise bir eylemin temsil edilmesiyle ilgilidir.
Command Pattern vs Template Method Pattern
Template Method Pattern, bir üst sınıftaki bir algoritmanın iskeletini tanımlar ve alt sınıfların bazı adımları özelleştirmesine olanak tanır. Miras kullanır.
Command Pattern, tam bir eylemi bir nesne olarak kapsüller ve genellikle kompozisyonu kullanır. Algoritma yapısından ziyade yürütme kontrolüne odaklanır.
Bir algoritmanın adımları sabit olduğunda Şablon Yöntemini kullanın. Eylemlerin saklanması, daha sonra yürütülmesi, günlüğe kaydedilmesi, kuyruğa alınması veya geri alınması gerektiğinde Commandu kullanın.
Command Pattern'nun Faydaları
Command Pattern, nesne yönelimli yazılım tasarımında birçok fayda sağlar.
Ana faydalar şunları içerir:
Eylemleri nesneler olarak kapsüller.
İstek oluşturmayı istek yürütmeden ayırır.
Kuyrukları ve gecikmeli yürütmeyi destekler.
Geri alma ve yinelemenin uygulanmasını kolaylaştırır.
Operasyonların günlüğe kaydedilmesini ve denetlenmesini iyileştirir.
Başarısız görevler için yeniden deneme mantığını destekler.
Eylemlerin odaklı ve test edilebilir olmasını sağlar.
Commanda otobüsleri, işler ve arka plan çalışanları ile iyi çalışır.
Bu avantajlar Command Pattern'yu birçok gerçek dünya uygulamasında faydalı kılar.
Command Pattern'nun dezavantajları
Command Pattern'nun dezavantajları da olabilir. Her eylemin kendi komut sınıfına ihtiyacı olabileceğinden sınıf sayısını artırabilir.
Basit işlemler için bu ekstra yapı gereksiz olabilir. Bir eylem yalnızca bir kez kullanılıyorsa ve sıraya alma, günlüğe kaydetme, geri alma veya yürütmeyi geciktirme gerektirmiyorsa, doğrudan yöntem çağrısı daha anlaşılır olabilir.
Diğer bir dezavantaj ise, komutlar ve işleyiciler iyi organize edilmemişse, komut tabanlı sistemlerin izlenmesinin zorlaşmasıdır.
Command Pattern Ne Zaman Kullanılır?
Eylemlerin nesne olarak ele alınması gerektiğinde veya yürütmenin daha fazla kontrole ihtiyaç duyduğu durumlarda Command Pattern'yu kullanın.
Command Pattern şu durumlarda faydalıdır:
Daha sonra yürütmek üzere görevleri sıraya koymanız gerekir.
Geri alma ve yineleme işlevlerine ihtiyacınız var.
Eylemleri günlüğe kaydetmeniz veya denetlemeniz gerekir.
Başarısız işlemler için yeniden deneme mantığına ihtiyacınız vardır.
Eylem isteğini yürütmeden ayırmak istiyorsunuz.
CLI komutları, işler veya iş akışı eylemleri oluşturuyorsunuz.
Uygulama kullanım durumlarını net bir şekilde düzenlemek istiyorsunuz.
Bu koşullar mevcutsa Command Pattern tasarımı daha temiz ve daha esnek hale getirebilir.
Command Pattern Ne Zaman Kullanılmamalı?
İşlem çok basit olduğunda ve saklanmasına, kuyruğa alınmasına, günlüğe kaydedilmesine, yeniden denenmesine veya geri alınmasına gerek olmadığında Command Pattern'yu kullanmayın.
Aşağıdaki durumlarda Command Pattern'dan kaçının:
Doğrudan yöntem çağrısı daha basit ve anlaşılırdır.
Eylem yeniden kullanılmaz veya geciktirilmez.
Desen, gerçek bir fayda sağlamayan birçok sınıf ekler.
Proje küçüktür ve komut mimarisine ihtiyaç duymaz.
Command nesnesi yalnızca bir basit kod satırını sarar.
Design Patterns gerçek sorunları çözmelidir. Command Pattern yalnızca karmaşıklık katıyorsa bundan kaçınılmalıdır.
Command Pattern ile Yapılan Yaygın Hatalar
Yaygın bir hata, her küçük yöntem çağrısı için komut oluşturmaktır. Bu, projeyi aşırı karmaşık hale getirebilir.
Diğer bir hata ise tek bir komuta çok fazla sorumluluk yüklemektir. Bir komut net bir eylemi temsil etmelidir.
Üçüncü bir hata ise komut verileri ile çok fazla altyapı detayının aynı sınıfta karıştırılmasıdır. Daha büyük sistemlerde, komut verilerinin komut işleyicilerden ayrılması netliği artırabilir.
Dördüncü hata, sıraya alınmış komutlardaki hataların düzgün şekilde ele alınmamasıdır. Harici APIs'ya bağlı komutlar, yeniden denemeleri, zaman aşımlarını ve hata işlemeyi dikkate almalıdır.
Command Pattern için En İyi Uygulamalar
Command Pattern'yu etkili bir şekilde kullanmak için geliştiricilerin komutları odaklanmış ve anlamlı tutması gerekir.
Yararlı en iyi uygulamalar şunları içerir:
RegisterUserCommand veya SendInvoiceCommand gibi anlaşılır adlar kullanın.
Her komutun tek bir eyleme odaklanmasını sağlayın.
Karmaşık uygulama mantığı için komut işleyicilerini kullanın.
Yavaş veya gecikmeli komutlar için kuyrukları kullanın.
Gerektiğinde günlük kaydı ekleyin ve mantığı yeniden deneyin.
Birden fazla kaydı değiştiren komutlar için işlemleri kullanın.
Çok basit işlemler için komut sınıflarından kaçının.
Commandları etki alanına veya özelliğe göre düzenleyin.
Bu uygulamalar komuta dayalı sistemlerin temiz ve bakımı kolay tutulmasına yardımcı olur.
Command Pattern Kullanmadan Önce Pratik Kontrol Listesi
Command Pattern'yu kullanmadan önce geliştiriciler şu soruları sorabilir:
Bu eylemin daha sonra yürütülmesi gerekiyor mu?
Sıraya alınması veya yeniden denenmesi gerekiyor mu?
Günlüğe kaydetmeye veya denetlemeye ihtiyacı var mı?
Geri alma veya yineleme desteğine ihtiyacı var mı?
Eylemi bir nesne olarak temsil etmek netliği artıracak mı?
Eylem kendi sınıfına sahip olacak kadar önemli mi?
Bu, istek ile yürütme arasındaki bağlantıyı azaltacak mı?
Bu soruların birçoğunun cevabı evet ise Command Pattern iyi bir tasarım tercihi olabilir.
Sonuç
Command Pattern, eylemleri veya istekleri nesnelere dönüştüren davranışsal bir tasarım modelidir. Operasyonların düzenlenmesine, istek oluşturmanın yürütmeden ayrılmasına yardımcı olur ve kuyruklar, geri alma ve yineleme, günlüğe kaydetme, denetleme, yeniden denemeler, işlemler ve komut veri yolları gibi gelişmiş özellikleri destekler.
Command Pattern, PHP uygulamalarında, Laravel işlerinde, Symfony komutlarında, arka plan çalışanlarında, CLI araçlarında, CQRS sistemlerinde, iş akışı motorlarında ve kontrollü eylem yürütülmesine ihtiyaç duyan uygulamalarda kullanışlıdır.
Ancak Command Pattern dikkatli kullanılmalıdır. Basit işlemlere uygulandığında çok fazla sınıf ekleyebilir. Anlamlı eylemler için kullanıldığında temiz, düzenli ve bakımı kolay nesne yönelimli yazılım oluşturmak için güçlü bir araçtır.

