آموزش دیزاین پترن Factory Method در کاتلین

آموزش دیزاین پترن Factory Method در کاتلین

در برنامه نویسی شئ گرا Factory یک شئ (Object) ای است که برای ساختن شئ های دیگر از آن استفاده می شود. همانطور که از نام آن پیدا است نقش یک کارخانه را دارد. این pattern اساس تعدادی دیزاین پترن های طراحی نرم افزار مانند دیزاین پترن Factory Method است. خود واژۀ Factory Method اشاره به تابعی داره که در مقام یک Factory هست.

دیزاین پترن Factory Method یک دیزاین پترن Creational است که از یک interface برای ساخت شئ ها در جایگاه یک superclass استفاده می کنه، اما به subclass ها اجازه می ده تا type اِ شئ ساخته شده رو تغییر بدن.

تعریف : یک کلاس concrete کلاسی است که می تواند نمونه سازی شود، در مقابل کلاس های انتزاعی که نمی توانند.

آموزش دیزاین پترن Factory Method در کاتلین

دیزاین پترن Factory Method

دیزاین پترن Factory Method برای حل چه مشکل یا مشکل هایی بوجود آمد؟ یک مشکل! تصور بکن داری یک اپلیکیشن برای مدیریت ورود و خروج محموله ها و کالا ها می نویسی. اولین نسخۀ اپلیکیشنت فقط می تونه حمل و نقل با کامیون رو انجام بده، بنابرین بخش عمدۀ کدی که نوشتی داخل کلاس Truck (به معنای کامیون) نوشته می شه.

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

دیزاین پترن Factory Method

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

در حال حاضر بیشتر کدهات با کلاس Truck اُخت شده. افزودن Shipe به اپلیکیشن مستلزم ایجاد تغییراتی در کل پایۀ کدت هست.

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

و اینجاست که دیزاین پترن Factory Method میاد به یاریمون؛ دیزاین پترن Factory Method پیشنهاد می کنه که فرمان های ساخت مستقیم شئ (که با استفاده از عملگر new در زبان هایی مثل جاوا صورت می گیره) رو با صدا زدن Factory Method (یعنی تابع Factory) جایگزین کنی.

نگران نباش: مثلا اگر از دیزاین پترن Factory Method در زبان جاوا استفاده کنیم شئ ها هنوز توسط عملگر new ساخته می شن(یعنی new ClassA(); )، در کاتلین هم باز به همان سبک کاتلینی که یک شئ از کلاس می سازیم یک شئ از کلاس خواهیم ساخت (یعنی ClassB() ) ؛ اما از Factory Method فراخوانی می شن، همچنین شئ هایی که توسط Factory Method بازگردانده می شن اغلب به عنوان Product ها شناخته می شن.

عکس زیر یک نمودار UML از نوع Class هست، اگر نمیدونی نمودار UML چیه و نیاز داری باهاش آشنا شی این مقاله رو بخون.

دیزاین پترن Factory Method

subclass ها می تونن کلاس شئ هایی را که با Factory Methodبرگردانده می شن تغییر بدن. نکته : logistics مفهوم مشترک product ها که حمل و نقل باشد را می رسونه.

در نگاه اول این تغییر ممکنه بی معنی بنظر برسه: ما فقط فراخوانی constructor اپلیکیشن رو از یک بخش به یک بخش دیگه منتقل کردیم. با این حال، این را در نظر بگیر: اکنون می تونی Factory Method رو در یک زیرکلاس override کنی و کلاسِ product های ایجاد شده توسط method رو تغییر بدی.

در دیزاین پترن Factory Method با این حال یک محدودیت جزئی وجود داره: زیرکلاس ها ممکنه type های گوناگون product ها رو تنها در صورتی برگردونن که این product ها یک کلاس پایه یا یک interface پایۀ مشترک داشته باشن که ازش ارث بری کرده باشن.

همچنین Factory Method در کلاس پایۀ مشترک باید type ای که قراره return کنه رو به عنوان این interface اعلام کنه.

در واقع ما با مفهوم casting برای cast کردن کلاس های product به interface یا کلاس پایۀ مشترک در دیزاین پترن Factory Method سروکار داریم.

دیزاین پترن Factory Method

همۀ product ها باید از interface یکسان پیروی کنن.

برای مثال کلاس های هر دو کلاس Truck و Ship باید interface اِ Transport را پیاده سازی کنن، که در آن یک متی به نام Deliver اعلام شده.

هر کلاس این متد رو به روشی متفاوت پیاده سازی می کنه: کامیون ها بارها رو از طریق زمینی، کشتی ها بارها رو از طریق دریا تحویل می دهن. Factory Method در کلاس RoadLogistics شئ های کامیون رو بر می گردونه، در حالی که Factory Method در کلاس SeaLogistics کشتی ها رو بر می گردونه.

دیزاین پترن Factory Method

تا زمانی که همۀ کلاس های product یک interface مشترک رو پیاده سازی کنن، می تونی شئ های آنها رو بدون شکستنشون، به کد client منتقل کنی

آن کد که از Factory Method استفاده می کنه (که معمولا client خوانده می شه) تفاوتی میان product های واقعی بازگردونده شده توسط subclass های گوناگون نمی بینه.

client با تمام product ها به عنوان Transport انتزاعی رفتار می کنه. client می دونه که قراره همۀ شئ های حمل و نقل دارای متدِ deliver باشند، اما نحوه عملکرد دقیق آن برای client مهم نیست.

ساختار دیزاین پترن Factory Method

ساختار کلی دیزاین پترن Factory Method را در نمودار UML Class زیر بررسی کن.

دیزاین پترن Factory Method

اینترفیس Product : Product، اینترفیس رو اعلام می کنه که برای همۀ شئ ها مشترکه، با استفاده از کلاس Creator و subclass هایش می شه ساختش.

Concrete Product ها : این کلاس ها پیاده سازی های متفاوتی از اینترفیس Product ارائه می دن.

کلاس Creator : این کلاس اقدام به اعلام Factory Method (یعنی فانکشن کارخانه) که شئ های product جدید رو بر می گردونه، می کنه. بر بنیاد دیزاین پترن Abstract Factory مهمه که نوع return شوندۀ این Method با اینترفیس Product مطابقت داشته باشه.

می توانی Factory Method رو به عنوان Abstract اعلام کنی تا همۀ subclass ها مجبور بشن تا نسخۀ خودشون رو از Method پیاده سازی کنن. به عنوان یک جایگزین، Factory Method اِ پایه می تونه برخی از type های پیش فرض رو برگردونه.

توجه داشته باشید با وجود نام Creator مسئولیت اصلی این کلاس ایجاد شئ نیست. معمولا کلاس Creator از قبل دارای منطق تجاری اصلی مرتبط با product هاست. Factory Method کمک می کنه منطق مذکور از product کلاس های concrete جدا بشه.

Concrete Class ها : در دیزاین پترن Factory Method با استفاده از این کلاس ها می شه Factory Method پایه رو بازنویسی کرد. بنابرین انواع مختلفی از Product رو می تونه برگردونه.

توجه داشته باش که Factory Method همواره نباید شئ های جدید بسازه. او همچنین می تونه شئ های موجود رو از یک cache یا هر چیز دیگری برگردونه.

مثال کد نویسی از دیزاین پترن Factory Method

این مثال نشان می ده که چطور می شه از دیزاین پترن Factory Method برای ایجاد عنصر های رابط کاربری چند سکویی بدون اتصال کد client (کد کلاینت کدیه که از دیزاین پترن Factory Method استفاده می کنه)  به کلاس های رابط کاربری concrete استفاده کرد.

نکته : منظور از چند سکویی اجرا شدن رابط کاربری در چندین سکو از جمله ویندوز، وب، موبایل و… می باشه. جالبه با استفاده از کاتلین می تونی برنامه نویسی multi-platform (چند سکویی) انجام بدی، (?What is Kotlin Multiplatform)

دیزاین پترن Factory Method

مثال از Diallog چند سکویی یا همان multi-platform

کلاس Dialog پایه، از عنصر های متفاوت UI استفاده می کنه برای رندر کردن پنجره هایش استفاده می کنه. در سیستم عامل های مختلف این عنصر ها ممکنه که مقدار کمی متفاوت باشه اما آنها باز باید هم اساس باشن. یک دکمه در ویندوز از نظر اساسی مانند یک دکمه در لینوکس است.

زمانی که دیزاین پترن Factory Method نقشش را بازی می کنه، دیگر نیازی نیست منطق کلاس Dialog رو برای هر سیستم عامل باز نویسی کنی. اگر یک Factory Method رو اعلام کنیم که Button ها رو در کلاس پایۀ Dialog بسازه، در آینده می تونیم subclass ای ایجاد کنیم که دکمه های به سبک سیستم عامل ویندوز رو از Factory Method بر می گردونه.

آنگاه subclass ای که یاد شد از اکثر کد را از کلاس پایه ارث بری خواهد کرد. اما به لطف Factory Method می شه دکمه های شبیه به ویندوز رو روی صفحه نمایش بده.

برای استفاده از دیزاین پترن Abstract Factory واسۀ این موضوع، کلاس Dialog باید با دکمه های Abstract کار بکنه: یک کلاس پایه یا یک interface که همۀ کلاس های دکمۀ concrete ازش پیروی می کنن. بدین ترتیب کد درون Dialog با هر دکمه ای که کار بکنه، کاربردی باقی می مونه.

البته می تونی این رویکرد رو در سایر عناصر رابط رابط کاربری به جز دکمه نیز اعمال کنی. با این روی با هر یک از Factory Method ای که به Dialog می افزونی به مفهوم دیزاین پترن Abstract Factory نزدیک تر می شی. در این مقاله دیزاین پترن Factory Method مورد بررسیمان هست و در یک مقاله جداگانه به دیزاین پترن Abstract Factory خواهیم پرداخت.

import java.util.Scanner

کلاس Creator آن Factory Method رو که باید یک شئ ای از کلاس Product رو بر گردونه، اعلام می کنه. Subclass های کلاس Creator معمولا پیاده سازی
این method رو بر می گردونن.

abstract class Dialog{

کلاس Creator همچنین ممکنه پیاده سازی های پیشفرضی (یعنی مثلا یک یا چند تابع یا property غیر انتزاعی)، برای Factory Method ارائه بده

abstract fun createButton() : Button

توجه داشته باشید که علیرغم نام کلاس Creator که به ساختن اشاره می کنه، مسئولیت اصلیش ساختن اشیاء نیست. به طور معمول دربردارندۀ برخی از منطق های اصلی برنامه است که بر شئ های ساخته و برگردونده شده توسط Factory Method، متکیاند. Subclass ها می توانن غیر مستقیم آن منطق اصلی برنامه رو با override کردن و برگردوندن شئ با یک type متفاوت تغییر بدن.

fun render(){

برای ایجاد یک شئ product باید Factory Method رو صدا بزنی

val okButton = createButton()

اکنون می تونی از product استفاده کنی

okButton.onClick { /* closeDialog */ }
        okButton.render()
    }
}

و در ادامه Creator های concrete، Factory Method رو برای تغییر type مقدار برگشتی override ، product می کنن.

class WindowsDialog : Dialog(){
    override fun createButton(): Button {
        return WindowsButton()
    }
}
class WebDialog() : Dialog(){
    override fun createButton(): Button {
        return HTMLButton()
    }
}
و interface product، عملیات هایی رو که همۀ product های concrete باید پیاده سازی کنن تعریف می کنه.
interface Button {
    fun render()
    fun onClick(f : () -> Unit)
}

و product های concrete هم، پیاده سازی متنوعی از interface product را فراهم می کنن.

class WindowsButton : Button{
    override fun render() {
        //رندر کردن دکمه به سبک ویندوزی
        println("render window button")
    }

    override fun onClick(f: () -> Unit) {
        //نهادن حالت کردن کلیک بومی سیستم عامل ویندوز برای دکمه
    }
}

class HTMLButton : Button{
    override fun render() {
        //بازگرداندن نمایش اچ تی ام ال یک دکمه
        println("render HTML button")
    }

    override fun onClick(f: () -> Unit) {
        //نهادن حالت کردن کلیک بومی مرورگر وب برای دکمه
    }
}

در ادامۀ مثال کنونی دیزاین پترن Factory Method سراغ کلاس Application که از یک تایپ creator متناسب با پیکربندی فعلی یا تنظیمات محیط (یعنی مثلا Windows باشه یا Web) دریافت می کنه می رویم.

class Application{
    lateinit var dialog : Dialog

    fun initialize(){
        val scanner = Scanner(System.`in`)
        val OS = scanner.next()

        when(OS){
            "Windows" -> dialog = WindowsDialog()
            "Web" -> dialog = WebDialog()
            else -> Throwable("خطا! سیستم عامل ناشناخته.")
        }
    }

}

کد سمت client با یک نمونه از concrete creator کار می کنه، هر چند از طریق interface پایه اش. تا زمانی که client با استفاده از interface پایه به کار کردن با creator ادامه می ده، تو می تونی هر یک از subclass های creator رو پاس بدی.

fun main() {
    val app = Application()
    app.initialize()
    app.dialog.render()
}

اگر می خوای به انیمیشن در برنامه نویسی اندروید مسلط بشی و همه نوع انیمیشن دلخواهی توی اندروید ایجاد بکنی دوره موشن لایوت رو یه سر بزن!

قابلیت اجرایی دیزاین پترن Factory Method

  • هنگامی از دیزاین پترن Factory Method استفاده کن که می خوای با استفاده از مجدد از شئ های موجود بجای دوباره از نو ساختن آنها در هر بار؛ در مصرف منابع سیستم صرفه جویی کنی.

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

بیایید بدین فکر کنیم برای استفادۀ مجدد از یک شئ موجود چه کاری باید انجام بشه:

  1. اول باید یکم فضای ذخیره سازی ایجاد کنی تا تمام شئ های ایجاد شده رو دنبال کنی.
  2. هنگامی که یک نفر درخواست یک شئ می کنه، برنامه باید دنبال یک شئ آزاد در داخل مخزن باشه.
  3. …و آنگاه آن رو به کد client برگردونه.
  4. اگر آنجا هیچ شئ آزادی نبود، برنامه باید یکی جدید بسازه(و آن رو به مخزن بیفزونه)

این کلی کد می شه! و همشون در یک جا باید قراربگیرن تا برنامه با کد های تکراری کثیف نشه.

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

بنابرین تو باید یک method منظبت و توانا برای ساخت شئ های جدید و همچنین استفادۀ مجدد کننده از شئ های موجود رو داشته باشی. و دیزاین پترن Factory Method پاسخ تو خواهد بود.

  • زمانی از دیزاین پترن Factory Method استفاده کن که می خواهی به کاربران کتابخانه یا framework خودت راهی برای گسترش component های داخلی آن ارائه کنی
  • زمانی از دیزاین پترن Factory Method استفاده کن که از قبل type ها و وابستگی های دقیق شئ هایی که کدت باید با آنها کار کنه نمیدونی

Factory Method کد ساخت product رو از کدی که دراصل از product استفاده می کنه جدا می کنه. بنابرین گسترش کد ساخت product به طور مستقل از بقیه کدها آسان تره.

روش پیاده سازی دیزاین پترن Factory Method

  1. در گام اول برای پیاده سازی دیزاین پترن Factory Method، اول کاری کن که همۀ product ها از یک interface واحد پیروی کنن. آن interface یاد شده باید method هایی رو که در تمام product ها معنا دار هستند تعریف کنه.
  2. یک Factory Method خالی درون کلاس Creator ایجاد کنید. Type بازگشتی آن method باید با آن interface ای که همۀ product ها از آن پیروی می کنن (یعنی product interface) برابر باشه.
  3. در دیزاین پترن Factory Method، اندرون کد های Creator همۀ reference ها به constructor های product رو، پیدا کن. تا زمانی که کد ایجاد product رو اندرون factory Method استخراج می کنی، یکی یکی آنها رو با فراخوانی به Factory Method جایگزین کن.شاید نیاز باشه برای کنترل تایپ product برگشتی، یک پارامتر موقت به Factory Method بیفزونین.در این نقطه از پیاده سازی دیزاین پترن Factory Method ممکنه، کد بسیار کثیف بچشم بیاد. ممکنه دستورهای از نوع when (که در زبان  جاوا switch گفته می شه) حجیمی داشته باشه که انتخاب می کنن کدام کلاس product رو نمونه سازی کنه. اما جای نگرانی نداره، بزودی این مشکل رو نیز برطرف می کنیم
  4. اکنون، مجموعه ای از subclass های creator برای هر type از product های فهرست شده در Factory Method ایجاد کن. Factory Method رو در subclass ها override کن و بیت های مناسب کد ساخت رو از method پایه استخراج کن.در بهبوبۀ پیاده سازی دیزاین پترن Factory Method اگر type های product بسیار هستند و ایجاد subclass برای همۀ آنها منطقی نیست، می تونین درون subclass ها از پارامتر کنترل که از کلاس پایه هست، در subclass ها دوباره استفاده کنی.
  5. در بهبوبۀ پیاده سازی دیزاین پترن Factory Method اگر type های product بسیار هستند و ایجاد subclass برای همۀ آنها منطقی نیست، می تونین درون subclass ها از پارامتر کنترل که از کلاس پایه هست، در subclass ها دوباره استفاده کنی.برای مثال تصور کن که ارث بری دنباله داری از کلاس ها بدین صورت داری: کلاس Mail همراه با دو subclass که AirMail و GroundMail (به معنای پست هوایی و پست زمینی) هستن.کلاس های ارث بری کننده از Transport هم Plane و Truck و Train (به معناهای هواپیما و کامیون و قطارند) هستن. تا زمانی که کلاس AirMail فقط از شئ های Plane (به معنای هواپیما) استفاده می کنه، GroundMail ممکنه هم با Train و هم با Trunk (به معنای قطار و کمیون) استفاده بکنه.می تونی یک subclass جدید درست بکنی (مثلا با نام TrainMail) برای مدیریت هر دو مورد، اما گزینۀ دیگری وجود داره. کد client (یعنی کدی که از دیزاین پترن Factory Method استفاده می کنه) می تونه یک آرگمان به Factory Method درون کلاس GroundMail بفرسته تا اینکه کدام product رو می خواهد دریافت بکنه کنترل کنه.
  6. اگر بعد از تمام استخراج ها، Factory Method پایه، تهی شد؛ می تونی آن رو Abstract کنی. اگر چیزی بازمانده باشه، می تونی آن رو به عنوان رفتار پیش فرض Method در بیاری.

مزایا و معایب دیزاین پترن Factory Method

مزایای دیزاین پترن Factory Method :

  • از اتصال محکم میان creator و product های concrete می پرهیزی.
  • Single responsibility (مسئولیت واحد) یکی از اصل های SOLID است که در دیزاین پترن Factory Method رعایت می شه. تو می تونی کد ساختن product رو در جایی از برنامه منتقل کنی تا پشتیبانی از کد آسانتر بشه.
  • در دیزاین پترن Factory Method همچنین اصل Open/Closed که یکی دیگر از اصل های SOLID می باشه استوار خواهد بود. Type های جدیدی از product رو بدون شکستن کد client موجود به برنامه معرفی کن.

معایب دیزاین پترن Factory Method :

  • ممکنه کد پیچیده تر بشه زیرا برای پیاده سازی دیزاین پترن Factory Method باید subclass های جدید بسیاری معرفی کنی. بهترین حالت زمانیه که دیزاین پترن Factory Method رو در یک سلسله مراتب موجود از کلاس های سازنده معرفی کنی.

روابط دیزاین پترن Factory Method با دیگر دیزاین پترن ها

  • بسیاری از دیزاین پترن ها با استفاده از دیزاین پترن Factory Method آغاز می شن (پیچیده تر و قابل تنظیم تر با استفاده از subclass ها) و به سمت دیزاین پترن های Prototype، Abstract Factory یا Builder (انعطاف پذیر تر اما پیچیده تر) فرگشت می یابه.
  • کلاس های Abstract Factory معمولا بر بنیاد مجموعه ای از Factory Method ها استوار می شون، اما همچنین بازم می تونی از Prototype برای ساختن method ها در این کلاس ها استفاده کنی.
  • می تونی از دیزاین پترن Factory Method همراه با دیزاین پترن Iterator استفاده کنی تا به مجموعه subclass ها اجازه بدی type های مختلفی از Iterator ها که با مجموعه ها سازگار هستند، برگردونن.
  • دیزاین پترن Prototype بر بنیاد ارث بری نیست بنابرین اشکلات مربوط بدان رو نداره. به دیگر سخن، Prototype به یک مقدار دهی اولیه از شئ شبیه سازی شده نیاز داره. دیزاین پترن Factory Method بر بنیاد ارث بری هست ولی نیاز به مقدار دهی اولیه از شئ شبیه سازی شده نداره.
  • دیزاین پترن Factory Method یک تخصص از Template Method است. در همان زمان، یک Factory Method ممکنه به عنوان مرحله ای در یک Template Method بزرگ عمل بکنه.

پروژۀ کوچکی از دیزاین پترن Factory Method در کاتلین

یک مروری می کنیم: دیزاین پترن Factory Method یک دیزاین پترن Creational هست که مشکل ایجاد شئ های Product رو بدون مشخص کردن کلاس های concrete ی آنها چاره می کنه.

دوست داری به همۀ معماری های رایج برنامه نویسی اندروید مسلط بشی و کلی تکنیک کد نویسی جهان امروز اندروید؟ اینجا رو نگاهی بنداز

Factory Method یک تابعی رو تعریف می کنه که باید بجای صدا زدن مستقیم Constructor کلاس مربوطه (مثلا MyClass(5,”abc”) ) از از آن تابع برای ایجاد شئ ها استفاده کرد.  Subclassها می تونن این Method رو برای تغییر کلاس شئ هایی که ایجاد خواهند شد override کنن.

دیزاین پترن پترن Factory Method زمانی بسیار سودمنده که نیاز به ارائۀ یک سطح بالایی از انعطاف پذیری برای کد خود داریم.

نحوۀ شناسایی دیزاین پترن Factory Method : Factory Method ها می تونن با تابع های ایجادگر که شئ ها را با استفاده از کلاس های concrete می سازن، شناسایی کرد. در حالی که کلاس های concrete در حین ایجاد شئ مورد استفاده قرار می گیرن. نوع برگشتی Factory Method ها معمولا به عنوان یک کلاس انتزاعی یا یک interface اعلام می شه.

در یک پروژه کوچک که حالت ماکت مانند آن رو در بالاتر بررسی کردیم، با موضوع تولید عناصر رابط کاربری چند سکویی به بررسی بیشتر دیزاین پترن Factory Method خواهیم پرداخت.

در این مثال دیزاین پترن Factory Method دکمه ها نقش Product رو بازی می کنن و Dialog ها به عنوان Creator عمل می کنن. Type های گوناگون Dialog ها به Type های عناصر خود نیاز دارن. به همین دلیل برای هر تایپِ Dialog ای یک subclass ایجاد می کنیم و Factory Method های آن رو override می کنیم.

اکنون هر تایپ Dialog ای کلاس دکمۀ مناسب خودش رو پیاده سازی می کنه. Dialog های با interface مشترک Product ها کار می کنه، به همین دلیل هست که کدش پس از آن همه تغییرات همچنان کاربردی باقی می مونه.

package factory_method.buttons

//اینترفیس مشترک برای همۀ دکمه ها
interface Button {
    fun render()
    fun onClick()
}
package factory_method.buttons
//پیاده سازی دکمۀ اچ تی ام ال
class HtmlButton : Button {
    override fun render() {
        println("<button>دکمۀ آزمایشی</button>")
    }

    override fun onClick() {
        println("روی دکمه کلیک شد!")
    }
}
package factory_method.buttons

import java.awt.Color
import java.awt.FlowLayout
import java.awt.Font
import javax.swing.*
import kotlin.properties.Delegates
import kotlin.system.exitProcess

//پیاده سازی دکمۀ ویندوز
class WindowsButton : Button {
    private val panel = JPanel()
    private val frame = JFrame()

    //button = no value!
    private var button: JButton by Delegates.notNull()

    override fun render() {
        frame.defaultCloseOperation = JFrame.EXIT_ON_CLOSE
        val label = JLabel("درود!")
        label.apply {
            isOpaque = true
            background = Color(235, 233, 126)
            font = Font("Dialog", Font.BOLD, 44)
            horizontalAlignment = SwingConstants.CENTER
        }
        panel.layout = FlowLayout(FlowLayout.CENTER)
        frame.contentPane.add(panel)
        panel.add(label)
        onClick()
        panel.add(button)

        frame.setSize(320, 200)
        frame.isVisible = true
        onClick()
    }

    override fun onClick() {
        button = JButton("بستن")
        button.addActionListener {
            frame.isVisible = false
            exitProcess(0)
        }
    }
}
package factory_method.factory

import factory_method.buttons.Button

abstract class Dialog {

    /*
    کلاس فکتوری پایه. توجه داشته باش که فکتوری صرفا یک نقش برای کلاسه
    باید مقداری از هستۀ منطق برنامه رو داشته باشه که به product های مختلفی نیاز داره
    تا ایجاد بشه
    */
    open fun renderWindow() {
        // ... دیگر کدها ...
        val okButton: Button = createButton()
        okButton.render()
    }

    /*
    زیر کلاس ها این متد را به ترتیب باز نویسی خواهند کرد تا شئ های دکمه را مشخص کنند
     */
    abstract fun createButton(): Button
}
package factory_method.factory

import factory_method.buttons.Button
import factory_method.buttons.HtmlButton
//این کلاس دکمه های مختص به اچ تی ام ال رو می سازه
class HtmlDialog : Dialog(){
    override fun createButton(): Button {
        return HtmlButton()
    }
}
package factory_method.factory

import factory_method.buttons.Button
import factory_method.buttons.WindowsButton
//این کلاس دکمه های مختص به ویندوز رو می سازه
class WindowsDialog : Dialog() {
    override fun createButton(): Button {
        return WindowsButton()
    }
}
package factory_method

import factory_method.factory.Dialog
import factory_method.factory.HtmlDialog
import factory_method.factory.WindowsDialog

//در کلاس ذیل همه چیز به هم می پیوندند
class Demo {
    companion object {
        lateinit var dialog : Dialog
        fun main() {
            configure()
            runBusinessLogic()
        }
        //وابسته به پیکر بندی یا تنظیمات محیط
        //concrete factory
        //انتخاب خواهد شد
        private fun configure() {
            dialog = if (System.getProperty("os.name") == "Windows 10") {
                WindowsDialog()
            } else {
                HtmlDialog()
            }
        }
        //همۀ کد های
        //client
        //باید با
        //Factories و Products
        //از طریق
        //Abstract interface
        //کار کنند.
        //در این صورت دیگر مهم نیست با کدام فکتوری کار می کنین و چه جور پروداکتی توسط آن برمیگرد.
        private fun runBusinessLogic() {
            dialog.renderWindow()
        }
    }
}
fun main() {
    Demo.main()
}

 

برای دانلود سورس کد آموزش داده شده از باکس زیر استفاده کنید.
جعبه دانلود فایل۱ فایل
دانلود سورس کد
گزارش خرابی لینک دانلود
فرم گزارش خرابی لینک دانلود
دیدگاه‌ها ۰
ارسال دیدگاه جدید