مرا اسکن کن!

معرفی Single responsibility principle در قاعده SOLID

معرفی Single responsibility principle در قاعده SOLID



Single Responsibility یا مسئولیت واحد که حرف اول اصل SOLID است به این نکته اشاره دارد که توابع و کلاس های مختلف، هر کدام فقط مسئول یک قسمت منطقی از سیستم هستند. یعنی هر کدام از کلاس ها یا توابع برنامه فقط و فقط باید یک کار یا وظیفه را انجام دهند.

(اگر با مفهوم SOLID آشنایی ندارین پیشنهاد میکنم اول این پست را مطالعه کنید)

اگر یک کلاس داشته باشید که چند کار را با هم انجام دهند، احتمالا در حال دور شدن از اصل مسئولیت واحد هستید.

تعریف را اینگونه بیان کرده اند :
A class should have one and only one reason to change, meaning that a class should have only one job.

 

شکل زیر را در نظر بگیرید:

همان طور که در تصویر بالا میبینید، یک کلاس داریم به اسم UserClass که وظایف و کارهای مربوط به کاربر را انجام می دهد. در این کلاس سن، نام و ایمیل از صفات این کلاس هستند و دو تابع دریافت سن و دریافت ایمیل از این کاربر، در این کلاس قرار دارند. اما نگاهی به دو تابع آخر بیندازید، یکی مربوط به چک کردن صحت ایمیل کاربر و یکی دیگر مربوط به ارسال پیامک برای این کاربر است. همان طور که مشاهده می کنید، به نظر این دو وظیفه آخر مربوط به کلاس کاربر نیست و خودشان می بایستی که در کلاس ها جداگانه ای نوشته شوند. یعنی الان یک کلاس داریم که مسئولیت واحدی ندارد و همه کاری انجام می دهد.

در برنامه نویسی معمولا اگر توابع شما تعدا خط زیادی داشته باشد این احتمال می رود که در حال دور شدن از Single Responsibility هستید. یکی از راه های غلبه بر این مشکل عملیات Refactoring است به گونه ای که توابع و کدها را دوباره از نو نگاهی بیندازید و آن هایی که از لحاظ منطقی میتوانند خودشان یک کلاس یا تابع باشند را جدا نمایید.

 

برای مثال دیگر ما تعدادی اشکال هندسی داریم (مربع و دایره و …) و می خواهیم مجموع محیط های این اشکال را حساب نماییم . خب  اولین چیزی که به ذهن ما می آید این است که برای هر شکل , یک کلاس در نظر بگیریم و توسط متد سازنده ی آن ((construct و زاویه یا طول هر ضلع را معلوم کنیم.

 

class Circle {

    public $radius;



    public function __construct($radius) {

        $this->radius = $radius;

    }

}



class Square {

    public $length;



    public function __construct($length) {

        $this->length = $length;

    }

}

 

در قطعه کد بالا ۲ کلاس برای دایره و مربع ایجاد کردیم  .در دومین مرحله به محاسبه ی محیط های اشکال هندسی می پردازیم.نام این کلاس را AreaCalculator می گذاریم.

class AreaCalculator {



    protected $shapes;



    public function __construct($shapes = array()) {

        $this->shapes = $shapes;

    }



    public function sum() {

        // logic to sum the areas

    }



    public function output() {

        return implode('', array(

            "

", "Sum of the areas of provided shapes: ", $this->sum(), "

PHP

"

        ));

    }

}

 

در تابع سازنده ی این کلاس اشکال مختلف را به صورت آرایه می گیریم سپس با استفاده از متد sum مجموع آنها را حساب می کنیم و در نهایت با استفاده از متد output نتیجه را در قالب HTML و CSS چاپ می کنیم .برای استفاده از این کلاس هم به صورت زیر  عمل می کنیم :

$shapes = array(

    new Circle(2),

    new Square(5),

    new Square(6)

);



$areas = new AreaCalculator($shapes);

echo $areas->output();

 

اگر بخواهیم به جای خروجی HTML به ما json بدهد چیکار باید انجام دهیم ؟

طبق اصل Single responsiblity principle که هر کلاس فقط باید یک کار انجام دهد عمل می کنیم . تمام منطق محاسبه محیط را داخل کلاس AreaCalculator انجام می دهیم و  نمایش خروجی را به کلاس دیگری مثلا به نام  SumCalculatorOutputter می سپاریم .در نهایت می توانیم به این صورت از این کلاس ها استفاده نماییم :

$shapes = array(

    new Circle(2),

    new Square(5),

    new Square(6)

);



$areas = new AreaCalculator($shapes);

$output = new SumCalculatorOutputter($areas);



echo $output->JSON();

echo $output->HAML();

echo $output->HTML();

echo $output->JADE();

 

مثال دوم :

فرض کنید کلاسی با نام Book دارید که وظیفه مدیریت عناوین و محتوای کتاب را بر عهده دارد مانند کلاس زیر :

function getTitle() {

        return "A Great Book";

    }



    function getAuthor() {

        return "John Doe";

    }



    function turnPage() {

        // pointer to next page

    }



    function printCurrentPage() {

        echo "current page content";

    }

}

 

این کلاس به هر حال کاری که از آن انتظار داریم انجام می دهد و میتوان گفت به ظاهر کلاس خوبی نوشته ایم. اما مشکلی که وجود دارد این است که وظیفه ی پرینت کردن صفحه با وظیفه ی مدیریت یک کتاب وظایف بسیار متفاوتی هستند. لذا با این ساختار در واقع SRP را نادیده گرفته ایم. اما بیایید همین مثال را در قالب SRP پیاده سازی کنیم :


class Book {



    function getTitle() {

        return "A Great Book";

    }



    function getAuthor() {

        return "John Doe";

    }



    function turnPage() {

        // pointer to next page

    }



    function getCurrentPage() {

        return "current page content";

    }



}



interface Printer {



    function printPage($page);

}



class PlainTextPrinter implements Printer {



    function printPage($page) {

        echo $page;

    }



}



class HtmlPrinter implements Printer {



    function printPage($page) {

        echo '

' . $page . '

PHP

';

    }



}

با روش جدید ما ساختار مدیریت کتاب را از بخش پرینت جدا کردیم و همچنین ممکن است به انواع فرمت های مختلف بخواهیم خروجی پرینت داشته باشیم. با تعریف یک اینترفیس برای کلاس های پرینت می توانیم ساختار مشترکی برای آنها ایجاد کنیم و مجددا وظیفه پرینت هر فرمت را به کلاس مستقلی بسپاریم. به نظر شما با این روش انعطاف پذیری برنامه نویس بیشتر نمی شود؟ اگر روزی قرار باشد پرینت با فرمت جدیدی به پروژه افزوده شود نیازی به تغییر کلاس های دیگر نخواهد بود و فقط کلاس جدیدی به مجموعه افزوده خواهد شد که همین باعث حفظ ساختار, تفکیک وظایف, مدیریت بهتر و خطای کمتر خواهد بود.


نوشته شده توسط :

وحید صمدیان وحید صمدیان



پنجشنبه, 19 مهر 1397

تعداد بازديد : 537

برچسب ها : الگوی طراحی Design Pattern

3.0 ستاره