Design Pattern چیست؟ راهنمای جامع الگوهای طراحی نرم افزار

Design Pattern چیست؟ راهنمای جامع الگوهای طراحی نرم افزار

  • سطح : متوسط
  • مطالعه : 5 دقیقه
  • 39

مقدمه

اگر چند سالی برنامه‌نویسی کرده باشید، احتمالاً بارها نام Design Pattern را شنیده‌اید. شاید در آگهی‌های استخدام دیده باشید که نوشته‌اند «آشنایی با Design Pattern الزامی است»، یا هنگام مطالعه مستندات Laravel، Spring و .NET با مفاهیمی مانند Factory، Strategy، Observer و Singleton روبه‌رو شده باشید.

اما سؤال اصلی اینجاست؛ آیا Design Pattern فقط مجموعه‌ای از چند الگوی آماده است که باید آن‌ها را حفظ کنیم، یا واقعاً مشکلی را در پروژه‌های واقعی حل می‌کند؟

واقعیت این است که بسیاری از برنامه‌نویسان، Design Pattern را از انتها یاد می‌گیرند. آن‌ها ابتدا نام Patternها را حفظ می‌کنند، سپس سعی می‌کنند هر پروژه‌ای را با یکی از آن‌ها پیاده‌سازی کنند. نتیجه معمولاً کدهایی پیچیده‌تر از قبل است.

در حالی که مسیر درست کاملاً برعکس است. ابتدا باید مشکل را ببینید، درد پروژه را لمس کنید و بعد متوجه شوید چرا Design Pattern به وجود آمده است. زمانی که دلیل وجود یک Pattern را درک کنید، دیگر نیازی به حفظ کردن آن نخواهید داشت.

در این مقاله ابتدا با یک مثال واقعی شروع می‌کنیم، سپس مفهوم Design Pattern را به زبان ساده بررسی می‌کنیم، با تاریخچه شکل‌گیری آن آشنا می‌شویم، سه دسته اصلی الگوهای طراحی را یاد می‌گیریم و در پایان نیز خواهید دانست چه زمانی باید از Design Pattern استفاده کنید و چه زمانی استفاده نکردن از آن تصمیم بهتری است.

آنچه در این مقاله یاد می‌گیرید

  • Design Pattern چیست و چرا به وجود آمد.
  • با یک مثال واقعی، مشکلات پروژه بدون Design Pattern را درک کنید.
  • تاریخچه شکل‌گیری Design Pattern و نقش کتاب GoF را بشناسید.
  • سه دسته اصلی Design Pattern (Creational، Structural و Behavioral) را یاد بگیرید.
  • مزایا و معایب استفاده از Design Pattern را بررسی کنید.
  • تفاوت استفاده و عدم استفاده از Design Pattern را با یک مثال عملی در PHP مقایسه کنید.
  • با رایج‌ترین Design Patternها و کاربرد هرکدام آشنا شوید.
  • یاد بگیرید چه زمانی باید از Design Pattern استفاده کنید و چه زمانی استفاده نکردن از آن تصمیم بهتری است.
  • اشتباهات رایج برنامه‌نویسان در استفاده از Design Pattern را بشناسید.

یک پروژه واقعی بدون Design Pattern

اگر این فروشگاه را بدون استفاده از Design Pattern توسعه دهیم، معمولاً ساختار پروژه به مرور زمان شبیه نمونه زیر خواهد شد.

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

نمونه زیر یکی از رایج‌ترین ساختارهایی است که در پروژه‌های واقعی مشاهده می‌شود.

<?php
class PaymentService
{
    public function pay(array $order, string $gateway)
    {
        if ($gateway === 'zarinpal') {
            // اعتبارسنجی اطلاعات
            // ارسال درخواست
            // ذخیره لاگ
            // بررسی خطا
            // بازگرداندن لینک پرداخت
        } elseif ($gateway === 'idpay') {
            // اعتبارسنجی
            // ساخت درخواست
            // ارسال API
            // بررسی پاسخ
            // ثبت لاگ
        } elseif ($gateway === 'nextpay') {
            // کدهای مشابه...
        } elseif ($gateway === 'stripe') {
            // کدهای مشابه...
        } elseif ($gateway === 'paypal') {
            // کدهای مشابه...
        } else {
            throw new Exception('Gateway not found');
        }
    }
}

شاید در نگاه اول این کد مشکل خاصی نداشته باشد، اما تصور کنید شش ماه از شروع پروژه گذشته است.

اکنون فروشگاه از هشت درگاه پرداخت مختلف پشتیبانی می‌کند، هر درگاه قوانین مخصوص خودش را دارد و هر بار که یک درگاه جدید اضافه می‌شود باید همین کلاس را تغییر دهید.

این دقیقاً همان نقطه‌ای است که نگهداری پروژه دشوار می‌شود.

مشکل اصلی چیست؟

  • هر قابلیت جدید باعث تغییر کلاس اصلی می‌شود.
  • کدهای تکراری به مرور زیاد می‌شوند.
  • تست واحد (Unit Test) سخت‌تر می‌شود.
  • ریسک ایجاد باگ در تغییرات بعدی افزایش پیدا می‌کند.
  • کلاس مسئولیت‌های زیادی بر عهده گرفته است.

حالا فرض کنید مدیر پروژه از شما می‌خواهد درگاه «ملت» را نیز به سیستم اضافه کنید.

اگر از این ساختار استفاده کرده باشید، مجبور هستید دوباره فایل اصلی را باز کنید، یک شرط جدید بنویسید، بخش‌های مختلف کلاس را تغییر دهید و امیدوار باشید که تغییرات جدید باعث خراب شدن درگاه‌های قبلی نشود.

به این نوع طراحی اصطلاحاً Tight Coupling یا وابستگی شدید گفته می‌شود؛ یعنی اجزای سیستم آن‌قدر به یکدیگر وابسته هستند که کوچک‌ترین تغییر، بخش‌های مختلف پروژه را تحت تأثیر قرار می‌دهد.
مشکل پروژه ها بدون الگوی طراحی

اگر از Design Pattern استفاده کنیم چه تغییری ایجاد می‌شود؟

حالا همان پروژه را از زاویه‌ای دیگر ببینیم.

به جای اینکه تمام منطق پرداخت داخل یک کلاس قرار بگیرد، هر درگاه پرداخت را به یک کلاس مستقل تبدیل می‌کنیم. تمام این کلاس‌ها فقط یک قرارداد مشترک را پیاده‌سازی می‌کنند.

در این مثال از Strategy Pattern استفاده می‌کنیم؛ یکی از پرکاربردترین الگوهای طراحی که در ادامه این مجموعه به صورت کامل آن را بررسی خواهیم کرد.

در Strategy Pattern دیگر کلاس PaymentService مسئول پیاده‌سازی تمام درگاه‌های پرداخت نیست. تنها کاری که انجام می‌دهد، استفاده از یک درگاه پرداخت است؛ بدون اینکه بداند پشت صحنه آن درگاه چگونه کار می‌کند.

ابتدا یک قرارداد (Interface) تعریف می‌کنیم که تمام درگاه‌های پرداخت موظف به پیاده‌سازی آن هستند.

<?php
interface PaymentGateway
{
    public function pay(array $order): string;
}

حالا هر درگاه پرداخت کلاس مخصوص خودش را خواهد داشت.

<?php
class ZarinpalGateway implements PaymentGateway
{
    public function pay(array $order): string
    {
        // ارتباط با API زرین‌پال

        return "https://zarinpal.com/payment/...";
    }
}
<?php
class StripeGateway implements PaymentGateway
{
    public function pay(array $order): string
    {
        // ارتباط با API استرایپ

        return "https://stripe.com/payment/...";
    }
}

اکنون کلاس اصلی پرداخت بسیار ساده شده است.

<?php
class PaymentService
{
    public function __construct(
        private PaymentGateway $gateway
    ) {
    }

    public function pay(array $order): string
    {
        return $this->gateway->pay($order);
    }
}

در نهایت فقط کافی است درگاه موردنظر را هنگام ساخت شیء مشخص کنیم.

<?php
$gateway = new StripeGateway();

$payment = new PaymentService($gateway);

$link = $payment->pay($order);

اگر فردا مشتری بخواهد درگاه جدیدی مانند PayPal یا ملت به سیستم اضافه کند، دیگر نیازی نیست کلاس PaymentService را تغییر دهید. فقط کافی است یک کلاس جدید ایجاد کنید که Interface بالا را پیاده‌سازی کند.

به عبارت دیگر، سیستم شما برای افزوده شدن قابلیت‌های جدید باز است، اما برای تغییر کدهای قبلی بسته است. این دقیقاً یکی از مهم‌ترین ایده‌هایی است که بعدها در اصل Open/Closed از SOLID نیز مشاهده خواهید کرد.

نکته

NOTE
دقت کنید که Strategy Pattern قرار نیست تعداد خطوط کد را کمتر کند. اتفاقاً ممکن است تعداد فایل‌های پروژه بیشتر شود؛ اما در عوض هر کلاس فقط یک مسئولیت خواهد داشت و توسعه و نگهداری پروژه بسیار ساده‌تر می‌شود.

مقایسه دو رویکرد

بدون Design Pattern با Design Pattern
تمام منطق پرداخت داخل یک کلاس قرار دارد. هر درگاه پرداخت کلاس مستقل خود را دارد.
اضافه شدن هر درگاه نیازمند تغییر کلاس اصلی است. تنها یک کلاس جدید ایجاد می‌شود.
وابستگی بین اجزای پروژه زیاد است. وابستگی‌ها به حداقل می‌رسند.
تست واحد دشوار است. هر کلاس به‌صورت مستقل قابل تست است.
احتمال ایجاد باگ هنگام توسعه زیاد است. تغییرات روی بخش‌های دیگر پروژه اثر نمی‌گذارد.
نگهداری پروژه با گذشت زمان سخت‌تر می‌شود. توسعه و نگهداری پروژه ساده‌تر و کم‌هزینه‌تر است.

قبل و بعد از استفاده از design pattern

پس Design Pattern دقیقاً چیست؟

تا اینجا هنوز حتی تعریف رسمی Design Pattern را نخوانده‌ایم، اما احتمالاً خودتان متوجه شده‌اید که الگوهای طراحی قرار نیست معجزه کنند یا پروژه را کوتاه‌تر بنویسند.
کاری که Design Pattern انجام می‌دهد، ارائه یک راه‌حل آزموده‌شده برای حل مشکلاتی است که هزاران برنامه‌نویس قبل از ما بارها با آن‌ها روبه‌رو شده‌اند. به همین دلیل، به جای اینکه هر بار از صفر برای حل یک مشکل معماری فکر کنیم، می‌توانیم از تجربه‌ای استفاده کنیم که بارها در پروژه‌های واقعی امتحان خود را پس داده است.
به بیان ساده، Design Pattern یا الگوی طراحی یک قطعه کد آماده، یک کتابخانه یا یک فریم‌ورک نیست؛ بلکه یک راه‌حل استاندارد و بارها آزموده‌شده برای حل مشکلات رایج در طراحی نرم‌افزار است.
به عبارت دیگر، زمانی که برنامه‌نویسان مختلف در پروژه‌های گوناگون بارها با یک مشکل مشابه روبه‌رو شدند و در نهایت به یک راه‌حل مؤثر رسیدند، آن راه‌حل به مرور زمان به یک Design Pattern تبدیل شد.
بنابراین Design Pattern به شما نمی‌گوید دقیقاً چه کدی بنویسید؛ بلکه نشان می‌دهد برای حل یک مسئله مشخص، چه ساختاری معمولاً بهترین نتیجه را خواهد داشت.

تعریف کوتاهDesign Pattern مجموعه‌ای از راهکارهای استاندارد برای حل مشکلات تکرارشونده در طراحی نرم‌افزار است؛ راهکارهایی که در پروژه‌های واقعی بارها امتحان شده‌اند و هدف آن‌ها افزایش خوانایی، توسعه‌پذیری و قابلیت نگهداری کد است.

چرا به آن «Pattern» می‌گوییم؟

کلمه Pattern به معنی «الگو» است، نه «قالب».
فرض کنید یک معمار برای ساخت پل، بیمارستان یا برج از الگوهای مشخصی استفاده می‌کند. این الگوها تضمین نمی‌کنند که تمام ساختمان‌ها کاملاً شبیه هم باشند، اما نشان می‌دهند بهترین روش برای حل یک مسئله چیست.
در برنامه‌نویسی نیز دقیقاً همین اتفاق می‌افتد. دو تیم ممکن است هر دو از Strategy Pattern استفاده کنند، اما ساختار پروژه و حتی کدهای آن‌ها کاملاً متفاوت باشد.
چیزی که مشترک است، ایده حل مسئله است، نه کد.

Design Pattern کد آماده نیست

یکی از رایج‌ترین اشتباهات برنامه‌نویسان تازه‌کار این است که تصور می‌کنند Design Pattern مجموعه‌ای از کلاس‌های آماده است که می‌توان آن‌ها را کپی و در پروژه استفاده کرد.
در حالی که این تصور کاملاً اشتباه است.
فرض کنید دو فروشگاه اینترنتی مختلف دارید؛ یکی سیستم فروش کتاب است و دیگری سامانه رزرو بلیت هواپیما. ممکن است هر دو از Strategy Pattern برای مدیریت پرداخت استفاده کنند، اما کدهای آن‌ها حتی یک خط هم شبیه یکدیگر نباشد.
زیرا Pattern فقط ساختار کلی حل مسئله را مشخص می‌کند، نه جزئیات پیاده‌سازی را.

اگر بخواهیم در یک جمله تفاوت را بیان کنیم:

  • Code یعنی پیاده‌سازی.
  • Framework یعنی مجموعه‌ای از ابزارها.
  • Library یعنی قابلیت‌های آماده.
  • Design Pattern یعنی یک روش استاندارد برای طراحی ساختار کد.

Design Pattern راه_حل است، نه کد آماده

چه مشکلاتی با Design Pattern حل می‌شوند؟

شاید این سؤال برایتان پیش آمده باشد که اگر می‌توان بدون Design Pattern هم پروژه نوشت، پس اصلاً چرا باید آن را یاد بگیریم؟
واقعیت این است که در پروژه‌های کوچک معمولاً نیازی به استفاده از الگوهای طراحی احساس نمی‌شود. اما هرچه پروژه بزرگ‌تر شود، تعداد توسعه‌دهندگان افزایش پیدا کند و امکانات جدید به سیستم اضافه شوند، مشکلاتی ظاهر می‌شوند که بدون یک معماری مناسب کنترل آن‌ها بسیار دشوار خواهد بود.
Design Pattern دقیقاً برای حل چنین مشکلاتی به وجود آمده است.

  • کاهش وابستگی بین کلاس‌ها (Loose Coupling)
  • افزایش قابلیت توسعه (Extensibility)
  • سادگی در نگهداری پروژه (Maintainability)
  • افزایش خوانایی کد (Readability)
  • تسهیل تست‌نویسی (Testability)
  • استفاده مجدد از کدها (Reusability)
  • کاهش احتمال ایجاد باگ هنگام توسعه قابلیت‌های جدید

به همین دلیل است که تقریباً تمام فریم‌ورک‌های مدرن مانند Laravel، Symfony، ASP.NET Core، Spring Boot و Django در بخش‌های مختلف خود از Design Patternهای گوناگون استفاده می‌کنند.

تاریخچه Design Pattern؛ از معماری ساختمان تا مهندسی نرم‌افزار

شاید برایتان جالب باشد که ایده Design Pattern ابتدا در دنیای برنامه‌نویسی شکل نگرفت.
در دهه ۱۹۷۰، معمار برجسته Christopher Alexander هنگام طراحی ساختمان‌ها و شهرها متوجه شد بسیاری از مشکلات معماری بارها و بارها تکرار می‌شوند. او به جای اینکه برای هر پروژه از صفر فکر کند، تصمیم گرفت راه‌حل‌های موفق را مستندسازی کند.
نتیجه این تحقیقات در کتاب مشهور A Pattern Language منتشر شد؛ کتابی که مفهوم «الگو» را به عنوان راهکاری تکرارپذیر برای حل یک مسئله معرفی کرد.
چند سال بعد، برنامه‌نویسان متوجه شدند توسعه نرم‌افزار نیز دقیقاً با همین چالش روبه‌رو است؛ بسیاری از پروژه‌ها مشکلات مشابهی دارند و توسعه‌دهندگان مختلف بارها راه‌حل‌های تقریباً یکسانی را پیاده‌سازی می‌کنند.
به همین دلیل ایده الگوهای طراحی وارد دنیای مهندسی نرم‌افزار شد.

کتابی که همه چیز را تغییر داد

در سال ۱۹۹۴ چهار برنامه‌نویس کتابی منتشر کردند که مسیر طراحی نرم‌افزار را برای همیشه تغییر داد.
این کتاب با عنوان Design Patterns: Elements of Reusable Object-Oriented Software منتشر شد و نویسندگان آن بعدها با نام Gang of Four (GoF) شناخته شدند.
هدف این کتاب معرفی زبان برنامه‌نویسی جدید یا ارائه کتابخانه‌ای خاص نبود؛ بلکه نویسندگان تلاش کردند رایج‌ترین راه‌حل‌هایی را که در پروژه‌های شی‌گرا بارها امتحان خود را پس داده بودند، در قالب الگوهای استاندارد مستند کنند.
این کتاب ۲۳ Design Pattern را معرفی کرد؛ الگوهایی که بسیاری از آن‌ها پس از گذشت بیش از سه دهه، همچنان در فریم‌ورک‌های مدرن و پروژه‌های بزرگ مورد استفاده قرار می‌گیرند.

نکته

NOTE
بیشتر Patternهایی که امروز در Laravel، Symfony، Spring Boot، ASP.NET Core و حتی بسیاری از کتابخانه‌های JavaScript مشاهده می‌کنید، ریشه در همان کتاب معروف GoF دارند.

تاریخچه-design-pattern

آیا همه Design Patternها متعلق به کتاب GoF هستند؟

خیر.
کتاب GoF نقطه شروع بسیار مهمی در تاریخ مهندسی نرم‌افزار بود، اما پایان ماجرا نیست. با پیشرفت زبان‌های برنامه‌نویسی، معماری نرم‌افزار و فریم‌ورک‌های مدرن، الگوهای طراحی جدیدی نیز معرفی شدند که در کتاب اصلی وجود نداشتند.
با این حال، هنوز هم همان ۲۳ Pattern کلاسیک پایه و اساس یادگیری Design Pattern محسوب می‌شوند و تقریباً تمام برنامه‌نویسان حرفه‌ای حداقل با مفاهیم آن‌ها آشنا هستند.

دسته‌بندی Design Patternها

برای اینکه یادگیری ۲۳ Pattern ساده‌تر شود، نویسندگان کتاب GoF آن‌ها را بر اساس نوع مشکلی که حل می‌کنند، در سه گروه اصلی قرار دادند.
این دسته‌بندی باعث می‌شود هنگام مواجه شدن با یک مسئله، سریع‌تر تشخیص دهید باید به سراغ کدام گروه از Patternها بروید.

۱. Creational Patterns (الگوهای ایجادکننده)

فرض کنید قرار است یک شیء (Object) در پروژه ایجاد کنید، اما فرآیند ساخت آن ساده نیست. شاید ساخت آن به تنظیمات مختلف، وابستگی‌های متعدد یا شرایط گوناگون وابسته باشد.
در چنین شرایطی اگر ایجاد شیء را در بخش‌های مختلف پروژه انجام دهید، به مرور زمان نگهداری سیستم بسیار دشوار خواهد شد.
الگوهای Creational دقیقاً برای حل همین مشکل طراحی شده‌اند. این دسته از Patternها فرآیند ایجاد اشیا را مدیریت می‌کنند تا وابستگی بین کلاس‌ها کاهش پیدا کند و توسعه سیستم ساده‌تر شود.
به عنوان مثال، در Laravel هنگام استفاده از Service Container یا Factoryها، در واقع با نمونه‌هایی از همین دسته روبه‌رو هستید.


۲. Structural Patterns (الگوهای ساختاری)

بعد از ایجاد اشیا، نوبت به برقراری ارتباط میان آن‌ها می‌رسد.
گاهی لازم است دو کلاس که ساختار متفاوتی دارند با یکدیگر کار کنند، گاهی باید بدون تغییر کلاس اصلی قابلیت جدیدی اضافه کنیم و گاهی نیز می‌خواهیم چندین شیء را مانند یک شیء واحد مدیریت کنیم.
الگوهای Structural روی نحوه ارتباط و ترکیب کلاس‌ها تمرکز دارند تا ساختار پروژه انعطاف‌پذیرتر و قابل نگهداری‌تر شود.
برای مثال Patternهایی مانند Adapter، Decorator و Facade در همین گروه قرار می‌گیرند و تقریباً در تمام پروژه‌های بزرگ کاربرد دارند.


۳. Behavioral Patterns (الگوهای رفتاری)

حال تصور کنید اشیای پروژه ایجاد شده‌اند و ارتباط آن‌ها نیز مشخص است؛ اما این اشیا چگونه باید با یکدیگر تعامل داشته باشند؟
برای مثال، زمانی که یک سفارش ثبت می‌شود باید پیامک ارسال شود، ایمیل ارسال شود، موجودی انبار کاهش پیدا کند و فاکتور صادر شود.
اگر تمام این منطق را داخل یک کلاس قرار دهید، پروژه خیلی سریع به سیستمی پیچیده و غیرقابل نگهداری تبدیل خواهد شد.
الگوهای Behavioral نحوه تعامل بین اشیا را مدیریت می‌کنند و باعث می‌شوند هر بخش از سیستم فقط مسئول انجام وظیفه خود باشد.
Patternهایی مانند Strategy، Observer و Command از مشهورترین اعضای این خانواده هستند.
سه دسته اصلی design pattern

جدول کامل Design Patternها

کتاب GoF در مجموع ۲۳ Design Pattern را معرفی می‌کند که در سه دسته بالا قرار می‌گیرند. لازم نیست در اولین مطالعه همه آن‌ها را حفظ کنید. در ادامه این مجموعه، هر کدام را با مثال‌های واقعی و پروژه‌محور به صورت کامل بررسی خواهیم کرد.

دسته Pattern کاربرد
Creational Singleton مدیریت ایجاد تنها یک نمونه از کلاس
Factory Method ساخت اشیا بدون وابستگی به کلاس‌های مشخص
Abstract Factory ایجاد خانواده‌ای از اشیای مرتبط
Builder ساخت مرحله‌ای اشیای پیچیده
Prototype ساخت شیء با کپی کردن نمونه موجود
Structural Adapter اتصال کلاس‌های ناسازگار
Bridge جدا کردن پیاده‌سازی از انتزاع
Composite مدیریت ساختارهای درختی
Decorator افزودن قابلیت بدون تغییر کلاس اصلی
Facade ارائه یک رابط ساده برای سیستم پیچیده
Flyweight کاهش مصرف حافظه
Proxy کنترل دسترسی به اشیا
Behavioral Strategy تغییر رفتار در زمان اجرا
Observer اطلاع‌رسانی خودکار بین اشیا
Command تبدیل درخواست به شیء
Chain of Responsibility عبور درخواست بین چند پردازشگر
State تغییر رفتار بر اساس وضعیت شیء
Template Method تعریف اسکلت الگوریتم
Mediator کاهش وابستگی بین اشیا
Iterator پیمایش مجموعه داده‌ها
Memento ذخیره و بازیابی وضعیت قبلی
Visitor افزودن عملیات بدون تغییر کلاس‌ها
Interpreter تفسیر زبان‌های ساده و دستورات

لازم نیست همه Patternها را حفظ کنید.بسیاری از برنامه‌نویسان حرفه‌ای سال‌هاست با Patternهایی مانند Strategy، Factory، Observer، Decorator، Adapter و Facade کار می‌کنند، اما شاید هیچ‌وقت در پروژه‌های واقعی از Visitor یا Interpreter استفاده نکرده باشند. مهم‌تر از حفظ کردن نام Patternها، درک مشکلی است که هر کدام حل می‌کنند.

رابطه Design Pattern با برنامه‌نویسی شی‌گرا (OOP)

اگر تا اینجا مقاله را مطالعه کرده باشید، احتمالاً یک سؤال برایتان پیش آمده است:

اگر Design Pattern این‌قدر مهم است، چرا قبل از یادگیری آن همیشه توصیه می‌شود ابتدا OOP را یاد بگیریم؟

پاسخ بسیار ساده است؛ زیرا تقریباً تمام Design Patternها بر پایه مفاهیم برنامه‌نویسی شی‌گرا ساخته شده‌اند.
در واقع Design Pattern جایگزین OOP نیست، بلکه راه صحیح استفاده از قابلیت‌های OOP برای حل مشکلات واقعی است.
اگر مفاهیمی مانند کلاس، شیء، Interface، وراثت، چندریختی (Polymorphism) و Abstraction را ندانید، پیاده‌سازی اکثر Patternها تقریباً غیرممکن خواهد بود.

نکته

NOTE
ابتدا OOP به ما ابزار می‌دهد؛ سپس Design Pattern نشان می‌دهد این ابزارها را چگونه در کنار هم قرار دهیم تا نرم‌افزارهای قابل توسعه و قابل نگهداری بسازیم.

یک مثال ساده

بیایید دوباره همان مثال درگاه پرداخت را بررسی کنیم.
در نسخه اولیه پروژه، کلاس PaymentService مستقیماً کلاس زرین‌پال را ایجاد می‌کند.

<?php
class PaymentService
{
    public function pay(array $order)
    {
        $gateway = new ZarinpalGateway();

        return $gateway->pay($order);
    }
}

در نگاه اول هیچ مشکلی وجود ندارد؛ اما اگر فردا تصمیم بگیرید از Stripe یا PayPal استفاده کنید چه اتفاقی می‌افتد؟

مجبور هستید دوباره همین کلاس را تغییر دهید.

علت این مشکل این است که کلاس PaymentService مستقیماً به ZarinpalGateway وابسته شده است.

حالا از یکی از مهم‌ترین مفاهیم OOP یعنی Interface استفاده می‌کنیم.

<?php
interface PaymentGateway
{
    public function pay(array $order);
}

سپس هر درگاه این قرارداد را پیاده‌سازی می‌کند.

<?php
class ZarinpalGateway implements PaymentGateway
{
    public function pay(array $order)
    {
        //
    }
}
<?php
class StripeGateway implements PaymentGateway
{
    public function pay(array $order)
    {
        //
    }
}

اکنون PaymentService دیگر هیچ اطلاعی از نوع درگاه پرداخت ندارد.

<?php
class PaymentService
{
    public function __construct(
        private PaymentGateway $gateway
    ) {
    }

    public function pay(array $order)
    {
        return $this->gateway->pay($order);
    }
}

در این مثال هیچ Pattern پیچیده‌ای اتفاق نیفتاده است؛ فقط از قابلیت‌های OOP استفاده کردیم.

اما همین قابلیت‌ها پایه و اساس Patternهایی مانند Strategy، Factory و Dependency Injection هستند.

Design Pattern بدون OOP تقریباً بی‌معناست

فرض کنید یک نجار بهترین ابزارهای دنیا را در اختیار داشته باشد، اما نداند هر ابزار چه کاربردی دارد.

طبیعتاً داشتن ابزار به‌تنهایی باعث ساخت یک میز یا کمد باکیفیت نمی‌شود.

در برنامه‌نویسی نیز دقیقاً همین اتفاق می‌افتد.

OOP ابزارهایی مانند Interface، Inheritance، Polymorphism و Encapsulation را در اختیار ما قرار می‌دهد، اما این Design Pattern است که نشان می‌دهد این ابزارها را چگونه کنار هم قرار دهیم تا یک معماری تمیز و قابل توسعه ایجاد شود.

رابطه Design Pattern با برنامه_نویسی شی_گرا (OOP)

رابطه Design Pattern با SOLID

یکی دیگر از سؤالات رایج این است که آیا ابتدا باید SOLID را یاد بگیریم یا Design Pattern را؟
واقعیت این است که این دو مفهوم رقیب یکدیگر نیستند؛ بلکه یکدیگر را کامل می‌کنند.
SOLID مجموعه‌ای از اصول طراحی نرم‌افزار است که به شما کمک می‌کند کلاس‌های بهتری بنویسید، در حالی که Design Patternها راه‌حل‌هایی هستند که معمولاً همین اصول را در عمل پیاده‌سازی می‌کنند.

SOLID و Design Pattern چه تفاوتی دارند؟

فرض کنید قرار است یک ساختمان چندطبقه بسازید.
قبل از شروع ساخت، مهندس سازه قوانینی را مشخص می‌کند؛ مثلاً ضخامت ستون‌ها چقدر باشد، فاصله تیرها چگونه محاسبه شود و هر بخش چه استانداردی داشته باشد.
اما این قوانین به شما نمی‌گویند آشپزخانه کجا قرار بگیرد یا اتاق خواب چگونه طراحی شود.
در مهندسی نرم‌افزار نیز همین اتفاق رخ می‌دهد.
SOLID مجموعه‌ای از اصول طراحی است که کیفیت کلاس‌ها و ارتباط آن‌ها را بهبود می‌دهد، در حالی که Design Pattern راه‌حل‌هایی آماده برای حل مشکلات رایج طراحی نرم‌افزار ارائه می‌کند.
به زبان ساده، SOLID به شما می‌گوید چگونه فکر کنید و Design Pattern نشان می‌دهد چگونه آن فکر را پیاده‌سازی کنید.

SOLID Design Pattern
مجموعه‌ای از اصول طراحی مجموعه‌ای از راه‌حل‌های طراحی
روی کیفیت کلاس‌ها تمرکز دارد. روی حل مشکلات رایج تمرکز دارد.
قانون ارائه می‌کند. راهکار ارائه می‌کند.
مستقیماً قابل کدنویسی نیست. به صورت مستقیم قابل پیاده‌سازی است.
تقریباً در تمام Patternها استفاده می‌شود. معمولاً برای پیاده‌سازی اصول SOLID به کار می‌رود.

مهم

IMPORTANT
اگر SOLID را بلد باشید، یادگیری Design Pattern بسیار ساده‌تر خواهد شد؛ زیرا متوجه می‌شوید هر Pattern در واقع برای پیاده‌سازی یکی یا چند اصل از SOLID ایجاد شده است.

آیا Design Pattern همیشه بهترین انتخاب است؟

خیر.
یکی از بزرگ‌ترین اشتباهاتی که بسیاری از برنامه‌نویسان پس از یادگیری Design Pattern مرتکب می‌شوند، استفاده از Pattern در هر پروژه و هر مسئله است.
در حالی که خود Patternها برای حل یک مشکل مشخص به وجود آمده‌اند. اگر آن مشکل وجود نداشته باشد، استفاده از Pattern نه‌تنها کمکی نمی‌کند، بلکه پروژه را پیچیده‌تر نیز خواهد کرد.
برای مثال، فرض کنید یک وب‌سایت معرفی شرکت دارید که تنها شامل چند صفحه درباره ما، خدمات و فرم تماس است.
آیا واقعاً لازم است برای چنین پروژه‌ای Factory، Strategy، Observer و ده‌ها Pattern دیگر پیاده‌سازی شوند؟
قطعاً خیر.
در چنین پروژه‌ای استفاده از Pattern فقط تعداد فایل‌ها، کلاس‌ها و پیچیدگی سیستم را افزایش می‌دهد؛ بدون اینکه ارزش خاصی ایجاد کند.

چه زمانی از Design Pattern استفاده کنیم؟

  • پروژه در حال بزرگ شدن است.
  • چند توسعه‌دهنده روی پروژه کار می‌کنند.
  • قابلیت‌های جدید مرتب به سیستم اضافه می‌شوند.
  • نیاز به تست‌نویسی وجود دارد.
  • کدها مرتب تغییر می‌کنند.
  • نگهداری پروژه اهمیت زیادی دارد.

چه زمانی استفاده نکنیم؟

  • پروژه بسیار کوچک است.
  • تنها یک قابلیت ساده وجود دارد.
  • قرار نیست پروژه توسعه پیدا کند.
  • استفاده از Pattern فقط باعث پیچیدگی بیشتر می‌شود.

اشتباه رایج

COMMON MISTAKE
Design Pattern ابزار نمایش مهارت برنامه‌نویس نیست.هدف آن حل یک مشکل واقعی است.اگر مشکلی وجود ندارد، استفاده از Pattern معمولاً تصمیم درستی نیست.

چه زمانی از design pattern استفاده کنیم؟

اشتباهات رایج هنگام یادگیری Design Pattern

اگر از برنامه‌نویسان باتجربه بپرسید سخت‌ترین بخش یادگیری Design Pattern چیست، احتمالاً پاسخ آن‌ها حفظ کردن نام Patternها نخواهد بود؛ بلکه استفاده نادرست از آن‌هاست.
در ادامه چند اشتباه رایج را می‌بینید که تقریباً همه برنامه‌نویسان در ابتدای مسیر مرتکب آن می‌شوند.

  1. حفظ کردن Patternها به جای درک مشکل آن‌هااول مشکل را بشناسید، بعد Pattern را یاد بگیرید؛ نه برعکس.
  2. استفاده از Pattern در همه پروژه‌هاهمه پروژه‌ها به معماری پیچیده نیاز ندارند.
  3. پیچیده کردن کد بدون دلیلگاهی یک کلاس ساده بهترین راه‌حل است.
  4. یادگیری Patternها بدون تسلط بر OOPاگر Interface، Polymorphism و Dependency Injection را ندانید، بسیاری از Patternها فقط چند اسم عجیب خواهند بود.
  5. حفظ کردن ۲۳ Patternهیچ برنامه‌نویس حرفه‌ای همه Patternها را حفظ نمی‌کند؛ او فقط می‌داند برای هر مشکل باید به سراغ کدام راه‌حل برود.

چگونه Design Pattern را یاد بگیریم؟

اگر همین امروز عبارت Design Pattern را در اینترنت جستجو کنید، احتمالاً با ده‌ها ویدئو، کتاب و مقاله روبه‌رو خواهید شد که مستقیماً آموزش Patternهایی مانند Singleton، Factory یا Strategy را آغاز می‌کنند.
مشکل اینجاست که یادگیری Design Pattern با حفظ کردن نام ۲۳ Pattern تقریباً هیچ کمکی به شما نخواهد کرد.
بهترین روش یادگیری این است که هر Pattern را همراه با مشکلی که برای حل آن ایجاد شده است یاد بگیرید.
هر زمان هنگام توسعه پروژه با یک چالش واقعی روبه‌رو شدید، آن زمان بهترین فرصت برای یادگیری Pattern مربوط به همان مسئله است.

ترتیب پیشنهادی یادگیری

اگر برای اولین بار قصد دارید وارد دنیای Design Pattern شوید، پیشنهاد می‌کنیم مسیر زیر را دنبال کنید.

  1. تسلط بر مفاهیم OOP
  2. آشنایی با اصول SOLID
  3. یادگیری Strategy Pattern
  4. یادگیری Factory Method
  5. یادگیری Observer
  6. یادگیری Decorator
  7. یادگیری Adapter
  8. یادگیری Facade
  9. سایر Patternها بر اساس نیاز پروژه

نکته مدرس

TEACHER TIP
هدف شما نباید حفظ کردن ۲۳ Pattern باشد.هدف این است که وقتی با یک مشکل واقعی روبه‌رو می‌شوید، بتوانید Pattern مناسب را تشخیص دهید.

مسیر یادگیری design pattern

جمع‌بندی

Design Pattern مجموعه‌ای از قوانین خشک یا کلاس‌های آماده نیست؛ بلکه تجربه چندین دهه توسعه نرم‌افزار است که در قالب الگوهای استاندارد در اختیار برنامه‌نویسان قرار گرفته است.
اگر بخواهیم فقط یک نکته از این مقاله را به خاطر بسپاریم، آن نکته این است که Design Pattern برای حل مشکل ایجاد شده است، نه برای پیچیده‌تر کردن پروژه.
هر زمان احساس کردید تغییر یک قابلیت باعث تغییر ده‌ها فایل مختلف می‌شود، اضافه کردن ویژگی جدید دشوار شده است یا کلاس‌های پروژه بیش از حد بزرگ و وابسته هستند، احتمالاً زمان آن رسیده است که از یک Design Pattern مناسب استفاده کنید.
از طرف دیگر، اگر پروژه شما کوچک است و با چند کلاس ساده به‌خوبی کار می‌کند، هیچ دلیلی وجود ندارد که صرفاً برای استفاده از Design Pattern، معماری سیستم را پیچیده کنید.
در ادامه این مجموعه، هر یک از Design Patternهای مهم را با پروژه‌های واقعی PHP و Laravel، مثال‌های کاربردی و بررسی مزایا و معایب آن‌ها به صورت کامل آموزش خواهیم داد.


سؤالات متداول

Design Pattern چیست؟

Design Pattern مجموعه‌ای از راه‌حل‌های استاندارد برای حل مشکلات رایج در طراحی نرم‌افزار است. این الگوها کد آماده نیستند، بلکه روش‌های اثبات‌شده‌ای برای طراحی بهتر سیستم هستند.

آیا یادگیری Design Pattern برای همه برنامه‌نویسان ضروری است؟

اگر روی پروژه‌های واقعی، تیمی یا بزرگ کار می‌کنید، بله. اما برای پروژه‌های بسیار کوچک، دانستن مفاهیم پایه OOP معمولاً کافی است.

آیا Design Pattern همان فریم‌ورک است؟

خیر. فریم‌ورک مجموعه‌ای از ابزارها و امکانات آماده است، اما Design Pattern یک الگوی طراحی برای حل یک مشکل مشخص محسوب می‌شود.

یادگیری OOP قبل از Design Pattern ضروری است؟

بله. تقریباً تمام Patternها بر پایه مفاهیمی مانند Interface، Inheritance، Polymorphism و Abstraction پیاده‌سازی می‌شوند.

آیا Laravel از Design Pattern استفاده می‌کند؟

بله. بخش‌های مختلف Laravel مانند Service Container، Events، Facades، Middleware، Queues و Eloquent از Patternهای مختلف استفاده می‌کنند.

چند Design Pattern وجود دارد؟

کتاب GoF بیست‌وسه Pattern کلاسیک را معرفی کرده است، اما امروزه الگوهای طراحی دیگری نیز در معماری نرم‌افزار و فریم‌ورک‌های مدرن استفاده می‌شوند.

کدام Design Pattern را ابتدا یاد بگیریم؟

معمولاً Strategy، Factory Method، Observer، Decorator، Adapter و Facade بهترین نقطه شروع هستند؛ زیرا بیشترین کاربرد را در پروژه‌های واقعی دارند.

آیا باید تمام Design Patternها را حفظ کنیم؟

خیر. مهم‌ترین مهارت، تشخیص مشکل و انتخاب Pattern مناسب است، نه حفظ کردن نام یا ساختار تمام الگوها.

آیا استفاده از Design Pattern باعث افزایش سرعت برنامه می‌شود؟

هدف اصلی Design Pattern افزایش سرعت اجرا نیست؛ بلکه بهبود ساختار کد، توسعه‌پذیری، نگهداری و کاهش وابستگی بین بخش‌های مختلف پروژه است.

آیا پروژه‌های کوچک هم به Design Pattern نیاز دارند؟

همیشه خیر. اگر استفاده از Pattern فقط باعث افزایش تعداد کلاس‌ها و پیچیدگی پروژه شود، بهتر است از راه‌حل‌های ساده‌تر استفاده کنید.

سؤالات رایج مصاحبه‌های استخدامی درباره Design Pattern

اگر قصد دارید به‌عنوان یک برنامه‌نویس PHP، Laravel یا Backend استخدام شوید، احتمال زیادی وجود دارد که در مصاحبه فنی با سؤالاتی درباره Design Pattern روبه‌رو شوید.

Design Pattern چیست؟

Design Pattern مجموعه‌ای از راه‌حل‌های استاندارد برای حل مشکلات تکرارشونده در طراحی نرم‌افزار است. این الگوها کد آماده نیستند، بلکه روش‌های اثبات‌شده‌ای برای طراحی سیستم‌های قابل توسعه و قابل نگهداری محسوب می‌شوند.

Design Pattern چه تفاوتی با Framework دارد؟

Framework مجموعه‌ای از ابزارها، قوانین و ساختارهای آماده برای توسعه نرم‌افزار است، اما Design Pattern تنها یک الگوی طراحی است که می‌تواند در هر زبان برنامه‌نویسی یا فریم‌ورکی پیاده‌سازی شود.

چرا قبل از یادگیری Design Pattern باید OOP را یاد بگیریم؟

زیرا بیشتر Patternها بر پایه مفاهیمی مانند Interface، Abstraction، Polymorphism و Dependency Injection پیاده‌سازی می‌شوند. بدون درک این مفاهیم، فهم بسیاری از الگوهای طراحی دشوار خواهد بود.

تفاوت SOLID و Design Pattern چیست؟

SOLID مجموعه‌ای از اصول طراحی نرم‌افزار است، اما Design Pattern مجموعه‌ای از راه‌حل‌های عملی برای پیاده‌سازی این اصول در مسائل رایج طراحی محسوب می‌شود.

پرکاربردترین Design Patternها در پروژه‌های واقعی کدام‌اند؟

Patternهایی مانند Strategy، Factory Method، Observer، Decorator، Adapter و Facade از پرکاربردترین الگوهای طراحی در پروژه‌های واقعی و فریم‌ورک‌های مدرن هستند.

چه زمانی نباید از Design Pattern استفاده کنیم؟

زمانی که پروژه بسیار کوچک است یا استفاده از Pattern فقط باعث افزایش تعداد کلاس‌ها و پیچیدگی کد می‌شود، بهتر است از راه‌حل‌های ساده‌تر استفاده شود.

Tight Coupling چیست و Design Pattern چگونه آن را کاهش می‌دهد؟

Tight Coupling زمانی رخ می‌دهد که کلاس‌ها به‌شدت به یکدیگر وابسته باشند. بسیاری از Design Patternها با کمک Interface و Abstraction این وابستگی را کاهش می‌دهند و توسعه، تست و نگهداری نرم‌افزار را آسان‌تر می‌کنند.

آیا همه پروژه‌ها به Design Pattern نیاز دارند؟

خیر. Design Pattern زمانی ارزشمند است که برای حل یک مشکل واقعی استفاده شود. استفاده بی‌دلیل از الگوهای طراحی می‌تواند پروژه را پیچیده‌تر کند.

آیا Laravel از Design Pattern استفاده می‌کند؟

بله. بخش‌های مختلف Laravel مانند Service Container، Events، Middleware، Facades، Queues و Eloquent از الگوهای طراحی مختلف برای افزایش انعطاف‌پذیری و توسعه‌پذیری استفاده می‌کنند.

در طراحی یک سیستم پرداخت از چه Design Patternهایی می‌توان استفاده کرد؟

معمولاً از Strategy برای انتخاب درگاه پرداخت، Factory Method برای ایجاد شیء مناسب، Observer برای ارسال اعلان‌ها و Facade برای ساده‌سازی ارتباط با سرویس‌های پرداخت استفاده می‌شود.

مهم‌ترین اشتباه برنامه‌نویسان هنگام استفاده از Design Pattern چیست؟

بزرگ‌ترین اشتباه، استفاده از Pattern بدون وجود یک مشکل واقعی است. ابتدا باید مسئله را شناسایی کرد و سپس الگوی طراحی مناسب را انتخاب نمود.

چگونه در مصاحبه تشخیص دهیم کدام Design Pattern مناسب‌تر است؟

مصاحبه‌کننده معمولاً به دنبال حفظ بودن نام Patternها نیست؛ بلکه می‌خواهد بداند آیا می‌توانید مشکل را تحلیل کرده و مناسب‌ترین راه‌حل را انتخاب کنید. بنابراین ابتدا مسئله را بررسی کنید، سپس دلیل انتخاب Pattern موردنظر را توضیح دهید.

اطلاعات

INFO

آیا می‌توانید به همه این سؤال‌ها پاسخ دهید؟

اگر پاسخ برخی از این پرسش‌ها را نمی‌دانید، نگران نباشید. هدف این مقاله آشنایی با مفاهیم پایه Design Pattern بود.
در ادامه این مجموعه، هر یک از الگوهای طراحی را به‌صورت پروژه‌محور، همراه با مثال‌های واقعی در PHP و Laravel بررسی خواهیم کرد تا علاوه بر آمادگی برای مصاحبه‌های استخدامی، بتوانید از آن‌ها در پروژه‌های واقعی نیز استفاده کنید.

پرسش و پاسخ

اولین دیدگاه رو شما برای ما ارسال و کنید

کاربر گرامی : برای ارسال دیدگاه خود ابتدا باید در سایت لاگین نمایید.