The Chain of Responsibility Pattern
Selamlar,
The Chain of Responsibility Pattern, sorumluluk zinciri adı ile de bilinen Behavioral yani nesneler arasındaki bağlantıyı ayarlayan design patterin kategorisindeki bir tasarım desenidir. Güncel hayatımızda çokça karşımıza çıkmaktadır.
CoR bir amaca yönelik bir dizi işlemi gerçekleştiren nesnelerin birbirlerinden bağımsız bir şekilde çalışmasını ve her bir nesnenin sadece kendisine tanımlı işleri yapmasını sağlayan bir design patterindır. Sorumluluk zinciri ismi de burdan gelmektedir. Bu nesneler arasındaki tek bağlantı mesaj(request) yapısıdır. Bütün nesneler bu mesaj yapısını kullanarak işlerini gerçekleştirir. Bu nesneler, çalışma yapısı olarak aynı işi yapmalarına rağmen birbirlerinden haberdar olmamaları loosly coupled (gevşek bağlı) nesneler olarak anılmalarına sebep olmaktadır. CoR deseni daha çok bolca if-else blokları geçen yerlerde kullanılmalıdır. Yoksa belli bir süre sonra kodlar kontrolden çıkabilir.
Bügünkü örneğimizde bir dergideki yazının onaylanmasını konu alacağız. Sözde yazı 1000 karakterden az ise Editor (A). 1000 ile 2000 karakter arasında ise Executive Editor(B). Eğer 2000 karakterden fazla ise Managing Editor(C) tarafından onaylanması gerekmektedir.
Öncelikle onaylanacak document’ı tanımlayalım. Aşağıda görüldüğü gibi onaylanacak yazı TextContent bir de yazı Id’si bulunmktadır.
Documnet.cs:
1 2 3 4 5 6 7 8 |
namespace ChainOfCommandPatternDemo { public class Document { public string TextContent { get; set; } public int Id { get; set; } } } |
Şimdi ReviewResult denen yazının onaylanması sonucu dönen class’ı tanımlayalım. Aşağıda görüldüğü gibi onaylanma sonucu Approved ve onaylayan kişinin title’ının belirtildiği Reviewer propertyleri tanımlanmıştır.
ReviewResult.cs:
1 2 3 4 5 6 7 8 |
namespace ChainOfCommandPatternDemo { public class ReviewResult { public bool Approved { get; set; } public string Reviewer { get; set; } } } |
Tüm editor tiplerinin yaratıldığı IEditor interface class’ı aşağıda tanımlanmıştır. Aşağıda görüldüğü gibi tüm editor’lerin işi ortaktır. Hepsi yazıları onaylarlar. Yani ReviewDocument methoduna, onaylanacak document parametresini alarak ReviewResult class’ı sonucunu döndürürler.
IEditor.cs:
1 2 3 4 5 6 7 |
namespace ChainOfCommandPatternDemo { public interface IEditor { ReviewResult ReviewDocument(Document document); } } |
Aşağıda Editor classını görmekteyiz. Constractor’da tanımlanan Successor işte bahsettiğimiz relation’ın ta kendisidir.Yani bağlı olduğu üstüdür. ReviewDocument yani onaylama işleminde eğer onaylama sorumluluğu kendisinde değilse bir üstüne ilgili document’i havale eder. Aşağıda görüldüğü gibi gelen document‘ı TextContent‘i 1000 karakterden fazla ise bir üstü olan Executive Editor’e onaylanması için göndermektedir. Böylece aynı işi yapan birbirinden habersiz 2 class Successor property’si ile kendi aralarındaki relation’ı kurmuş olurlar . Eğer değilse kendi onaylama işlemini yapıp ReviewResult class’ını döndürmektedir. Eğer gelen document’ın TextContent’i 600’den küçük ise rejected olmaktadır. Title yani Reviewer‘ı Editor‘dür.
Editor.cs:
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 |
namespace ChainOfCommandPatternDemo { public class Editor : IEditor { public IEditor Successor { get; private set; } public Editor(IEditor successor) { Successor = successor; } public ReviewResult ReviewDocument(Document document) { ReviewResult result = new ReviewResult() { Reviewer = "Editor" }; if (!string.IsNullOrWhiteSpace(document.TextContent)) { if (document.TextContent.Length > 1000) return Successor.ReviewDocument(document); if (document.TextContent.Length >= 600) result.Approved = true; } return result; } } } |
Aşağıda görüldüğü gibi yine bağlı olduğu üst editor Successor ile constractor’da tanımlanmıştır. Onaylama yani ReviewDocument işleminde ilgili document‘ı 2000 karakterden fazla ise bir üst’ü olan Managing Editor‘e göndermektedir. Eğer değilse kendi onaylama işlemini yapıp ReviewResult class’ını döndürmektedir. Gelen document‘ın TextContent‘i 1500’den küçük ise rejected olmaktadır. Title yani Reviewer‘ı Executive Editor‘dür.
ExecutiveEditor.cs :
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 |
namespace ChainOfCommandPatternDemo { public class ExecutiveEditor : IEditor { public IEditor Successor { get; private set; } public ExecutiveEditor(IEditor successor) { Successor = successor; } public ReviewResult ReviewDocument(Document document) { ReviewResult result = new ReviewResult() { Reviewer = "Executive Editor" }; if (!string.IsNullOrWhiteSpace(document.TextContent)) { if (document.TextContent.Length > 2000) return Successor.ReviewDocument(document); if (document.TextContent.Length <= 1500) result.Approved = false; if (document.TextContent.Length > 1500) result.Approved = true; } return result; } } } |
Aşağıda görüldüğü gibi editörlerin en üst class’ı ManagingEditor‘dür. Çünkü kendisinin Successor property’si yani üsttü yoktur. Eğer bu editöre bir document geliyor ise onaylanması için TextContent‘i 2000’den fazla olmalıdır. Title yani Reviewer‘ı Managing Editor‘dür.
ManagingEditor.cs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ChainOfCommandPatternDemo { class ManagingEditor : IEditor { public ReviewResult ReviewDocument(Document document) { ReviewResult result = new ReviewResult() { Reviewer = "Managing Editor" }; result.Approved = !string.IsNullOrWhiteSpace(document.TextContent) && document.TextContent.Length > 2000; return result; } } } |
Aşağıda görüldüğü gibi 4 farklı document onaylanmak için gönderilmiştir. Herbiri için onaylama işlemi yapıcak editor aşağıdaki gibi iç içe tanımlı IEditor class’ları ile tanımlanmıştır. Her editor’ün constractor‘unda bir üst editor‘ü tanımlanmıştır.
IEditor editor = new Editor(new ExecutiveEditor(new ManagingEditor()));
TextContent’i 1000 den küçük olan ilk 2 document Editor tarafından onaylanmıştır. Ama ilk document’ın TextContent’i 600’den küçük olduğu için rejected olmuştur. TextContent’i 1500 olan 3. document ExecutiveEditor tarafından rejected olmuştur. Son document 2500 karakterdir. Ve ManagingEditor tarafından onaylanmıştır.
Program.cs:
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 |
using System; using System.Collections.Generic; namespace ChainOfCommandPatternDemo { class Program { static void Main(string[] args) { List<Document> documents = new List<Document>() { new Document() { Id = 1, TextContent = new string('*', 500)}, new Document() { Id = 2, TextContent = new string('*', 850)}, new Document() { Id = 3, TextContent = new string('*', 1500)}, new Document() { Id = 4, TextContent = new string('*', 2500) } }; IEditor editor = new Editor(new ExecutiveEditor(new ManagingEditor())); documents.ForEach(x => { var result = editor.ReviewDocument(x); Console.WriteLine(result.Approved ? "Document {0} approved by {1}" : "Document {0} rejected by {1}", x.Id, result.Reviewer); }); Console.Read(); } } } |
Sonuç Ekranı:
Böylece belli bir amaca yönelik bir dizi işlemin gerçekleştiği durumlarda birbirinden bağımsız çalışan nesnelerin, mesaj(request) ile nasıl birbirlerine bağlandıklarını ve sorumluluk zincirinde bireysel olarak nasıl çalıştıklarını gördük. Böylece belli bir sorumluluk silsilesinde Chain of Responsibility Pattern‘ın nekadar faydalı olduğunu birdaha görmüş olduk.
Yeni bir makalede buluşmak dileği ile hoşçakalın.
Source Code:http://www.borakasmer.com/projects/ChainOfCommandPatternDemo.rar
Source: https://www.dofactory.com/net/chain-of-responsibility-design-pattern
Son Yorumlar