Skip to content

Chain of Responsibility Pattern

Chain of Responsibility (Sorumluluk Zinciri) tasarım deseni, bir request nesnesinin bir dizi handler nesnesi tarafından işlenmesi için kullanılır. İsteğin bir işlemci tarafından işlenememesi durumunda, isteği diğer işlemcilere yönlendirerek işlemin gerçekleştirilmesini sağlar.

Temel amaç bir dizi nesne arasında bir zincir oluşturmaktır. Request nesnesi, zincirin başındaki bir handler nesnesine gönderilir ve handler nesnesi, requesti işleyebileceği durumda işlemini gerçekleştirir ve sonuç nesnesini döndürür. Handler, requesti işleyemezse request, zincirdeki bir sonraki handler nesnesine gönderilir ve işlem bu şekilde devam eder.

Chain of responsibility sistemdeki nesnelerin birbirinden bağımsız olmasını sağlayarak kodun yeniden kullanılabilirliğini artırır. Ayrıca, requestlerin işlenme sırasının dinamik olarak değiştirilebilmesine olanak tanıdığından esnekliği artırır.

https://www.dofactory.com/img/diagrams/net/chain.png

UML diagramınındaki elemanları kısaca şu şekilde özetleyebiliriz:

Handler: İşlemci nesnelerinin soyut sınıfını temsil eder.

Concrete Handler: İşlemci sınıflarının gerçek uygulamalarını temsil eder.

Request: İşlenecek isteği temsil eder.

Client: İstekleri oluşturan ve zincirin başlangıcını belirleyen nesnelerdir.

Bir senaryo üzerinden giderek örnek kodumuzu yazalım, senayomuz şöyle olsun; Bir ATM den farklı birimlerde para çekmek istiyor olalım atmnin vereceği para birimi tamemen girilen tutara bağlı bir şekilde değişiyor ve tutara göre farklı değerlerdeki uygun birimleri veriyor olsun, temel amaç ise birim değerlerini en uygun/verimli şekilde dağıtarak en az sayıda banknotu vermek.

from abc import ABC, abstractmethod

# Abstract Handler
class Handler(ABC):
    @abstractmethod
    def handle(self, amount):
        pass

# Base Currency Handler
class Currency(Handler):
    def __init__(self, name, next_currency=None):
        self.name = name
        self.next_currency = next_currency

    def handle(self, amount):
        if amount >= self.value:
            count = amount // self.value
            remainder = amount % self.value
            print(f"{count} {self.name}(s) of {self.value} {self.symbol}(s)")

            if remainder:
                if self.next_currency:
                    self.next_currency.handle(remainder)
                else:
                    print(f"Cannot dispense {remainder} {self.symbol}(s)")
        elif self.next_currency:
            self.next_currency.handle(amount)

# Concrete Handler
class USDollar(Currency):
    def __init__(self, next_currency=None):
        super().__init__(self.__class__.__name__, next_currency)
        self.symbol = "USD"
        self.value = 1

# Concrete Handler
class Euro(Currency):
    def __init__(self, next_currency=None):
        super().__init__(self.__class__.__name__, next_currency)
        self.symbol = "EUR"
        self.value = 2

# Concrete Handler
class PoundSterling(Currency):
    def __init__(self, next_currency=None):
        super().__init__(self.__class__.__name__, next_currency)
        self.symbol = "GBP"
        self.value = 5


# Client
class ATM:
    def __init__(self):
        self.us_dollar = USDollar()
        self.euro = Euro(self.us_dollar)
        self.pound_sterling = PoundSterling(self.euro)

    def withdraw(self, amount):
        print(f"Withdrawing {amount}...\n")
        self.pound_sterling.handle(amount)
        print("\n")

atm = ATM()

# withdraw 27 pounds sterling
atm.withdraw(27)

# withdraw 8 euros
atm.withdraw(8)

# withdraw 100 US dollars
atm.withdraw(100)

-----------------------------------------------------------------------
Output:

Withdrawing 27...

5 PoundSterling(s) of 5 GBP(s)
1 Euro(s) of 2 EUR(s)


Withdrawing 8...

1 PoundSterling(s) of 5 GBP(s)
1 Euro(s) of 2 EUR(s)
1 USDollar(s) of 1 USD(s)


Withdrawing 100...

20 PoundSterling(s) of 5 GBP(s)

Chain of responsbility patternde gereksiz/yanlış biçince uygulandığında dezavantajlar ortaya çıkacaktır, zincirin her bir halkasında halkanın kendine ait business logici yürütüldüğünden işlem maliyeti artacaktır aynı zamanda zincirin sonuna ulaşmak için tüm halklarda işlemler gerçekleşeceğinden performans kaybına yol açabilir, ayrıca her halkanın isteği reddetme ve geçersiz kılma yeteneği olacağından isteğin kaybolması ihtimalini engelleyecek önlemler alınmalıdır.


Sources:

Published inBehavioral Design PatternsDesign Patterns

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *