iToverDose/Yazılım· 28 MAYIS 2026 · 08:02

Modern C++ ile Durum Yönetimini Güçlendirme: Kodlarınızı Temiz ve Ölçeklenebilir Yapın

C++ projelerinizde durum yönetimini nasıl daha anlaşılır ve bakımı kolay hale getirebilirsiniz? Fonksiyonel programlama prensiplerini kullanarak domain modelinizi nasıl korurken, iç durumları etkili bir şekilde kapsayacağınızı öğrenin. Pratik örneklerle modüler tasarımın gücünü keşfedin.

DEV Community4 dk okuma0 Yorumlar

Modern C++ projelerinde durum yönetimi, kodun okunabilirliği ve bakım kolaylığı açısından kritik bir rol oynar. Ancak, durumun nasıl tanımlandığı ve yönetildiği, sistemin karmaşıklığını belirleyen en önemli faktörlerden biridir. Özellikle domain modeline ait olmayan iç durumların (internal state) nasıl ele alınacağı, geliştiricilerin sıkça karşılaştığı bir zorluktur. Bu durumlar, modüllerin kendi işlevlerini yerine getirmek için ihtiyaç duyduğu ancak domain modeline ait olmayan bilgileri temsil eder. Örneğin, bir metin çözümleyicinin okuma konumunu takip etmesi veya önbelleğin önceki sorgularını hatırlaması gibi.

Bu iç durumları domain modeline dahil etmek, hem kodun anlaşılabilirliğini azaltır hem de beklenmedik bağımlılıkların oluşmasına yol açar. Nesne yönelimli yaklaşımlarda, böyle durumlar genellikle sınıf arayüzleri arkasına gizlenir. Ancak fonksiyonel programlama prensiplerini benimseyen geliştiriciler için bu durum daha karmaşık hale gelir: durumun gizli mutasyona uğramadan nasıl kapsüllenebileceği sorusu ortaya çıkar.

Bu makalede, durumlu fonksiyonel modüller olarak adlandırılan bir yaklaşımla, iç durumların nasıl etkili bir şekilde kapsüllenebileceği ve domain modelinin temiz kalmasının sağlanabileceği inceleniyor. Özellikle, iç durum modülleri (internal state modules) adı verilen bir varyant üzerinde durulacak ve bu modüllerin nasıl tasarlanacağına dair pratik örnekler sunulacak.

İç Durum Modüllerinin Temelleri

Bir durumlu fonksiyonel modül, bir modülün durumunu ve bu durumun nasıl evrim geçirdiğini tanımlayan saf fonksiyonlarla birlikte gruplandırır. Bu yaklaşımda, modülün durumunun domain modeline ait olmadığı varsayılır; bunun yerine, modülün kendi iç mantığını uygulamak için ihtiyaç duyduğu bir araçtır. Bu durum, modülün içinde yer alır ve dışarıdan erişilemez.

Önceki makalelerde, durumun nasıl açık hale getirildiği ve kabuk (shell) ile çekirdek (core) arasında nasıl geçirildiği ele alınmıştı. Ancak, iç durumlar söz konusu olduğunda, durumun geçiş şekli değişmez — sadece durumun nasıl ele alındığı önem kazanır. Artık durum, domain modeline ait bir bilgi olarak değil, modülün yerel uygulama detayına ait bir unsur olarak işlenir.

Bu yaklaşımın en büyük avantajı, modülün kapsüllenme sınırını (encapsulation boundary) güçlendirmesidir. Dışarıdan kodlar, modülün durumunu depolayabilir veya geçirebilir, ancak bu durumun iç yapısına bağımlı olamaz. Böylece, domain modeli temiz kalırken, modülün iç işleyişi gizlenmiş olur.

Gerçek Dünyadan Bir Örnek: FunkySnakes Projesi

Bu yaklaşımı somutlaştırmak için, FunkySnakes adlı bir proje üzerinden ilerleyelim. Bu proje, yılan oyununun klavyeden kontrol edilen bir versiyonunu içeriyor. Oyun döngüsü ve tuş olayları asenkron olarak çalışırken, her yılanın hareket yönü, önceki döngüden bu yana alınan yeni tuş olaylarına göre güncellenir. Bu yön güncelleme mantığı, direction_command_filter adlı bir modülde uygulanıyor.

Bu modülün temel görevi, tuş olaylarını filtreleyerek hangi yön komutunun geçerli olduğunu belirlemektir. Ancak, bu işlevsellik için modülün kendi iç durumuna ihtiyacı vardır: her oyuncu için bir yön komutu kuyruğu. Bu kuyruk, oyun durumunun bir parçası değildir — sadece filtreleme mantığının uygulanması için gereklidir.

namespace direction_command_filter {
    struct State {
        using PerPlayerDirectionQueue = std::map<PlayerId, std::deque<Direction>>;
        PerPlayerDirectionQueue queues;
    };
}

Bu modülün arayüzünde iki ana fonksiyon bulunur:

  • tryAdd: Yeni bir yön komutunu filtreye ekler ve durumunu günceller.
  • tryConsumeNext: Filtrelenmiş bir sonraki yön komutunu alır ve durumunu günceller.
namespace direction_command_filter {
    State tryAdd(State state, const PerPlayerSnakes& snakes, const DirectionCommand& cmd);
    std::tuple<State, PerPlayerDirection> tryConsumeNext(State state);
}

Oyun durumunda hem yılanlar (PerPlayerSnakes) hem de modülün iç durumunun (direction_command_filter::State) birlikte yer aldığını görebiliriz:

namespace shell {
    struct GameState {
        ...
        PerPlayerSnakes snakes;
        direction_command_filter::State direction_command_filter_state;
    };

    class GameEngineActor : public Actor<GameEngineActor> {
        ...
        GameState game_state_;
    };
}

GameEngineActor, modül durumunu fonksiyonlara geçirerek günceller:

state_.direction_command_filter_state = direction_command_filter::tryAdd(
    state_.direction_command_filter_state,
    state_.snakes,
    new_command
);

auto [new_state, direction] = direction_command_filter::tryConsumeNext(
    state_.direction_command_filter_state
);

state_.direction_command_filter_state = new_state;

Modül Tasarımının Genelleştirilmesi

Yukarıdaki örnek, spesifik bir duruma ait olsa da, bu yaklaşımın arkasındaki genel desen oldukça basittir. Bir modül, kendi durumunu ve bu durumu değiştiren saf fonksiyonları tanımlar. Her fonksiyon, mevcut durumu alır ve güncellenmiş durumu döndürür.

namespace module {
    struct State { ... };
    State operation(State state, Input input);
}

Burada, module ad alanı, hem durum tipini hem de bu durumun nasıl evrim geçirdiğini tanımlayan fonksiyonları gruplandırır. Bu sayede modül, kendi içinde kapalı hale gelir: durumunu oluşturabilir, fonksiyonlarını çağırabilir ve geriye dönen durumu kontrol ederek modülün davranışını test edebilirsiniz.

Ayrıca, bu yapı, sınıf üye fonksiyonlarına benzer şekilde, fonksiyonlara görünür bir kapsam sağlar. İstemci kodlar, module::operation şeklinde fonksiyonları çağırabilir ve module::State şeklinde durumu depolayabilir. Bu da modül sınırının çağrı noktalarında görünür kalmasını sağlar. Dahası, durum tipinin basitçe State olarak adlandırılması, anlamın ad alanından (namespace) geldiğini gösterir.

Kabuk tarafında bu yapı oldukça basittir:

namespace shell {
    module::State module_state;
    module_state = module::operation(module_state, input);
}

Kabuk, durumu çağrılar arasında saklar ve modül fonksiyonlarına geçirir. Bu şekilde, durum, modülün yerel uygulama detayı olarak ele alınır — saklanır, geçirilir ve güncellenir, ancak asla doğrudan erişilmez.

Sonuç: Temiz ve Ölçeklenebilir Kod için Pratik Yaklaşım

Modern C++ projelerinde durum yönetimini geliştirmek, hem kodun okunabilirliğini artırır hem de bakım maliyetlerini düşürür. İç durumları domain modelinden ayırmak ve onları modüler bir şekilde kapsüllenmek, sistemin karmaşıklığını azaltır ve gelecekteki değişikliklerin daha kolay uygulanmasını sağlar.

Bu yaklaşıma geçiş, başlangıçta biraz alışma gerektirse de, uzun vadede daha temiz, daha anlaşılır ve daha ölçeklenebilir kodlar yazmanıza olanak tanır. Fonksiyonel programlama prensiplerini C++ projelerinizde uygularken, durum yönetimini de bu prensiplere uygun şekilde ele almayı unutmayın — böylece kodunuz hem güçlü hem de bakımı kolay hale gelecektir.

Yapay zeka özeti

C++ projelerinizde durum yönetimini nasıl daha anlaşılır ve bakımı kolay hale getirebilirsiniz? Fonksiyonel programlama prensiplerini kullanarak domain modelinizi korurken iç durumları nasıl kapsayacağınızı öğrenin.

Yorumlar

00
YORUM BIRAK
ID #41T98B

0 / 1200 KARAKTER

İnsan doğrulaması

4 + 4 = ?

Editör onayı sonrası yayına girer

Moderasyon · Spam koruması aktif

Henüz onaylı yorum yok. İlk yorumu sen bırak.