Emre Kovancı
YazarEmre Kovancı
4 dakika okuma süresi
Nis 16, 2019

Open-Closed Princible(C++)


Solid ilkerinden olan OCP'nin C++ dili üzerinde örnek basit bir kullanımı

Merhabalar, bu yazımızda SOLID ilkelerinden olan Open-Closed princible(OCP) ilkesine göz atacağız.

"Değişmeyen tek şey değişimin kendisidir."
Heraklitos

Uygulama geliştirme süreci boyunca ve daha sonra müşterilerden gelecek yeni istekler doğrultusunda, uygulamamıza yeni özellikler eklememiz kaçınılmaz bir durum, hali hazırda olan kodlarımıza bakım ve eklemeler yapmamız bu işin doğası.

"Ve şöyle dedi usta programcı: İyi yazılmış bir program kendi içinde bir cennettir, kötu bir program ise cehennemin ta kendisi."

Şimdi bu alıntıda nerden çıktı dediğinizi duyar gibiyim.

OCP ilkesi kısaca şöyle diyor “Yazılım varlıkları (sınıflar, modüller, fonksiyonlar vs.) gelişime AÇIK, kod değişimine KAPALI olmalıdır.”

* Aşağıdaki kod bloğuna bir göz atalım. Ürün sınıfımızın belirli özellikleri mevcut(isim, agirlik vs.) ve bu özelliklere göre filtreleme yapmak istiyoruz. Örneğin belirli ağırlıktaki ürünleri bilmeliyim ya da mavi renkteki ürünleri veya boyut olarak sadece küçük ürünleri filtremelek isteyebilirim. Nasıl bir yöntem izleyemeliyim? Her özellik için bir filtreleme içeren bir fonksiyon yapabilirim, bu ihtiyacımı bu aşama da karşılayacak gibi duruyor. (1)


* Fakat bir dakika bir sorunum var. 2 farklı filtre kullanmak istediğimde ne olacak? Yani hem küçük hem mavi olan ürünleri bulmak istiyorum. Hemen bir fonksiyon daha yazıp bu fonksiyonda boyut ve renk filtrelemesi uygulayabilirim. Yapmam gereken ekstradan bir if sorgusu eklemek bu kadar basit.(2)

* Doğru yolda olduğunuzu düşünüyorsanız yanılıyorsunuz. Peki ürünlerin onlarca farklı özelliği olsaydı ne olacaktı? Ve bu özelliklerden seçeceğim 10 tanesine göre filtrelrme yapmam istense yeni bir fonksion oluşturup, 10 farklı if koşulu ekleyip bütün filtreler için doğrulama yapmam gerekecek, bi saniye işler çığrından çıkıyor.

* UrunFiltreleme yapisini kullanan onlarca dosya olduğunu düşünün, filtreleme ölçütümüz değiştiğinde bu yapı içindeki kodlarda değişiklik yapmamız gerekecek ve bu yapıyı kullanan bütün dosyaların yeniden derlenmesi gerekecek.

OCP prensibi tam bu noktada devreye giriyor. Oluşturacağımız daha iyi bir mekanizmayla(specification pattern olarak'ta geçiyor bu) kodumuzu genişletilebilir hale getireceğiz. (3)

#include

#include
#include

enum class Agirlik { Hafif, EhCokAgirDegil, Agir };
enum class Boyut { Kucuk, Orta, Buyuk };
enum class Renk { Kirmizi, Yesil, Mavi, Beyaz };

struct Urun
{
    std::string isim;
    Agirlik agirlik;
    Renk renk;
    Boyut boyut;
};

// (1)

struct UrunFiltreleme
{
    using Urunler = std::vector;

    static Urunler renkFiltresi(Urunler urunler, Renk renk)
    {
        Urunler filtrelenmis_urunler;

        for (const auto& urun : urunler)
            if (urun->renk == renk)
                filtrelenmis_urunler.push_back(urun);

        return filtrelenmis_urunler;
    }

    static Urunler boyutFiltresi(Urunler urunler, Boyut boyut)
    {
        Urunler filtrelenmis_urunler;

        for (const auto& urun : urunler)
            if (urun->boyut == boyut)
                filtrelenmis_urunler.push_back(urun);

        return filtrelenmis_urunler;
    }

// (2)

    static Urunler agirlikVeRenkFiltresi(Urunler urunler, Agirlik agirlik, Renk renk)
    {
        Urunler filtrelenmis_urunler;

        for (const auto& urun : urunler)
            if (urun->agirlik == agirlik && urun->renk == renk)
                filtrelenmis_urunler.push_back(urun);

        return filtrelenmis_urunler;
    }
};

// GELİŞMİŞ YÖNTEM(3): Bu mekanizma sayesinde artık yeni bir filtre oluşturmak istediğimizde tek yapmamız gereken Sartname arayüzünden kalıtım alıp yeni filtre sorgusunu ayarlamamız.

// Daha sonra eklenecek olan Filtrelere arayüz olan sınıf.
template
struct Sartname
{
    virtual bool uygun_mu(T* urun) = 0;
};

template
struct Filtreci
{
    virtual std::vector filtre(std::vector urunler, Sartname& sart) = 0;
};

struct DahaIyiBirFiltreleyici : public Filtreci
{
    using Urunler = std::vector;

    virtual Urunler filtre(std::vector urunler, Sartname& sart) override
    {
        Urunler filtrelenmis_urunler;

        for (const auto& urun : urunler)
            if (sart.uygun_mu(urun))
                filtrelenmis_urunler.push_back(urun);
        return filtrelenmis_urunler;
    }
};

struct AgirlikFiltresi : public Sartname
{
    Agirlik agirlik;

    explicit AgirlikFiltresi(const Agirlik agirlik) : agirlik{agirlik}
    { }

    virtual bool uygun_mu(Urun* urun) override
    {
        return urun->agirlik == agirlik;
    }
};

struct BoyutFiltresi : public Sartname
{
    Boyut boyut;

    explicit BoyutFiltresi(const Boyut boyut) : boyut{boyut}
    { }

    virtual bool uygun_mu(Urun* urun) override
    {
        return urun->boyut == boyut;
    }
};

struct RenkFiltresi : public Sartname
{
    Renk renk;

    explicit RenkFiltresi(const Renk renk) : renk{renk}
    { }

    virtual bool uygun_mu(Urun* urun) override
    {
        return urun->renk == renk;
    }
};

// iki farklı filtreyi bir arama getirip yeni bir filtre oluşturmak istediğimizde ise yapmamız gereken Sartname arayüzünden kalıtım alıp kuralları belirlemek

template
struct VeSartnamesi : public Sartname
{
    Sartname& birinci;
    Sartname& ikinci;

    VeSartnamesi(Sartname& birinci, Sartname& ikinci) : birinci{birinci}, ikinci{ikinci}
    { }

    virtual bool uygun_mu(T* urun) override
    {
        return birinci.uygun_mu(urun) && ikinci.uygun_mu(urun);
    }
};

int main()
{
    Urun kalem {"Kalem", Agirlik::Hafif, Renk::Beyaz, Boyut::Kucuk};
    Urun elma {"Elma", Agirlik::Hafif, Renk::Yesil, Boyut::Kucuk};
    Urun bot {"Bot", Agirlik::EhCokAgirDegil, Renk::Mavi, Boyut::Orta};
    Urun masa{"Masa", Agirlik::EhCokAgirDegil, Renk::Yesil, Boyut::Orta};

    std::vector butun_urunler{&kalem, &elma, &bot, &masa};

    DahaIyiBirFiltreleyici iyi_filtreleyici;

    RenkFiltresi renk_filtresi{Renk::Yesil};

    auto yesil_renkli_seyler = iyi_filtreleyici.filtre(butun_urunler, renk_filtresi);
    for (const auto& urun : yesil_renkli_seyler)
        std::cout << urun->isim << " yesildir\n";

    BoyutFiltresi boyut_filtresi{Boyut::Orta};
    VeSartnamesi yesil_ve_orta_filtre{renk_filtresi, boyut_filtresi};

    auto yesil_ve_orta_seyler = iyi_filtreleyici.filtre(butun_urunler, yesil_ve_orta_filtre);

    for (const auto& urun : yesil_ve_orta_seyler)
        std::cout << urun->isim << " hem orta boyutlu hem yesildir\n";

    std::cin.get();

}

Yazı OCP prensibinin basit bir örnek üzerinde kullanımını göstermek için yazılmıştır. Kodları derlemek için C++ derleyicinizde C++11 standartı aktif değilse aktif hale getirmeniz gerekiyor.

Teşekkürler.

Bunlar İlginizi Çekebilir