Memento Design Pattern
Selamlar;
Bugün benim de çokça kullandığım davranışsal yani behavioral tasarım kalıplarından memento üzerine konuşacağız. Amacı özellikle oyunlarda, yazı editör uygulamalarında yada adım adım işlem yapılan uygulamalarda en son yapılan işi geri almaktır. Bugünkü ugulamamızda biz ileri ve geri alma işlemlerini yani undo ve redo’yu birlikte kullanacağız. Mesela ben en son yazdığım puzzle oyununda bu yapıyı kullandım. Bu kalıp 3 yapıdan oluşur. Durumu korunmak istenen objenin tamamının yada bir kısmının başka bir yerde kopyasını saklayan(Memento). Tamamının veya bazı özelliklerinin kopyasının tutulacağı gerçek nesne aynı zamanda memento nesnesini oluşturan ve geri yüklenmesinden sorumlu kısım (Originator). Saklanacak olan memento nesnesinin referans değerlerini saklayan (Caretaker) dır.
Image Source: http://sourcemaking.com/files/sm/images/patterns/Mediator__1.gif
Örnek olarak bir Console Application yazacağız. Aşağıda görüldüğü gibi bir twitter uygulamamız var. Konu ve harf sayısı alanları bulunmaktadır.
1 2 3 4 5 |
class TwitterWordMemento { public string Topic { get; set; } public int LetterCount { get; set; } } |
Bu tweet’de harf sayısı arttığı zaman önceki durumu aşağıdaki nesnede saklanır. Görüldüğü gibi değişimden önceki son durum Stack nesnesinde saklanmaktadır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class TwitterWordTaker { Stack<TwitterWordMemento> Space = new Stack<TwitterWordMemento>(); public TwitterWordMemento World { get { return Space.Pop(); } set { Space.Push(value); } } } |
Son durum saklanırken bu son hali(TwitterWordMemento), TwitterWordTaker nesnesine Push() edilir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class TwitterWords { public string Topic { get; set; } public int LetterCount { get; set; } TwitterWordTaker WT; public TwitterWords() { WT = new TwitterWordTaker(); } public void Save() { WT.World= new TwitterWordMemento { Topic = this.Topic, LetterCount = this.LetterCount }; } } |
Var olan durumdan bir önceki duruma dönmek için aşağıda görüldüğü gibi Undo() methodu kullanılır. Bu sırada önceki kayıtlar çekilmeden önce TwitterWordTakerRedo nesnesine o anki değerler atanır. Böylece istendiğinde ileri doğru(redo) bir hareket yapldığında güncel datalar çekilip atanabilmektedir. TwitterWordTaker nesnesinden çekilen bir önceki duruma ait datalar geçerli güncel duruma ait olan Topic ve LetterCount propertyleri yerine atanır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
class TwitterWords { public string Topic { get; set; } public int LetterCount { get; set; } TwitterWordTaker WT; TwitterWordTakerRedo WTR; public TwitterWords() { WT = new TwitterWordTaker(); WTR= new TwitterWordTakerRedo(); } public void Undo() { WTR.World = new TwitterWordMemento { Topic = this.Topic, LetterCount = this.LetterCount }; TwitterWordMemento unduResult = WT.World; this.Topic = unduResult.Topic; this.LetterCount = unduResult.LetterCount; } } class TwitterWordTakerRedo { Stack<TwitterWordMemento> Space = new Stack<TwitterWordMemento>(); public TwitterWordMemento World { get { return Space.Pop(); } set { Space.Push(value); } } } |
Var olan durumdan bir sonraki duruma geçmek için aşağıda görüldüğü gibi Redo() methodu kullanılır. Bu sefer var olan güncel durum TwitterWordTaker nesnesine atanır. Amaç ileride Undo() yani geri alma işlemi yapılmak istendiğinde ilgili bir önceki dataya dönebilmektir. TwitterWordTakerRedo nesnesinden bir sonraki data çekilerek güncel Topic ve LetterCount değerleri yerine atanır. Böylece bir sonraki dataya geçilmiş olunur.
1 2 3 4 5 6 7 |
public void Redo() { WT.World = new TwitterWordMemento { Topic = this.Topic, LetterCount = this.LetterCount }; TwitterWordMemento redoResult = WTR.World; this.Topic = redoResult.Topic; this.LetterCount = redoResult.LetterCount; } |
Yukarıda bahsedilen Undo() ve Redo() methodları aşağıda görüldüğü gibi kullanılmıştır. MementoDesignPattern tweetinde ilk baştaki harf sayısı 50 dir. Daha sonra bu değer 60 ve daha sonrada 110 olarak artmıştır. Her Save() methodunda TwitterWordTaker’a data güncellenmeden önceki değerleri Push() edilmiştir. Böylece geriye dönük hareket edildiğinde yani Undo() methodunda bir önceki datalar çekilip atanabilmektedir. Daha sonra Undo işlemleri yapılmakta ve her Undo() işleminden sonra bir önceki kayıt çekilip yazdırılmaktadır. İlerleyen adımlarda Redo işlemleri yapılmakta ve her Redo() işleminde bir sonraki kayıt çekilerek güncel data olarak atanıp yazdırılmaktadır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
class Program { static void Main(string[] args) { TwitterWords MyTwitter = new TwitterWords { Topic = "MementoDesignPattern", LetterCount = 50 }; Console.WriteLine(MyTwitter.ToString()); MyTwitter.Save(); MyTwitter.LetterCount += 10; Console.WriteLine(MyTwitter.ToString()); MyTwitter.Save(); MyTwitter.LetterCount += 50; Console.WriteLine(MyTwitter.ToString()); Console.WriteLine(""); Console.WriteLine("Undo".PadLeft(20, '*')); MyTwitter.Undo(); Console.WriteLine(MyTwitter.ToString()); MyTwitter.Undo(); Console.WriteLine(MyTwitter.ToString()); Console.WriteLine(""); Console.WriteLine("Redo".PadLeft(20,'*')); MyTwitter.Redo(); Console.WriteLine(MyTwitter.ToString()); MyTwitter.Redo(); Console.WriteLine(MyTwitter.ToString()); Console.ReadLine(); } } |
Örnek Ekran Çıktısı:
Tüm Kodlar:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MementoDesignPattern { class TwitterWords { public string Topic { get; set; } public int LetterCount { get; set; } TwitterWordTaker WT; TwitterWordTakerRedo WTR; public TwitterWords() { WT = new TwitterWordTaker(); WTR= new TwitterWordTakerRedo(); } public void Save() { WT.World= new TwitterWordMemento { Topic = this.Topic, LetterCount = this.LetterCount }; } public void Undo() { WTR.World = new TwitterWordMemento { Topic = this.Topic, LetterCount = this.LetterCount }; TwitterWordMemento unduResult = WT.World; this.Topic = unduResult.Topic; this.LetterCount = unduResult.LetterCount; } public void Redo() { WT.World = new TwitterWordMemento { Topic = this.Topic, LetterCount = this.LetterCount }; TwitterWordMemento redoResult = WTR.World; this.Topic = redoResult.Topic; this.LetterCount = redoResult.LetterCount; } public override string ToString() { return String.Format("{0} twittinde {1} harf var", this.Topic, this.LetterCount); } } class TwitterWordTaker { Stack<TwitterWordMemento> Space = new Stack<TwitterWordMemento>(); public TwitterWordMemento World { get { return Space.Pop(); } set { Space.Push(value); } } } class TwitterWordTakerRedo { Stack<TwitterWordMemento> Space = new Stack<TwitterWordMemento>(); public TwitterWordMemento World { get { return Space.Pop(); } set { Space.Push(value); } } } class TwitterWordMemento { public string Topic { get; set; } public int LetterCount { get; set; } } class Program { static void Main(string[] args) { TwitterWords MyTwitter = new TwitterWords { Topic = "MementoDesignPattern", LetterCount = 50 }; Console.WriteLine(MyTwitter.ToString()); MyTwitter.Save(); MyTwitter.LetterCount += 10; Console.WriteLine(MyTwitter.ToString()); MyTwitter.Save(); MyTwitter.LetterCount += 50; Console.WriteLine(MyTwitter.ToString()); Console.WriteLine(""); Console.WriteLine("Undo".PadLeft(20, '*')); MyTwitter.Undo(); Console.WriteLine(MyTwitter.ToString()); MyTwitter.Undo(); Console.WriteLine(MyTwitter.ToString()); Console.WriteLine(""); Console.WriteLine("Redo".PadLeft(20,'*')); MyTwitter.Redo(); Console.WriteLine(MyTwitter.ToString()); MyTwitter.Redo(); Console.WriteLine(MyTwitter.ToString()); Console.ReadLine(); } } } |
Yukarıda görüldüğü gibi design patternlar işimizi çok kolaylaştırmaktadır. Dünyayı birdaha keşfetmeye gerek yoktur. Özellikle adım adım işleyen yapılarda memento design pattern çok kullanışlıdır. Güncel hayatımız da nerde ise heryerde kullandığımız undo() ve redo() işlemleri için bu design pattern’ın önemi kaçınılmazdır.
Bir sonraki makalede görüşmek üzere hoşçakalın.
Source: https://www.dofactory.com/net/memento-design-pattern, https://en.wikipedia.org/wiki/Memento_pattern
Elinize saglik.
Cok guzel olmus hocam. Zevkle okudum. Design pattern ayni satranc gibi.
Iyi hafta sonlari..
Ne yaptığını bilmek insana değer katıyor. elinize sağlık
Guzel yorumun icin Tesekkurler Muhammed.