Menu
Coddy logo textTech

Strategy Kalıbı

Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 95 / 104.

Strateji deseni (Strategy pattern), bir algoritma ailesi tanımlar, her birini kapsüller ve onları birbirinin yerine kullanılabilir hale getirir. Bu, bir nesnenin davranışını kodunu değiştirmeden çalışma zamanında değiştirmenize olanak tanır; algoritma, onu kullanan istemcilerden bağımsız olarak değişir.

Desen üç parçadan oluşur: algoritma metodunu bildiren bir Strateji arayüzü, farklı varyasyonları uygulayan Somut Stratejiler ve bir strateji kullanan bir Bağlam:

#include <iostream>
#include <memory>

// Strategy arayüzü
class PaymentStrategy {
public:
    virtual void pay(int amount) = 0;
    virtual ~PaymentStrategy() = default;
};

// Somut stratejiler
class CreditCardPayment : public PaymentStrategy {
public:
    void pay(int amount) override {
        std::cout << "Paid " << amount << " via Credit Card\n";
    }
};

class PayPalPayment : public PaymentStrategy {
public:
    void pay(int amount) override {
        std::cout << "Paid " << amount << " via PayPal\n";
    }
};

// Bağlam (Context)
class ShoppingCart {
    std::unique_ptr<PaymentStrategy> strategy;
public:
    void setPaymentMethod(std::unique_ptr<PaymentStrategy> s) {
        strategy = std::move(s);
    }
    
    void checkout(int total) {
        if (strategy) strategy->pay(total);
    }
};

int main() {
    ShoppingCart cart;
    
    cart.setPaymentMethod(std::make_unique<CreditCardPayment>());
    cart.checkout(100);
    
    cart.setPaymentMethod(std::make_unique<PayPalPayment>());
    cart.checkout(50);
}

ShoppingCart hangi ödeme yöntemini kullandığını bilmez; sadece ayarlanmış olan strateji neyse onun üzerinde pay() metodunu çağırır. setPaymentMethod() ile çalışma zamanında stratejileri değiştirebilirsiniz, bu da sistemi esnek ve yeni ödeme seçenekleriyle genişletilmesi kolay hale getirir.

Belirli bir görev için birden fazla algoritmanız olduğunda ve bunlar arasında dinamik olarak geçiş yapmak istediğinizde veya davranış seçimi için koşullu ifadelerden kaçınmak istediğinizde Strategy desenini kullanın.

challenge icon

Görev

Kolay

Farklı gönderim yöntemlerine göre teslimat maliyetlerini hesaplamak için Strateji (Strategy) desenini kullanan bir Gönderim Hesaplayıcı oluşturalım. Bu, algoritmaları çalışma zamanında değiştirmeniz gereken pratik bir senaryodur; aynı paket, her biri kendi fiyatlandırma mantığına sahip olan kara, hava veya ekspres yoluyla gönderilebilir.

Kodunuzu üç dosya halinde düzenleyeceksiniz:

  • ShippingStrategy.h: Strateji arayüzünüzü ve somut gönderim stratejilerini tanımlayın.

    Gönderim maliyetini double olarak döndüren saf sanal bir calculateCost(double weight) metoduna ve sanal bir yıkıcıya (destructor) sahip soyut bir ShippingStrategy sınıfı oluşturun.

    Ardından üç somut strateji uygulayın:

    • GroundShipping — ağırlık birimi başına 1.5 maliyet (weight * 1.5)
    • AirShipping — ağırlık birimi başına 4.0 maliyet (weight * 4.0)
    • ExpressShipping — ağırlık birimi başına 6.5 maliyet artı 10.0 sabit ücret (weight * 6.5 + 10.0)
  • ShippingService.h: Bir gönderim stratejisi kullanan bağlam (context) sınıfını oluşturun.

    ShippingService sınıfınız, özel bir üye olarak bir std::unique_ptr<ShippingStrategy> tutmalıdır. Şunları uygulayın:

    • Gönderim yöntemini değiştirmek için bir setStrategy(std::unique_ptr<ShippingStrategy> strategy) metodu
    • Maliyeti hesaplamak ve döndürmek için mevcut stratejiyi kullanan bir calculateShipping(double weight) metodu

    calculateShipping çağrıldığında herhangi bir strateji ayarlanmamışsa 0.0 döndürün.

  • main.cpp: Çalışma zamanında strateji değiştirmeyi gösterin.

    İki girdi okuyun:

    1. Paket ağırlığı (double)
    2. Gönderim yöntemi: ground, air veya express

    Bir ShippingService oluşturun ve girdi yöntemine göre uygun stratejiyi ayarlayın. Gönderim maliyetini hesaplayın ve yazdırın.

    Ardından farklı bir stratejiye geçin (girdi air değilse air kullanın, aksi takdirde ground kullanın) ve aynı ağırlık için maliyeti tekrar hesaplayın. Bu, çalışma zamanında strateji değiştirmenin gücünü gösterir.

    Her maliyeti, yöntem adıyla ön eklenmiş olarak tam olarak bir ondalık basamakla kendi satırına yazdırın:

    [Method]: $[cost]

Örneğin, 5.0 ve ground girdileriyle:

Ground: $7.5
Air: $20.0

3.0 ve express girdileriyle:

Express: $29.5
Air: $12.0

10.0 ve air girdileriyle:

Air: $40.0
Ground: $15.0

ShippingService sınıfının her bir fiyatlandırma algoritmasının ayrıntılarını bilmesine nasıl gerek kalmadığına dikkat edin; sadece o anda ayarlanmış olan stratejiye işi devreder. Servis sınıfını hiç değiştirmeden kolayca yeni gönderim yöntemleri (drone teslimatı veya aynı gün teslimat gibi) ekleyebilirsiniz.

Kopya kağıdı

Strateji deseni (Strategy pattern), bir algoritma ailesi tanımlar, her birini kapsüller ve onları birbirinin yerine kullanılabilir hale getirir. Bu, bir nesnenin davranışının, kodunu değiştirmeden çalışma zamanında değiştirilmesine olanak tanır.

Desen üç bölümden oluşur:

  • Strateji arayüzü (Strategy interface): Algoritma metodunu bildiren soyut bir sınıf
  • Somut Stratejiler (Concrete Strategies): Farklı algoritma varyasyonlarını uygulayan sınıflar
  • Bağlam (Context): Bir stratejiyi kullanan ve bunlar arasında geçiş yapabilen bir sınıf
// Strategy interface
class PaymentStrategy {
public:
    virtual void pay(int amount) = 0;
    virtual ~PaymentStrategy() = default;
};

// Concrete strategies
class CreditCardPayment : public PaymentStrategy {
public:
    void pay(int amount) override {
        std::cout << "Paid " << amount << " via Credit Card\n";
    }
};

class PayPalPayment : public PaymentStrategy {
public:
    void pay(int amount) override {
        std::cout << "Paid " << amount << " via PayPal\n";
    }
};

// Context
class ShoppingCart {
    std::unique_ptr<PaymentStrategy> strategy;
public:
    void setPaymentMethod(std::unique_ptr<PaymentStrategy> s) {
        strategy = std::move(s);
    }
    
    void checkout(int total) {
        if (strategy) strategy->pay(total);
    }
};

Bağlam, hangi somut stratejiyi kullandığını bilmez; sadece arayüz metodunu çağırır. Stratejiler, bir ayarlayıcı (setter) metodu kullanılarak çalışma zamanında değiştirilebilir:

ShoppingCart cart;

cart.setPaymentMethod(std::make_unique<CreditCardPayment>());
cart.checkout(100);

cart.setPaymentMethod(std::make_unique<PayPalPayment>());
cart.checkout(50);

Ne zaman kullanılır: Belirli bir görev için birden fazla algoritmanız olduğunda ve bunlar arasında dinamik olarak geçiş yapmak istediğinizde veya davranışı seçmek için koşullu ifadelerden (if-else, switch) kaçınmak istediğinizde Strateji desenini kullanın.

Kendin dene

#include <iostream>
#include <string>
#include <iomanip>
#include <memory>
#include "ShippingStrategy.h"
#include "ShippingService.h"

int main() {
    double weight;
    std::string method;
    
    std::cin >> weight;
    std::cin >> method;
    
    // Çıktıyı 1 ondalık basamağa ayarla
    std::cout << std::fixed << std::setprecision(1);
    
    ShippingService service;
    
    // TODO: Giriş yöntemine göre ("ground", "air" veya "express"):
    // 1. Servis üzerinde uygun stratejiyi ayarla
    // 2. Maliyeti şu formatta hesapla ve yazdır: "[Method]: $[cost]"
    
    // TODO: Farklı bir stratejiye geç:
    // - Eğer giriş "air" ise, GroundShipping stratejisine geç
    // - Aksi takdirde, AirShipping stratejisine geç
    // Yeni maliyeti hesapla ve yazdır
    
    return 0;
}
quiz iconKendini test et

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