سی ام اس نویسسی ام اس نویسیاد بگیر، سیستم خودتو بنویس
مجلهتماس با ما
سی ام اس نویسسی ام اس نویس

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

پیوندهای سریع
محصولات فیزیکیمحصولات دیجیتالدوره‌های آموزشی
سی ام اس نویس
مجله سی ام اس نویستماس با ماپنل کاربری
سی ام اس نویس — تمامی حقوق برای سی ام اس نویس محفوظ است. © 1406 تماس با ما09307711941
Adapter Pattern در PHP چیست؟ آموزش الگوی Adapter با مثال عملی

Adapter Pattern در PHP چیست؟ آموزش الگوی Adapter با مثال عملی

ممدیر کل سیستم ۲۱ شهریور ۱۴۰۵ ۲۰ دقیقه مطالعه ۴ بازدید
Adapter Pattern در PHP چیست؟ آموزش الگوی Adapter با مثال عملی

در پروژه‌های واقعی PHP، معمولاً با سرویس‌ها، کتابخانه‌ها و کلاس‌هایی روبه‌رو می‌شویم که هرکدام Interface یا روش استفاده متفاوتی دارند. برای مثال، ممکن است پروژه ما به متدی با نام send() نیاز داشته باشد، اما سرویس پیامکی که از آن استفاده می‌کنیم متدی با نام sendMessage() ارائه دهد.

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

راه‌حل بهتر استفاده از Adapter Pattern در PHP است.

Adapter Pattern به ما کمک می‌کند Interface یک کلاس موجود را به Interface مورد انتظار برنامه تبدیل کنیم؛ بدون اینکه مجبور باشیم کد اصلی کلاس یا تمام بخش‌های وابسته پروژه را تغییر دهیم.

در این مقاله ابتدا مفهوم Adapter Pattern را بررسی می‌کنیم، سپس با مثال عملی در PHP، روش ساخت Object Adapter و Class Adapter را یاد می‌گیریم. در ادامه نیز نحوه استفاده از Adapter در پروژه‌های Laravel، درگاه‌های پرداخت، سرویس‌های پیامک و کدنویسی مبتنی بر Dependency Injection را بررسی خواهیم کرد.

Adapter Pattern در PHP چیست؟

Adapter Pattern یکی از الگوهای طراحی ساختاری یا Structural Design Patterns است.

وظیفه این الگو، ایجاد یک لایه سازگارکننده بین دو کلاس یا دو سیستم است که Interfaceهای متفاوتی دارند.

به زبان ساده، Adapter مانند یک مبدل عمل می‌کند. برنامه شما با یک Interface مشخص کار می‌کند، اما Adapter درخواست‌ها را به شکلی تبدیل می‌کند که کلاس یا سرویس اصلی بتواند آن‌ها را اجرا کند.

برای مثال، برنامه ما انتظار دارد سرویس ارسال پیامک متدی مانند زیر داشته باشد:


public function send(string $recipient, string $message): bool;

اما سرویس قدیمی پیامک متد زیر را ارائه می‌دهد:


public function sendMessage(string $phone, string $text): bool;

هر دو متد تقریباً یک کار انجام می‌دهند، اما نام پارامترها و نام متد آن‌ها متفاوت است. Adapter این تفاوت را پنهان می‌کند.

مثال ساده برای درک Adapter Pattern

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

در این مثال:

  • دستگاه برقی، کلاس اصلی برنامه است.
  • پریز برق، Interface مورد انتظار است.
  • مبدل برق، Adapter است.
  • تفاوت نوع اتصال، ناسازگاری Interfaceها است.

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

مشکل استفاده مستقیم از کلاس ناسازگار

فرض کنید یک سرویس پیامک قدیمی داریم:


class LegacySmsService

{

    public function sendMessage(

        string $phone,

        string $text

    ): bool {

        // ارسال پیامک از طریق سرویس قدیمی



        return true;

    }

}

حالا در بخش‌های مختلف برنامه مستقیماً از این کلاس استفاده می‌کنیم:


$smsService = new LegacySmsService();



$smsService->sendMessage(

    '09120000000',

    'پیام آزمایشی'

);

در نگاه اول این کد مشکلی ندارد؛ اما با بزرگ‌تر شدن پروژه، وابستگی مستقیم به LegacySmsService دردسرساز می‌شود.

برای مثال:

  • تغییر سرویس پیامک دشوار می‌شود.
  • تست کردن کد سخت‌تر خواهد شد.
  • تمام بخش‌های پروژه به نام متد sendMessage() وابسته می‌شوند.
  • اگر سرویس جدید از متد send() استفاده کند، باید کدهای زیادی را تغییر دهیم.
  • استفاده از چند سرویس پیامکی مختلف پیچیده می‌شود.

برای حل این مشکل ابتدا یک Interface عمومی تعریف می‌کنیم.

تعریف Interface مشترک برای سرویس پیامک


interface NotificationSender

{

    public function send(

        string $recipient,

        string $message

    ): bool;

}

اکنون تمام بخش‌های برنامه باید فقط با NotificationSender کار کنند، نه با یک سرویس پیامکی خاص.

کلاس اصلی ما همچنان همان Interface قدیمی را دارد:


class LegacySmsService

{

    public function sendMessage(

        string $phone,

        string $text

    ): bool {

        return true;

    }

}

این کلاس نمی‌تواند مستقیماً NotificationSender را پیاده‌سازی کند؛ چون متد آن sendMessage() است، در حالی که Interface ما متد send() را مشخص کرده است.

اینجا Adapter وارد عمل می‌شود.

پیاده‌سازی Object Adapter در PHP

در روش Object Adapter، یک شیء از کلاس اصلی را داخل Adapter نگهداری می‌کنیم. سپس Adapter Interface مورد انتظار برنامه را پیاده‌سازی می‌کند.


class SmsAdapter implements NotificationSender

{

    public function __construct(

        private LegacySmsService $service

    ) {

    }



    public function send(

        string $recipient,

        string $message

    ): bool {

        return $this->service->sendMessage(

            $recipient,

            $message

        );

    }

}

در این مثال:

  • SmsAdapter همان Adapter است.
  • NotificationSender Interface مورد انتظار برنامه است.
  • LegacySmsService کلاس قدیمی و ناسازگار است.
  • متد send() درخواست برنامه را به sendMessage() تبدیل می‌کند.

استفاده از SmsAdapter


$legacyService = new LegacySmsService();



$sender = new SmsAdapter($legacyService);



$result = $sender->send(

    '09120000000',

    'سلام، این یک پیام آزمایشی است.'

);



var_dump($result);

از دید برنامه، فقط متد send() استفاده می‌شود. برنامه لازم نیست بداند سرویس واقعی در پشت Adapter چه نامی برای متد ارسال پیامک دارد.

ساختار کلی Object Adapter

ساختار این الگو را می‌توان به شکل زیر خلاصه کرد:


Application

    ↓

NotificationSender Interface

    ↓

SmsAdapter

    ↓

LegacySmsService

برنامه به Interface وابسته است، نه به سرویس قدیمی.

این موضوع باعث می‌شود اگر در آینده سرویس پیامک تغییر کرد، فقط Adapter جدیدی بنویسیم و بخش‌های دیگر برنامه را دست‌نخورده نگه داریم.

چرا Object Adapter انتخاب مناسبی است؟

Object Adapter معمولاً در PHP انتخاب بهتری نسبت به Class Adapter است؛ زیرا از Composition استفاده می‌کند.

در این روش Adapter یک نمونه از کلاس اصلی را دریافت می‌کند:


public function __construct(

    private LegacySmsService $service

) {

}

مزایای این روش عبارت‌اند از:

  • کاهش وابستگی مستقیم به کلاس اصلی
  • امکان استفاده از چند پیاده‌سازی مختلف
  • تست‌پذیری بهتر
  • امکان تزریق وابستگی با Dependency Injection
  • عدم نیاز به تغییر کد کلاس اصلی
  • امکان Adapter کردن کلاس‌هایی که مالک آن‌ها نیستیم

Class Adapter در PHP چیست؟

در روش Class Adapter، Adapter از کلاس اصلی ارث‌بری می‌کند و هم‌زمان Interface مورد انتظار را پیاده‌سازی می‌کند.

مثال:


class SmsClassAdapter

    extends LegacySmsService

    implements NotificationSender

{

    public function send(

        string $recipient,

        string $message

    ): bool {

        return $this->sendMessage(

            $recipient,

            $message

        );

    }

}

استفاده از Class Adapter:


$sender = new SmsClassAdapter();



$sender->send(

    '09120000000',

    'پیام ارسال‌شده با Class Adapter'

);

در این حالت، متد send() در کلاس Adapter قرار دارد و داخل آن متد قدیمی sendMessage() فراخوانی می‌شود.

تفاوت Object Adapter و Class Adapter

ویژگیObject AdapterClass Adapter
روش ارتباطCompositionInheritance
نگهداری نمونه کلاس اصلیبلهخیر
انعطاف‌پذیریبیشترکمتر
تست‌پذیریبهترمعمولاً محدودتر
وابستگی به ساختار کلاس اصلیکمتربیشتر
امکان Adapter کردن چند کلاسساده‌ترمحدودتر
پیشنهاد در پروژه‌های مدرن PHPمعمولاً مناسب‌ترفقط در شرایط خاص

در PHP به دلیل وجود وراثت تک‌کلاسه، Object Adapter معمولاً انعطاف بیشتری دارد. اگر Adapter از یک کلاس ارث‌بری کند، دیگر نمی‌تواند هم‌زمان از کلاس دیگری نیز ارث‌بری کند.

مثال واقعی Adapter برای درگاه پرداخت

یکی از کاربردهای مهم Adapter Pattern در PHP، سازگار کردن درگاه‌های پرداخت مختلف است.

فرض کنید برنامه ما با Interface زیر کار می‌کند:


interface PaymentGateway

{

    public function pay(int $amount): PaymentResult;

}

اما یک درگاه پرداخت قدیمی چنین ساختاری دارد:


class LegacyPaymentGateway

{

    public function requestPayment(

        int $price,

        string $callbackUrl

    ): string {

        return 'https://payment.example.com/start';

    }

}

درگاه قدیمی:

  • از نام requestPayment() استفاده می‌کند.
  • علاوه بر مبلغ، به آدرس بازگشت نیاز دارد.
  • به جای PaymentResult یک رشته برمی‌گرداند.

برای سازگار کردن آن، Adapter می‌سازیم:


class PaymentResult

{

    public function __construct(

        public readonly string $redirectUrl

    ) {

    }

}

سپس Adapter را پیاده‌سازی می‌کنیم:


class PaymentGatewayAdapter implements PaymentGateway

{

    public function __construct(

        private LegacyPaymentGateway $gateway,

        private string $callbackUrl

    ) {

    }



    public function pay(int $amount): PaymentResult

    {

        $redirectUrl = $this->gateway->requestPayment(

            $amount,

            $this->callbackUrl

        );



        return new PaymentResult($redirectUrl);

    }

}

استفاده:


$gateway = new PaymentGatewayAdapter(

    new LegacyPaymentGateway(),

    'https://example.com/payment/callback'

);



$result = $gateway->pay(250000);



echo $result->redirectUrl;

در این مثال، تمام بخش‌های برنامه با PaymentGateway کار می‌کنند و جزئیات درگاه قدیمی فقط داخل Adapter قرار دارد.

استفاده از Adapter برای چند سرویس مختلف

فرض کنید پروژه ما ممکن است از چند سرویس پیامک استفاده کند:

  • سرویس پیامک قدیمی
  • سرویس پیامک جدید
  • سرویس پیامک داخلی شرکت
  • سرویس پیامک خارجی

برای هر سرویس یک Adapter جداگانه می‌سازیم.


class NewSmsService

{

    public function send(

        string $mobile,

        string $content

    ): bool {

        return true;

    }

}

چون این سرویس تقریباً با Interface ما سازگار است، Adapter آن ساده خواهد بود:


class NewSmsAdapter implements NotificationSender

{

    public function __construct(

        private NewSmsService $service

    ) {

    }



    public function send(

        string $recipient,

        string $message

    ): bool {

        return $this->service->send(

            $recipient,

            $message

        );

    }

}

اکنون سرویس اصلی برنامه هیچ تفاوتی بین دو سرویس پیامک احساس نمی‌کند:


function notifyUser(

    NotificationSender $sender,

    string $phone,

    string $message

): bool {

    return $sender->send($phone, $message);

}

استفاده:


notifyUser(

    new SmsAdapter(new LegacySmsService()),

    '09120000000',

    'پیام اول'

);



notifyUser(

    new NewSmsAdapter(new NewSmsService()),

    '09120000000',

    'پیام دوم'

);

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

Adapter Pattern و Dependency Injection

Adapter Pattern در کنار Dependency Injection عملکرد بسیار خوبی دارد.

به جای اینکه داخل کلاس اصلی مستقیماً Adapter را بسازیم، آن را از طریق Constructor دریافت می‌کنیم:


class OrderService

{

    public function __construct(

        private PaymentGateway $gateway

    ) {

    }



    public function payOrder(int $amount): PaymentResult

    {

        return $this->gateway->pay($amount);

    }

}

اکنون OrderService هیچ وابستگی مستقیمی به یک درگاه خاص ندارد.

استفاده:


$gateway = new PaymentGatewayAdapter(

    new LegacyPaymentGateway(),

    'https://example.com/payment/callback'

);



$orderService = new OrderService($gateway);



$result = $orderService->payOrder(500000);

مزیت مهم این روش این است که در زمان تست می‌توانیم یک Gateway جعلی یا Fake به کلاس بدهیم.

استفاده از Adapter در Laravel

در پروژه‌های Laravel معمولاً Interfaceها و پیاده‌سازی‌های آن‌ها در Service Provider به یکدیگر متصل می‌شوند.

فرض کنید Interface زیر را داریم:


interface NotificationSender

{

    public function send(

        string $recipient,

        string $message

    ): bool;

}

و Adapter ما چنین است:


class SmsAdapter implements NotificationSender

{

    public function __construct(

        private LegacySmsService $service

    ) {

    }



    public function send(

        string $recipient,

        string $message

    ): bool {

        return $this->service->sendMessage(

            $recipient,

            $message

        );

    }

}

در Service Provider می‌توانیم Interface را به Adapter متصل کنیم:


use App\Contracts\NotificationSender;

use App\Services\LegacySmsService;

use App\Services\SmsAdapter;



public function register(): void

{

    $this->app->bind(

        NotificationSender::class,

        function () {

            return new SmsAdapter(

                new LegacySmsService()

            );

        }

    );

}

اکنون در هر کلاس Laravel می‌توانیم Interface را تزریق کنیم:


class WelcomeNotification

{

    public function __construct(

        private NotificationSender $sender

    ) {

    }



    public function send(string $phone): bool

    {

        return $this->sender->send(

            $phone,

            'به سایت ما خوش آمدید.'

        );

    }

}

این ساختار باعث می‌شود منطق برنامه به سرویس پیامکی مشخصی وابسته نباشد.

نکته مهم در پروژه واقعی

در پروژه‌های واقعی بهتر است تنظیمات سرویس، کلید API، آدرس سرویس و سایر وابستگی‌ها از فایل تنظیمات Laravel خوانده شوند و مستقیماً داخل Adapter هاردکد نشوند.

همچنین بهتر است Adapter فقط وظیفه سازگار کردن Interface را بر عهده داشته باشد و منطق تجاری مانند محاسبه قیمت، ثبت سفارش یا مدیریت موجودی را انجام ندهد.

Adapter برای APIهای خارجی

یکی دیگر از کاربردهای Adapter Pattern، اتصال APIهای خارجی به ساختار داخلی پروژه است.

فرض کنید برنامه شما انتظار دارد API به شکل زیر کار کند:


interface WeatherProvider

{

    public function getTemperature(string $city): float;

}

اما سرویس خارجی متد زیر را دارد:


class ExternalWeatherApi

{

    public function fetchWeather(

        string $location

    ): array {

        return [

            'temp' => 28.5,

        ];

    }

}

Adapter:


class WeatherApiAdapter implements WeatherProvider

{

    public function __construct(

        private ExternalWeatherApi $api

    ) {

    }



    public function getTemperature(string $city): float

    {

        $response = $this->api->fetchWeather($city);



        return (float) $response['temp'];

    }

}

در این حالت، اگر ساختار پاسخ API خارجی تغییر کند، لازم نیست تمام برنامه را تغییر دهیم. فقط Adapter مربوط به آن API را اصلاح می‌کنیم.

Adapter Pattern در لایه Repository

فرض کنید پروژه شما از یک Repository داخلی استفاده می‌کند:


interface UserRepository

{

    public function findById(int $id): ?array;

}

اما یک کتابخانه قدیمی متدی با نام دیگری دارد:


class LegacyUserStorage

{

    public function getUser(int $userId): ?array

    {

        return [

            'id' => $userId,

            'name' => 'Ali',

        ];

    }

}

Adapter:


class UserRepositoryAdapter implements UserRepository

{

    public function __construct(

        private LegacyUserStorage $storage

    ) {

    }



    public function findById(int $id): ?array

    {

        return $this->storage->getUser($id);

    }

}

اکنون سرویس‌های پروژه فقط به UserRepository وابسته هستند و نمی‌دانند اطلاعات از یک سیستم قدیمی دریافت می‌شود.

تفاوت Adapter و Facade در PHP

Adapter و Facade هر دو می‌توانند یک لایه واسط بین کد ما و یک سیستم دیگر ایجاد کنند، اما هدف آن‌ها متفاوت است.

Adapter

Adapter زمانی استفاده می‌شود که Interface موجود با Interface مورد انتظار ما سازگار نیست.

مثال:


send()  →  sendMessage()

Adapter نام و ساختار درخواست را تبدیل می‌کند.

Facade

Facade برای ساده‌تر کردن کار با یک سیستم پیچیده استفاده می‌شود.

برای مثال، یک سیستم سفارش ممکن است چندین کلاس برای موجودی، پرداخت، ارسال و اعلان داشته باشد. Facade می‌تواند همه این عملیات را پشت یک متد ساده قرار دهد.

خلاصه:

  • Adapter، Interface را تغییر می‌دهد.
  • Facade، استفاده از یک سیستم پیچیده را ساده می‌کند.

تفاوت Adapter و Decorator

Decorator برای افزودن رفتار جدید به یک شیء استفاده می‌شود، در حالی که Adapter برای سازگار کردن Interfaceها به کار می‌رود.

مثال Decorator:


ارسال پیامک

    ↓

ثبت لاگ

    ↓

ارسال پیامک

Decorator ممکن است بدون تغییر Interface، قابلیت لاگ‌گیری یا کش را اضافه کند.

اما Adapter:


send()

    ↓

sendMessage()

در اینجا هدف تبدیل Interface است، نه افزودن قابلیت جدید.

تفاوت Adapter و Factory

Factory مسئول ساخت یا انتخاب یک شیء است.

برای مثال:


$gateway = PaymentGatewayFactory::make('zarinpal');

اما Adapter مسئول سازگار کردن شیئی است که از قبل وجود دارد.

ممکن است در یک پروژه هر دو الگو با هم استفاده شوند:


Factory

    ↓

ساخت Adapter مناسب

    ↓

استفاده از Interface مشترک

Factory تصمیم می‌گیرد کدام Adapter ساخته شود و Adapter Interface سرویس انتخاب‌شده را سازگار می‌کند.

تفاوت Adapter و Bridge

Bridge برای جدا کردن Abstraction از Implementation طراحی می‌شود تا هرکدام بتوانند مستقل از دیگری تغییر کنند.

Adapter معمولاً زمانی استفاده می‌شود که یک کلاس موجود از قبل ساخته شده و Interface آن با نیاز برنامه سازگار نیست.

به صورت خلاصه:

  • Adapter برای سازگار کردن کد موجود است.
  • Bridge برای طراحی مستقل دو بخش از ابتدا استفاده می‌شود.

ارتباط Adapter Pattern با اصول SOLID

Adapter Pattern با چند اصل مهم SOLID ارتباط دارد.

اصل Dependency Inversion

کلاس‌های اصلی برنامه بهتر است به Interface وابسته باشند، نه به پیاده‌سازی‌های خاص.


class OrderService

{

    public function __construct(

        private PaymentGateway $gateway

    ) {

    }

}

در این مثال، OrderService به Interface وابسته است و Adapter وظیفه اتصال آن به سرویس واقعی را بر عهده دارد.

اصل Open/Closed

با استفاده از Adapter می‌توانیم سرویس جدیدی اضافه کنیم، بدون اینکه منطق اصلی برنامه را تغییر دهیم.

اصل Single Responsibility

Adapter باید یک مسئولیت مشخص داشته باشد: تبدیل Interface و انتقال درخواست.

اگر Adapter شروع به مدیریت سفارش، محاسبه کمیسیون یا اجرای منطق پیچیده تجاری کند، بهتر است این وظایف به سرویس‌های جداگانه منتقل شوند.

مدیریت خطا در Adapter

یکی از اشتباهات رایج این است که Adapter تمام خطاهای سرویس اصلی را نادیده بگیرد:


public function send(

    string $recipient,

    string $message

): bool {

    try {

        return $this->service->sendMessage(

            $recipient,

            $message

        );

    } catch (Throwable $exception) {

        return false;

    }

}

این کد ممکن است خطای واقعی را پنهان کند.

در پروژه‌های واقعی بهتر است:

  • خطاها لاگ شوند.
  • Exceptionهای مهم از بین نروند.
  • خطای سرویس خارجی به Exception مناسب پروژه تبدیل شود.
  • مقدار بازگشتی Adapter با قرارداد Interface سازگار باشد.
  • اطلاعات حساس مانند API Key داخل لاگ ثبت نشود.

نمونه بهتر:


public function send(

    string $recipient,

    string $message

): bool {

    try {

        return $this->service->sendMessage(

            $recipient,

            $message

        );

    } catch (Throwable $exception) {

        logger()->error(

            'SMS provider request failed.',

            [

                'recipient' => $recipient,

                'error' => $exception->getMessage(),

            ]

        );



        throw $exception;

    }

}

در این مثال، خطا ثبت می‌شود اما پنهان نمی‌ماند.

تست کردن Adapter در PHP

Adapter باید به‌صورت جداگانه تست شود. هدف تست این است که مطمئن شویم:

  • متد صحیح سرویس اصلی فراخوانی می‌شود.
  • پارامترها درست تبدیل می‌شوند.
  • مقدار بازگشتی صحیح است.
  • خطاها به‌درستی مدیریت می‌شوند.

یک نمونه ساده:


class FakeSmsService

{

    public string $lastPhone = '';



    public string $lastText = '';



    public function sendMessage(

        string $phone,

        string $text

    ): bool {

        $this->lastPhone = $phone;

        $this->lastText = $text;



        return true;

    }

}

تست Adapter:


$fakeService = new FakeSmsService();



$adapter = new SmsAdapter($fakeService);



$adapter->send(

    '09120000000',

    'Test message'

);



assert($fakeService->lastPhone === '09120000000');



assert($fakeService->lastText === 'Test message');

در پروژه‌های Laravel می‌توان همین رفتار را با PHPUnit و Mock انجام داد.

اشتباهات رایج هنگام استفاده از Adapter

۱. قرار دادن منطق تجاری در Adapter

Adapter نباید مسئول محاسبه تخفیف، ثبت سفارش یا مدیریت کیف پول باشد. وظیفه آن فقط سازگار کردن Interface است.

۲. ساخت مستقیم سرویس داخل Adapter

اگر Adapter خودش تمام وابستگی‌ها را بسازد، تست کردن آن دشوار می‌شود.

بهتر است وابستگی‌ها از طریق Constructor تزریق شوند.

۳. وابسته کردن برنامه به کلاس اصلی

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

۴. ناهماهنگ بودن مقدار بازگشتی

اگر Interface مقدار bool برمی‌گرداند، Adapter نباید گاهی رشته و گاهی آرایه برگرداند.

۵. تبدیل نکردن Exceptionها

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

۶. استفاده از Adapter برای هر کلاس ساده

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

مزایای Adapter Pattern در PHP

مهم‌ترین مزایای Adapter Pattern عبارت‌اند از:

  • کاهش وابستگی بین بخش‌های پروژه
  • امکان استفاده از کدهای قدیمی
  • اتصال کتابخانه‌ها و APIهای خارجی
  • امکان تغییر سرویس‌دهنده بدون تغییر منطق اصلی
  • افزایش تست‌پذیری
  • رعایت بهتر Dependency Inversion
  • سازگار کردن چند سرویس با یک Interface مشترک
  • جلوگیری از پخش شدن کدهای مخصوص سرویس خارجی در کل پروژه

معایب Adapter Pattern

این الگو در کنار مزایا، معایبی نیز دارد:

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

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

استفاده از Adapter Pattern در شرایط زیر مناسب است:

  • یک کتابخانه خارجی Interface متفاوتی دارد.
  • می‌خواهیم سرویس پیامک یا پرداخت را تعویض کنیم.
  • پروژه باید از چند Provider مختلف پشتیبانی کند.
  • بخشی از سیستم قدیمی است اما امکان بازنویسی آن وجود ندارد.
  • می‌خواهیم منطق اصلی برنامه به یک سرویس خاص وابسته نباشد.
  • در حال مهاجرت تدریجی از یک سیستم قدیمی به سیستم جدید هستیم.
  • نیاز داریم API خارجی را با قرارداد داخلی پروژه هماهنگ کنیم.

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

اگر کلاس موردنظر از ابتدا با ساختار پروژه سازگار است و هیچ ناسازگاری در Interface وجود ندارد، ساخت Adapter ضرورتی ندارد.

همچنین اگر هدف فقط ساده‌تر کردن چند عملیات پیچیده است، احتمالاً Facade انتخاب مناسب‌تری خواهد بود.

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

جمع‌بندی

Adapter Pattern در PHP یکی از الگوهای طراحی ساختاری مهم است که برای سازگار کردن دو Interface متفاوت استفاده می‌شود.

در این الگو، برنامه با یک Interface استاندارد کار می‌کند و Adapter درخواست‌ها را به ساختار مورد نیاز کلاس یا سرویس اصلی تبدیل می‌کند.

در این مقاله با موارد زیر آشنا شدیم:

  • مفهوم Adapter Pattern در PHP
  • دلیل استفاده از Adapter
  • پیاده‌سازی Object Adapter
  • پیاده‌سازی Class Adapter
  • تفاوت Object Adapter و Class Adapter
  • استفاده از Adapter برای درگاه پرداخت
  • اتصال APIهای خارجی به پروژه
  • استفاده از Adapter در Laravel
  • ارتباط Adapter با Dependency Injection و SOLID
  • تفاوت Adapter با Factory، Facade، Decorator و Bridge
  • تست و مدیریت خطا در Adapter

در پروژه‌های واقعی، مخصوصاً پروژه‌های Laravel، بهتر است منطق اصلی برنامه به Interfaceها وابسته باشد و جزئیات سرویس‌های خارجی داخل Adapterها قرار بگیرد. این روش تغییر سرویس‌ها، تست کد و توسعه آینده پروژه را ساده‌تر می‌کند.


# سوالات متداول درباره Adapter Pattern در PHP

Adapter Pattern در PHP چیست؟

Adapter Pattern یک الگوی طراحی ساختاری است که Interface یک کلاس یا سرویس را به Interface مورد انتظار برنامه تبدیل می‌کند.

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

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

تفاوت Object Adapter و Class Adapter چیست؟

Object Adapter از Composition و نگهداری یک نمونه از کلاس اصلی استفاده می‌کند؛ اما Class Adapter از کلاس اصلی ارث‌بری می‌کند. Object Adapter معمولاً انعطاف‌پذیری بیشتری دارد.

آیا Adapter Pattern در Laravel کاربرد دارد؟

بله. Adapter در Laravel برای سازگار کردن درگاه‌های پرداخت، سرویس‌های پیامک، APIهای خارجی، Repositoryها و سرویس‌های شخص ثالث کاربرد زیادی دارد.

تفاوت Adapter و Facade چیست؟

Adapter Interface ناسازگار را به Interface مورد انتظار تبدیل می‌کند؛ اما Facade استفاده از یک سیستم پیچیده را ساده می‌کند.

آیا Adapter می‌تواند منطق تجاری داشته باشد؟

بهتر است Adapter فقط وظیفه تبدیل Interface و انتقال درخواست را انجام دهد. منطق تجاری باید در Service یا کلاس‌های مسئول آن قرار داشته باشد.

آیا Adapter فقط برای APIهای خارجی استفاده می‌شود؟

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

آیا Adapter با Dependency Injection ارتباط دارد؟

بله. Adapter معمولاً از طریق Dependency Injection به سرویس اصلی متصل می‌شود و Interface مورد انتظار برنامه را در اختیار کلاس‌های دیگر قرار می‌دهد.


نظرات (0)

نظرات پس از تأیید مدیر نمایش داده می‌شوند

هنوز نظری ثبت نشده است؛ اولین نفر باشید!

مقالات مرتبط

ساخت CMS با Laravel 12؛ آموزش ساخت سیستم مدیریت محتوا از صفر

ساخت CMS با Laravel 12؛ آموزش ساخت سیستم مدیریت محتوا از صفر

۲۴ دقیقه
ساخت فروشگاه اینترنتی با Laravel 12؛ آموزش پروژه‌محور از صفر تا طراحی هسته فروشگاه

ساخت فروشگاه اینترنتی با Laravel 12؛ آموزش پروژه‌محور از صفر تا طراحی هسته فروشگاه

۲۲ دقیقه
ساخت Marketplace با Laravel؛ آموزش طراحی فروشگاه چندفروشندگی

ساخت Marketplace با Laravel؛ آموزش طراحی فروشگاه چندفروشندگی

۲۲ دقیقه
مدیریت Queueهای سنگین در Laravel؛ آموزش پردازش کارهای طولانی

مدیریت Queueهای سنگین در Laravel؛ آموزش پردازش کارهای طولانی

۲۲ دقیقه