Behavioral — Davranışsal Desenler

Memento

Hatıra (Anlık Görüntü) Deseni

Amaç: Bir nesnenin iç durumunu, kapsüllemeyi bozmadan kaydetmek ve daha sonra nesneyi bu duruma geri döndürebilmek.

1

Basit Anlatım

Teknik terim yok — günlük hayattan bir benzetme

Bir video oyunu oynuyorsunuz ve zorlu bir bölüme girmeden önce oyunu kaydediyorsunuz. Bölümde kaybederseniz kayıt noktasına geri dönersiniz: canınız, envanteriniz, konumunuz kaydettiğiniz andaki gibi olur.

Kayıt dosyasının içinde ne olduğunu bilmezsiniz, açıp değiştiremezsiniz de. Sadece "şu kaydı yükle" dersiniz. İçeriği yalnızca oyun anlayabilir.

Telefon yedeği

Telefonunuzu güncellemeden önce yedek alırsınız. Bir sorun çıkarsa yedekten geri dönersiniz. Yedeği saklayan bulut servisi, içinde hangi fotoğrafların veya mesajların olduğuyla ilgilenmez; sadece saklar.

Memento şunu der: "Bu anın bir fotoğrafını çek ve sakla; gerekirse tam bu ana geri dönebilelim."
2

Teknik Anlamı

Bir yazılımcının bakış açısıyla

Memento, bir nesnenin durumunun anlık görüntüsünü (snapshot) ayrı bir nesnede saklar. Kritik nokta: durumu yalnızca onu üreten nesne okuyabilir; saklayan taraf içeriğe erişemez. Böylece encapsulation korunur.

Katılımcılar

Command deseniyle ilişkisi

Undo'nun iki yolu vardır: Command her işlemin tersini bilir; Memento ise önceki durumun tamamını saklar. Tersini hesaplamanın zor olduğu durumlarda Memento daha basittir, ama daha fazla bellek kullanır.

Kullanım alanları

Avantajlar

Dezavantajlar

3

C# ile Uygulama

Adım adım, çalışan kod örneği

Senaryo: Bir oyun karakterinin durumunu kayıt noktalarında saklamak ve gerektiğinde geri yüklemek.

Adım 1 Memento: değiştirilemez anlık görüntü

C# record tipi immutable olduğu için memento için idealdir. Liste de kopyalanarak saklanır, böylece sonradan değişiklikler kaydı etkilemez.

public sealed record CharacterSnapshot(
    int Level, int Health, string Location, IReadOnlyList<string> Inventory,
    DateTime SavedAt);

Adım 2 Originator: durumu kaydedilen nesne

Alanlar private. Dışarıdan doğrudan değiştirilemez; sadece kendi snapshot'ını üretip ondan geri yüklenebilir.

public class GameCharacter
{
    private int _level = 1;
    private int _health = 100;
    private string _location = "Köy";
    private List<string> _inventory = new() { "Tahta Kılıç" };

    public void Play(string location, int damage, string? loot = null)
    {
        _location = location;
        _health -= damage;
        if (loot is not null) _inventory.Add(loot);
        if (damage == 0) _level++;
    }

    public CharacterSnapshot Save() =>
        new(_level, _health, _location, _inventory.ToList(), DateTime.Now);

    public void Restore(CharacterSnapshot s)
    {
        _level = s.Level;
        _health = s.Health;
        _location = s.Location;
        _inventory = s.Inventory.ToList();
    }

    public override string ToString() =>
        $"Seviye {_level} | Can {_health,3} | {_location,-12} | {string.Join(", ", _inventory)}";
}

Adım 3 Caretaker: kayıtları saklar ama içine bakmaz

public class SaveManager
{
    private readonly Dictionary<string, CharacterSnapshot> _slots = new();

    public void Save(string slot, GameCharacter character)
    {
        _slots[slot] = character.Save();
        Console.WriteLine($"  [Kaydedildi: {slot}]");
    }

    public void Load(string slot, GameCharacter character)
    {
        character.Restore(_slots[slot]);
        Console.WriteLine($"  [Yüklendi: {slot}]");
    }
}

Adım 4 Kullanım

Tüm parçaları bir araya getiren istemci (client) kodu:

var hero = new GameCharacter();
var saves = new SaveManager();
Console.WriteLine(hero);

hero.Play("Orman", damage: 0, loot: "Demir Kalkan");
Console.WriteLine(hero);
saves.Save("Kayıt-1", hero);

hero.Play("Ejderha İni", damage: 80, loot: "Ejderha Pulu");
Console.WriteLine(hero);
hero.Play("Ejderha İni", damage: 45);
Console.WriteLine(hero + "  <- karakter öldü!");

saves.Load("Kayıt-1", hero);
Console.WriteLine(hero);

Konsol çıktısı

Seviye 1 | Can 100 | Köy          | Tahta Kılıç
Seviye 2 | Can 100 | Orman        | Tahta Kılıç, Demir Kalkan
  [Kaydedildi: Kayıt-1]
Seviye 2 | Can  20 | Ejderha İni  | Tahta Kılıç, Demir Kalkan, Ejderha Pulu
Seviye 2 | Can -25 | Ejderha İni  | Tahta Kılıç, Demir Kalkan, Ejderha Pulu  <- karakter öldü!
  [Yüklendi: Kayıt-1]
Seviye 2 | Can 100 | Orman        | Tahta Kılıç, Demir Kalkan

Kodda dikkat edilmesi gerekenler

Özetle: Memento, bir nesnenin durumunu kapsüllemeyi bozmadan dışarıda saklar ve o duruma geri dönmeyi sağlar.
← Ana sayfaya dön