اصول SOLID یک اصول قانون مند در برنامه نویسی شی گرا است که در تمام زبان های برنامه نویسی شی گرا مثل php موجود و قابل پیاده سازی است .
SOLID مخفف پنج اصل بسیار مهم در مدیریت وابستگی (Dependency Management) در توسعه ی برنامه های شی گرا می باشد. در واقع هر کدام از حروف کلمه ی SOLID به یکی از این اصول بر می گردد.
یکی از مشکلاتی که طراحی نامناسب برنامه های شی گرا برای برنامه نویسان ایجاد می کند موضوع مدیریت وابستگی در اجزای برنامه می باشد. اگر این وابستگی به درستی مدیریت نشود مشکلاتی شبیه موارد زیر در برنامه ایجاد می شوند:
برنامه ی نوشته شده را نمی توان تغییر داد و یا قابلیت جدید اضافه کرد. دلیل آن هم این است که با ایجاد تغییر در قسمتی از برنامه، این تغییر به صورت آبشاری در بقیه ی قسمت ها منتشر می شود و مجبور خواهیم بود که قسمت های زیادی از برنامه را تغییر دهیم. یعنی برنامه به یک برنامه ی ثابت و غیر پیشرفت تبدیل می شود. (این مشکل را Rigidity می نامیم.)
تغییر دادن برنامه مشکل است و آن هم به این دلیل که با ایجاد تغییر در یک قسمت از برنامه، قسمت های دیگر برنامه از کار می افتند و دچار مشکل می شوند. (این مشکل را Fragility می نامیم.(
قابلیت استفاده مجدد از اجزای برنامه وجود ندارد. در واقع، قسمت های مجدد برنامه ی شی گرای شما آنچنان به هم وابستگی تو در تو دارند که به هیچ وجه نمی توانید یک قسمت را جدا کرده و در برنامه ی دیگری استفاده کنید. (این مشکل را Immobility می نامیم.(
اصول SOLID که قصد رفع کردن این مشکلات و بسیاری مسائل گوناگون را دارد عبارت اند از:
- Single Responsibility Principle
- Open-Closed Principle
- Liskov Substitution Principle
- Interface Segregation Principle
- Dependency Inversion Principle
با کنار هم گذاشتن حرف اول هر کدام از این اصول کلمه ی SOLID ایجاد می شود. با در نظر گرفتن این پنج اصل و پیاده سازی آنها در برنامه های خود می توانید به یک طراحی شی گرا پاک و درست دست پیدا کنید.
S مخفف Single responsibility principle یا SRP به معنی اینکه هر کلاس بایستی فقط یک کار انجام دهد نه بیشتر, که در ادامه توضیح خواهم داد.
O مخفف Open/closed principle یا OCP به معنی اینکه کلاس ها جوری نوشته بشن که قابل گسترش باشند اما نیاز به تغییر نداشته باشند. در مطالب بعد بیشتر توضیح خواهم داد.
L مخفف Liskov Substitution Principle یا LSP به مفهوم اینکه هر کلاسی که از کلاس دیگر ارث بری میکند هرگز نباید رفتار کلاس والد را تغییر دهد.
I مخفف Interface Segregation Principle با ISP به مفهوم اینکه چند اینترفیس کوچک و خورد شده همیشه بهتر از یک اینترفیس کلی و بزرگ است.
D مخفف Dependency inversion principle یا DIP به معنی اینکه از اینترفیس ها به خوبی استفاده کن!
در این مقاله اصل اول را توضیح خواهیم داد و در مقالات بعدی چهار اصل دیگر را شرح خواهیم داد:
نوبت می رسد به معرفی اولین اصل یعنی Single responsibility principle که به صورت کوتاه S.R.P هم می گویند.
تعریف را اینگونه بیان کرده اند :
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(), "
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
';
}
}
با روش جدید ما ساختار مدیریت کتاب را از بخش پرینت جدا کردیم و همچنین ممکن است به انواع فرمت های مختلف بخواهیم خروجی پرینت داشته باشیم. با تعریف یک اینترفیس برای کلاس های پرینت می توانیم ساختار مشترکی برای آنها ایجاد کنیم و مجددا وظیفه پرینت هر فرمت را به کلاس مستقلی بسپاریم. به نظر شما با این روش انعطاف پذیری برنامه نویس بیشتر نمی شود؟ اگر روزی قرار باشد پرینت با فرمت جدیدی به پروژه افزوده شود نیازی به تغییر کلاس های دیگر نخواهد بود و فقط کلاس جدیدی به مجموعه افزوده خواهد شد که همین باعث حفظ ساختار, تفکیک وظایف, مدیریت بهتر و خطای کمتر خواهد بود.
دیدگاه و پرسش
در حال دریافت نظرات از سرور، لطفا منتظر بمانید
در حال دریافت نظرات از سرور، لطفا منتظر بمانید