دیزاین پترن Chain of Responsibility

دیزاین پترن Chain of Responsibility

Behavioral DESIGN PATTERNS

یکی از زیرشاخه های الگوهای طراحی، Behavioral Design Patterns است. تمرکز و وظیفه اصلی این دیزاین پترن ها تعامل اشیاء و جداسازی وظایف آن ها  از یکدیگر است.

Chain of Responsibility Design Pattern

دیزاین پترن Chain of Responsibility یکی از زیرشاخه های الگوهای طراحی از نوع Behavioral است.

الگوی طراحی زنجیره مسئولیت‌ها (Chain of Responsibility) از متصل کردن فرستنده درخواست به گیرنده آن با دادن فرصت به بیش از یک شی برای رسیدگی به درخواست جلوگیری می کند. این الگو اشیاء گیرنده را زنجیر می کند و درخواست را در امتداد زنجیره ارسال می کند تا زمانی که شیئی آن را کنترل کند.

سختش نکنیم! ما با استفاده از این دیزاین پترن میتوانیم زنجیره ای از کارها را پشت سر هم انجام دهیم بدون اینکه از بیرون بخواهیم این کارها را جداگانه انجام بدیم. در مثالی که در ادامه میزنیم، وظیفه لاگ کردن در فایل، در کنسول و یا نمایش Error را از هم جدا کرده ایم اما سطح خطا از سطح فایل و سطح فایل از سطح کنسول بالاتر است و میخواهیم بر اساس سطح ورودی لاگ های جداگانه را پشت سر هم بزنیم.

میزان استفاده : متوسط رو به پایین

پیاده سازی دیزاین پترن Chain of Responsibility

ما میخواهیم لاگ های نرم افزار را به ۳ سطح دسته بندی کنیم. سطوح Info, Debug و Error. بنابراین یک enum به شکل زیر داریم.

public enum LogLevel
    {
        INFO = 1,
        DEBUG = 2,
        ERROR = 3
    }

همچنین علاوه بر سطوح لاگ، کلاس هایی جهت مدیریت لاگ های Console, File و Error ها داریم.

کلاس ConsoleLogger را به شکل زیر نوشتیم.

public class ConsoleLogger : AbstractLogger
    {
        public ConsoleLogger(LogLevel level)
        {
            this.level = level;
        }
        protected override void Write(string message)
        {
            Console.WriteLine("Standard Console::Logger: " + message);
        }
    }

کلاس ErrorLogger را به شکل زیر نوشتیم.

public class ErrorLogger : AbstractLogger
    {
        public ErrorLogger(LogLevel level)
        {
            this.level = level;
        }

        protected override void Write(String message)
        {
            Console.WriteLine("Error Console::Logger: " + message);
        }
    }

کلاس FileLogger را به شکل زیر نوشتیم.

public class FileLogger : AbstractLogger
    {
        public FileLogger(LogLevel level)
        {
            this.level = level;
        }
        protected override void Write(String message)
        {
            Console.WriteLine("File::Logger: " + message);
        }
    }

همینطور که از کدهای بالا مشخص است، هر کدام یک متد جهت Write را override کرده اند. اما کلاس parent آنها که AbstractLogger است چه چیزی دارد که این دیزاین پترن را متمایز میکند؟

پس یک سر به کدهای نوشته شده در AbstractLogger بزنیم.

public abstract class AbstractLogger
    {
        protected LogLevel level;

        protected AbstractLogger nextLogger;

        public void SetNextLogger(AbstractLogger nextLogger)
        {
            this.nextLogger = nextLogger;
        }

        public void LogMessage(LogLevel level, String message)
        {
            if (this.level <= level)
            {
                Write(message);
            }
            if (nextLogger != null)
            {
                nextLogger.LogMessage(level, message);
            }
        }

        abstract protected void Write(String message);
    }

خب خب خب! ما توی این کلاس یک فیلد enumای از نوع LogLevel داریم که مشخص کننده سطح لاگ این کلاس است.

یک فیلد دیگر از نوع همین کلاس یعنی AbstractLogger داریم که از طریق متد SetNextLogger مقدار دهی می شود. اما چرا SetNextLogger؟

قلب تپنده این دیزاین پترن دقیقا همین متد است!

ما از طریق همین متد لاگ کننده بعدی این کلاس را مشخص می‌کنیم.

مطالب بعدی LogMessage است که وظیفه نمایش و ذخیره لاگ راه در لاگ کننده‌های سطح های پایین تر را بر عهده دارد. در ابتدا سطح فعلی با سطح وارد شده از متود را بررسی می کند و در صورتی که لاگ کننده بعدی برای این کلاس وجود داشت متد LogMessage که همین متد است را از لاگ کننده بعدی صدا میزند.

و در نهایت متد Write را داریم که از نوع abstract است.

حال به سراغ متد main برویم.

static void Main(string[] args)
        {
            try
            {
                ShowMessage("Chains Of Responsibility Design Pattern");

                AbstractLogger loggerChain = getChainOfLoggers();

                loggerChain.LogMessage(LogLevel.INFO, "This is an information.");

                loggerChain.LogMessage(LogLevel.DEBUG,
                         "This is an debug level information.");

                loggerChain.LogMessage(LogLevel.ERROR,
                         "This is an error information.");
            }
            catch (Exception ex)
            {
                ShowError(ex.Message);
            }
            Console.ReadLine();
        }

        private static AbstractLogger getChainOfLoggers()
        {

            AbstractLogger errorLogger = new ErrorLogger(LogLevel.ERROR);
            AbstractLogger fileLogger = new FileLogger(LogLevel.DEBUG);
            AbstractLogger consoleLogger = new ConsoleLogger(LogLevel.INFO);

            errorLogger.SetNextLogger(fileLogger);
            fileLogger.SetNextLogger(consoleLogger);

            return errorLogger;
        }

در کد بالا دو متد نوشتیم که یک از آن ها متد main است و دیگری متد getChainOfLoggers است.
ما در متد main این متد را صدا زدیم تا یک زنجیره ای از لاگ کننده ها را به ما بدهد و بعد از دریافت لاگ های مختلفی را از طریق متد LogMessage صادر کرده ایم.
حالا به متد getChainOfLoggers میپردازیم.
در این متد ۳ لاگ کننده از ۳ نوع ErrorLogger با سطح Error و FileLogger با سطح Debug و ConsoleError با سطح Info ایجاد کرده ایم.
نکته اصلی این دیزاین پترن، همانطور که در بالا گفتم متد SetNextLogger است.
با استفاده از این متد ما لاگ کننده بعدی ErrorLogger را برابر FileLogger و لاگ کننده بعدی FileLogger را ConsoleLogger کرده ایم.
و سپس بالاترین سطح یعنی ErrorLogger را بازگرداندیم.
خروجی کد بالا را میتوانیم در ادامه ببینیم.

Chains Of Responsibility Design Pattern
=================================================
Standard Console::Logger: This is an information.
=================================================
File::Logger: This is an debug level information.
Standard Console::Logger: This is an debug level information.
=================================================
Error Console::Logger: This is an error information.
File::Logger: This is an error information.
Standard Console::Logger: This is an error information.

برای آموزش رایگان در مورد انواع دیزاین پترن ها و معماری های نرم افزار می توانید به این آموزش مراجعه نمایید:

معماری نرم افزار مهم ترین پترن های معماری نرم افزار

دیزاین پترن یا الگوی طراحی چیست؟

دیزاین پترن  در حقیقت راه حل مسائل طراحی نرم افزار هستند که بارها و بارها در دنیای توسعه نرم افزار تکرار میشوند. الگوهایی از طراحی قابل استفاده مجدد (reusable) و تعامل اشیاء.

پرینت صفحه

هولوسن

با من یاد بگیر

آموزش های بیشتر در وبسایت هولوسن : https://holosen.net

بدون نظر

**پرسش و پاسخ** سوال خود مطرح کنید.
امتیاز شما*