آموزش Single responsibility principle در SOLID

علی حسین شهابی

1395/8/7

نظر0

سلام خدمت شما دوستان عزیز

در ادامه مبحث SOLID نوبت میرسه به معرفی اولین اصل  یعنی Single responsibility principle که به صورت کوتاه S.R.P  هم می گویند.(اگر با مفهوم SOLID آشنایی ندارین پیشنهاد میکنم اول این پست را مطالعه کنید)

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

که به این معنی است در برنامه نویسی شیء گرا این است که هر کلاس بایستی فقط و فقط یک وظیفه را برعهده داشته باشد. وقتی دو دلیل مختلف برای تغییر یک کلاس وجود داشته باشد بنابراین ممکن است دو تیم مختلف این کار را انجام دهند. در نهایت یک کلاس توسط دو تیم مختلف ویرایش می شود و این سبب می شود تا پروسه سوال و جواب برای هماهنگی و … طولانی شود.

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

class Circle {
    public $radius;

    public function __construct($radius) {
        $this->radius = $radius;
    }
}

class Square {
    public $length;

    public function __construct($length) {
        $this->length = $length;
    }
}

در بالا 2 کلاس برای دایره و مربع ایجاد کردیم  .

در دومین مرحله به ذهنمان می رسد کلاسی برای محاسبه ی محیط های اشکال هندسی ایجاد کنیم.نام این کلاس را 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(), "

"
        ));
    }
}

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

برای استفاده از این کلاس هم به صورت زیر  عمل می کنیم :

$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 . '
';
    }
 
}

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

خب مقاله امروز هم به پایان رسید.خوشحال میشم مثل همیشه نظراتتون رو اعلام کنید.

اینستاگرام علی حسین شهابی لینکدین علی حسین شهابی

1 نظر

( 1395/10/13 )ادیکام

بسیار عالی خسته نباشید

ارسال نظر جدید

 
Not signed in

کلیه حقوق مادی و معنوی , متعلق به سایت (Alihossein.ir)می باشد ,انتشار مطالب بدون ذکر منبع از نظر اخلاقی و شرعی صحیح نمی باشد.