برنامه نویسی

الگوی طراحی استراتژی با بوت فنری + کاتلین – کد پاک کننده بدون صفحه دیگ

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

چیست؟

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

به عبارت ساده، الگوی استراتژی برای مشخص کردن نحوه انجام کاری با ارائه یک الگوریتم خاص استفاده می شود.
به عنوان مثال، یک استراتژی برای پرداخت می تواند کارت اعتباری، پی پال یا هر ارائه دهنده پرداخت دیگری باشد.

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

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

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

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

نمودار UML الگوی استراتژی

توسط Vanderjoe – Own Work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=60733582

مثال کد

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

  • ما یک برنامه کاربردی با کاربران داریم و باید کدهای تأیید 2FA را برای آنها ارسال کنیم.
  • این برنامه از چندین گزینه ارتباطی برای دریافت کد تأیید پشتیبانی می کند: ایمیل، پیامک، اعلان ها …
  • هر کاربر روش ارتباطی دلخواه خود را انتخاب می کند.

ابتدا رابط استراتژی خود را تعریف می کنیم:

interface VerificationCodeCommunicationStrategy {
    fun sendVerificationCode(user: User, code: String)
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حالا بیایید پیاده سازی های خود را اضافه کنیم:

class EmailCommunicationStrategy : VerificationCodeCommunicationStrategy {
    override fun sendVerificationCode(user: User, code: String) {
        emailClient.sendEmail(user.email, "Verification code", "Your verification is $code.")
    }
}

class SmsCommunicationStrategy : VerificationCodeCommunicationStrategy {
    override fun sendVerificationCode(user: User, code: String) {
        smsClient.sendSMS(user.phone, "Your verification is $code.")
    }
}

// ...
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تنها چیزی که باقی می ماند استفاده از استراتژی است.

enum class CommunicationMethod {
    Email, SMS, Notification
}

class VerificationCodeService {
    val verificationCodeStrategyMap: Map<CommunicationMethod, VerificationCodeCommunicationStrategy> = mapOf(
        CommunicationMethod.Email to EmailCommunicationStrategy(),
        CommunicationMethod.SMS to SmsCommunicationStrategy(),
        // ...
    )

    fun sendVerificationCode(user: User) {
        val code = generateVerificationCode()

        val verificationCodeStrategy = verificationCodeStrategyMap[user.preferredCommunicationMethod]
        verificationCodeStrategy?.sendVerificationCode(user, code)
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ما می توانیم ببینیم که چگونه کد تمیز است و نگهداری آن آسان است. افزودن یک روش ارتباطی جدید آسان خواهد بود و نیازی به تغییر کد موجود به جز افزودن آن به نقشه استراتژی ها نخواهد بود.

ما از الگوی استراتژی a-looot در پروژه های خود استفاده می کنیم. علاوه بر این، مانند همه توسعه دهندگان ما تنبل هستیم و می خواهیم زندگی خود را تا حد امکان آسان کنیم. به همین دلیل است که ما کتابخانه خود را ایجاد کردیم.

مثال کد بدون کتابخانه

بیایید مثال قبلی را برای اجرای معمولی Spring Boot بازبینی کنیم.

هنگام استفاده از الگوی استراتژی در برنامه Spring Boot، زمینه و پیاده سازی استراتژی معمولا اجزای Spring هستند.

ما با استراتژی ها شروع می کنیم:

interface VerificationCodeCommunicationStrategy {
    fun sendVerificationCode(user: User, code: String)
}

@Component
class EmailCommunicationStrategy : VerificationCodeCommunicationStrategy {
    override fun sendVerificationCode(user: User, code: String) {
        emailClient.sendEmail(user.email, "Verification code", "Your verification is $code.")
    }
}

@Component
class SmsCommunicationStrategy : VerificationCodeCommunicationStrategy {
    override fun sendVerificationCode(user: User, code: String) {
        smsClient.sendSMS(user.phone, "Your verification is $code.")
    }
}

// ...
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

رابط ثابت می ماند و پیاده سازی ها را دریافت می کنند @Component حاشیه نویسی

حالا بیایید به سمت سرویس تأیید حرکت کنیم.

@Service
class VerificationCodeServiceImpl(
    private val emailCommunicationStrategy: EmailCommunicationStrategy,
    private val smsCommunicationStrategy: SmsCommunicationStrategy
) {
    private val verificationCodeStrategyMap: Map<CommunicationMethod, VerificationCodeCommunicationStrategy> = mapOf(
        CommunicationMethod.Email to emailCommunicationStrategy,
        CommunicationMethod.SMS to smsCommunicationStrategy
    )

    fun sendVerificationCode(user: User) {
        val code = generateVerificationCode()

        val verificationCodeStrategy = verificationCodeStrategyMap[user.preferredCommunicationMethod]
        verificationCodeStrategy?.sendVerificationCode(user, code)
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

باید پیاده سازی های استراتژی را به سازنده اضافه کنیم و نقشه را به صورت دستی بسازیم. این جالب نیست!

مثال کد با کتابخانه

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

ما با تغییر رابط استراتژی شروع خواهیم کرد.

interface VerificationCodeCommunicationStrategy {
    @get:ComponentMapKey
    val method: CommunicationMethod

    fun sendVerificationCode(user: User, code: String)
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

همانطور که می بینید ما یک فیلد جدید اضافه کرده ایم method با حاشیه نویسی @ComponentMapKey. این به کتابخانه اطلاع می دهد که کدام فیلد کلید نقشه استراتژی است.

بیایید پیاده سازی ها را با توجه به رابط تغییر یافته به روز کنیم.

@Component
class EmailCommunicationStrategy : VerificationCodeCommunicationStrategy {
    override val method = CommunicationMethod.Email

    override fun sendVerificationCode(user: User, code: String) {
        emailClient.sendEmail(user.email, "Verification code", "Your verification is $code.")
    }
}

@Component
class SmsCommunicationStrategy : VerificationCodeCommunicationStrategy {
    override val method = CommunicationMethod.SMS

    override fun sendVerificationCode(user: User, code: String) {
        smsClient.sendSMS(user.phone, "Your verification is $code.")
    }
}

// ...
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حالا بیایید سرویس را برای استفاده از کتابخانه تغییر دهیم.

@Service
class VerificationCodeServiceImpl {
    @ComponentMap
    private lateinit var verificationCodeStrategyMap: Map<CommunicationMethod, VerificationCodeCommunicationStrategy>

    fun sendVerificationCode(user: User) {
        val code = generateVerificationCode()

        val verificationCodeStrategy = verificationCodeStrategyMap[user.preferredCommunicationMethod]
        verificationCodeStrategy?.sendVerificationCode(user, code)
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

خودشه! تنها کاری که باید انجام می‌دادیم این بود که آن را اضافه کنیم @ComponentMap حاشیه نویسی به نقشه استراتژی و voila! نقشه به طور خودکار از اجزای سازنده ساخته می شود. بدون کد دیگ بخار برای افزودن یک استراتژی جدید تنها کاری که باید انجام دهید این است که یک پیاده سازی جدید ایجاد کنید و به صورت “جادویی” به تمام نقشه های استراتژی مرتبط اضافه می شود.

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

اگر مایل به استفاده از آن هستید، می توانید با راهنمای شروع سریع شروع کنید.

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

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا