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

آموزش دیزاین پترن Abstract Factory در کاتلین
در این پست می‌خوانید:

دیزاین پترن Abstract Factory یک دیزاین پترن Creational هست که بهت مکان می ده خانواده هایی از شئ های مرتبط رو بدون مشخص کردن کلاس های concrete ی آنها مشخص کنی.

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

آموزش جامع دیزاین پترن Abstract Factory در کاتلین

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

تصور کن که در حال ایجاد یک شبیه ساز مغازۀ مبلمان هستی. کدت دربرگیرندۀ کلاس هایی هست که نشان دهندۀ:

یک خانواده از محصولات مرتبط مانند : Chair + Sofa + CoffeeTable (به معنا های صندلی، مبل، میز قهوه خوری)

چندین گونه از این خانواده. برای مثال، محصولات Chair + Sofa + CoffeeTable در این انواع موجود هستن: Modern, Victorian, ArtDeco (به معنا های مدرن، ویکتوریایی و آرت دکو)

انواع مبلمان ها

خانواده محصولات و انواع آنها

تو به راهی برای ایجاد شئ های مجزا داری تا با سایر شئ های هم خانواده همخوانی داشته باشه. مشتریان با دریافت مبلمان نامناسب خشمگین می شن.

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

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

همچنین هنگام افزودن محصولات جدید یا خانواده محصولات به برنامه نمی خواهی کد موجود رو تغییر بدی.

فروشندگان مبلمان اغلب کاتالوگ های خود رو بروز می کنن و تو نمی خواهی هربار که این اتفاق میوفته کد اصلیت رو تغییر بدی.

راه حل این مشکل دیزاین پترن Abstract Factory هست. نخستین چیزی که دیزاین پترن Abstract Factory پیشنهاد می کنه اینه که به صراحت interface ها رو برای هر محصول متمایز از خانواده محصول (مانند صندلی، مبل یا میز قهوه) اعلام کنی.

سپس می تونی کاری کنی که همۀ انواع محصولات از آن interface ها پیروی کنن. به عنوان مثال، همۀ انواع صندلی می تونن interface صندلی رو پیاده سازی کنن.

همۀ انواع میز قهوه می تونن interface اِ CoffeeTable رو پیاده سازی کنن.

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

به تصویر UML بالا نگاه کن (اگر نمیدونی UML چیه این مقاله رو ببین)، همۀ انواع یک شئ باید به یک سلسله مراتب کلاس منتقل بشن.

حرکت بعدی اعلان Abstract Factory هست یک interface با فهرستی از method های ایجاد کننده، برای همۀ محصولاتی که بخشی از خانواده محصول هستن(به عنوان مثال، createChair، createSofa و CreateCoffeeTable).

این method ها باید type های Abstract اِ محصول ها رو که با interface هایی که پیشتر استخراج کردیم نشان می دن، return کنن: Chair, Sofa, CoffeeTable و…

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

هر Factory Concrete مربوط به یک نوع محصول خاص است.

اکنون، درمورد انواع محصول چطور؟ برای هرگونه از یک خانواده محصول، ما یک کلاس Factory جداگانه بر پایۀ اینترفیس AbstractFactory ایجاد می کنیم (یعنی همۀ Factory ها از یک interface که نقشش Abstract Factory هست ارث بری می کنن).

Factory کلاسی هست که محصولات از نوع خاصی رو برمی گردونه. برای مثال ModernFurnitureFactory فقط می تونه شئ های ModernChair، ModernSofa  و  ModernCoffeeTableایجاد کنه.

کد client باید از طریق اینترفیس های Abstract با Factory ها و محصولات کار کنه. این بهت امکان رو می ده تایپ Factory ای رو که به کد client می فرستی، و همچنین Type محصولی که Client دریافت می کنه، بدون شکستن کد Client واقعی، تغییر بدی.

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

فرض کن Client یک Factory برای تولید Chair (صندلی) می خواهد. Client نیازی نیست از کلاس Factory آگاه باشه و همچنین مهم نیست چه نوع Chair ای می گیره.

یک صندلی (Chair) چه سبک Modern باشه و چه سبک ویکتوریایی، Client باید با استفاده از interface انتزاعی Chair با همۀ صندلی ها به یک روش رفتار کنه. با این رویکرد، تنها چیزی که Client در مورد صندلی می دونه، آنه که Method اِ sitOn رو به نوعی پیاده سازی می کنه.

همچنین هر نوع Chair ای که return بشه، همیشه با نوع مبل یا میز قهوه خوری تولید شده توسط همان شئ Factory همخوانی داره.

یک چیز دیگر برای روشن شدن مونده: اگر Client فقط در معرض interface های انتزاعی قرار بگیره، چه چیزی شئ های Factory واقعی رو ایجاد می کنه؟ معمولا برنامه در مرحلۀ اولیه سازی (initialization) ، یک شئ Factory ی concrete ایجاد می کنه. درست قبل از آن، برنامه باید بسته به پیکربندی یا تنظیمات محیط، تایپ Factory رو انتخاب کنه.

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

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

Abstract Product ها : اعلان کردن interface ها، برای مجموعه ای از محصولات متمایز اما مرتبط که یک خانواده محصول رو تشکیل می دن.

Concrete Product ها : پیاده سازی های مختلفی از Product های انتزاعی هستن که بر پایۀ Type های گوناگون گروه بندی می شن. هر Abstract Product (صندلی / مبل) باید در تمام Type های داده شده (ویکتوریایی / مدرن) پیاده سازی بشه.

اینترفیس Abstract Factory : مجموعه ای از Method ها رو برای ایجاد هر یک از محصولات انتزاعی (Abstract Products) اعلام می کنه.

Factory Concrete ها : Method های ایجاد Abstract Factory رو پیاده سازی می کنن.

اگرچه Concrete Factory ها Concrete Product ها رو نمونه سازی می کنن، اما امضای Method های ایجاد آنها، باید Abstract Product ها رو return کنه. بدین ترتیب کد Client که از یک Factory استفاده می کنه با نوع خاصی از محصولی که از یک Factory دریافت می کنه همراه نمیشه.

Client می تونه با هر تایپ factory یا product کار کنه، تا زمانی که با شئ های آنها از طریق Abstract Interface ها ارتباط برقرار کنه.

بررسی دیزاین پترن Abstract Factory در کد نویسی

در این مثال دیزاین پترن Abstract Factory نشان داده می شه که چطوری می توان از دیزاین پترن Abstract Factory برای ایجاد عنصر های رابط کاربری چند سکویی بدون وصل شدن کد Client به کلاس های concrete ی رابط کاربری استفاده کرد. در حین سازگار نگهداری تمام عنصر های رابط کاربری ایجاد شده با یک سیستم عامل انتخاب شده.

در مقاله های پیشین موضوع همین مثال رو برای دیزاین پترن Factory Method بررسی کرده بودیم.

دیزاین پترن Abstract Factory همراه با مثال

مثال کلاس های رابط کاربری چند سکویی

در مثال کنونی دیزاین پترن Abstract Factory ؛ انتظار می ره که همان عنصر های رابط کاربری در یک برنامۀ چند سکویی (multi-platform) به صورت مشابه رفتار کنن، اما در سیستم عامل های مختلف کمی متفاوت به نظر می رسه.

افزون بر این، این وظیفۀ توهه که مطمئن بشی عنصر های رابط کاربری با سبک سیستم عامل فعلی همخوانی دارن. زمانی که برنامه رو در سیستم عامل ویندوز اجرا می کنی، انتظار نداری کنترل های سیستم عامل Mac رو رندر کنه.

اینترفیس Abstract Factory مجموعه ای از Method های ایجاد رو اعلام می کنه که کد Client میتونه برای تولید انواع مختلف UI ازشان استفاده کنه. Factory های concrete با سیستم عامل های خاصی همخوانی دارن و عنصر های UI ای رو ایجاد می کنن که با آن سیستم عامل همخوانی داره.

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

با این رویکرد، کد Client تا زمانی که با این شئ ها از طریق Abstract Interface های آنها کار می کنه، به کلاس های concrete ی Factory ها و عنصر های رابط کاربری وابستگی نداره.

این همچنین به کد Client اجازه می ده از Factory ها یا عنصرهای رابط کاربری دیگری که ممکنه در آینده اضافه کنی پشتیبانی کنه (مثلا Button یک Factory هست و کلاس های MacButton و WindowsButton عنصر های رابط کاربری هستن که کلاس های concrete می باشن).

در نتیجه هربار که تنوع جدیدی از عنصر های رابط کاربری رو به برنامه ات می افزونی، نیازی به تغییر کد Client نداری. تو فقط باید یک کلاس Factory جدید ایجاد کنی که این عنصر ها رو تولید کنه و کمی کد اولیۀ برنامه رو تغییر بدی تا در صورت لزوم ار آن کلاس استفاده کنی.

مثال کد دیزاین پترن Abstract Factory :

//Abstract Factory interface
//مجموعه ای از متد ها رو اعلام می کنه که محصولات انتزاعی مختلف
//رو بر می گردونن. این محصولات یک خانواده نامیده می شن و با
//یک موضوع یا یک مفهوم سطح بالا مرتبط هستن
//محصولات یک خانواده معمولا می تونن با یکدیگر همکاری کنن
//یک خانواده از محصولات ممکنه تایپ های مختلفی داشته باشن
//اما محصولات یک تایپ با محصولاتِ یک تایپ دیگر ناسازگار هستن
interface GUIFactory{
    fun createButton() : Button
    fun createCheckbox() : Checkbox
}
//Concrete Factories
//یک خانواده از محصولات رو تولید می کنن که به یک تایپ واحد تعلق داره
//فکتوری تضمین می کنه که محصول های حاصل با هم سازگار هستن
//سیگنیچر یا امضا های متد های
//Concrete Factory
//یک محصول انتزاعی بر می گردونن، حال آنکه در داخل متد یک محصولِ
//Concrete
//نمونه سازی می شه
class WinFactory : GUIFactory{
    override fun createButton(): Button {
        return WinButton()
    }

    override fun createCheckbox(): Checkbox {
        return WinCheckbox()
    }
}

//هر
//Concrete Factory
//دارای یک نوع محصول مربوطه هست
class MacFactory : GUIFactory{
    override fun createButton(): Button {
        return MacButton()
    }

    override fun createCheckbox(): Checkbox {
        return MacCheckbox()
    }
}

//هر محصول متمایز از یک خانواده محصول باید یک
//Base Interface
//داشته باشه. همۀ انواع محصول باید این
//Interface
//رو پیاده سازی کنن
interface Button{
    fun paint()
}

//Concrete product
//ها توسط
//concrete factory
//های مربوطه ایجاد میشن
class WinButton : Button{
    override fun paint() {
        println("winButton")
        //یک دکمه به سبک ویندوز رندر کن
    }
}
class MacButton : Button{
    override fun paint() {
        println("macButton")
        //یک دکمه به سبک سیستم عامل مک رندر کن
    }
}

//اینجا اینترفیس پایۀ یک محصول دیگر هست. همۀ محصولات می تونن با
//یکدیگر تعامل برقرار کنن، اما تعامل مناسب تنها میان محصولات یک نوع
//Concrete
//امکان پذیره
interface Checkbox{
    fun paint()
}
class WinCheckbox : Checkbox{
    override fun paint() {
        println("winCheckbox")
        //یک چکباکس به سبک ویندوز رندر کن
    }
}
class MacCheckbox : Checkbox{
    override fun paint() {
        println("macCheckbox")
        //یک چکباکس به سبک سیستم عامل مک رندر کن
    }
}

//کد کلاینت فقط از طریق تایپ های انتزاعی با فکتوری ها و محصولات
//کار می کنه:
//GUIFactory, Button ,  Checkbox
//و سرانجام این بهت این امکان رو میده تا هر فکتوری یا محصول زیرکلاسی رو بدون
//شکستن کد کلاینت به آن منتقل کنی
class Application(private val factory : GUIFactory){
    private var button : Button? = null
    private var checkbox : Checkbox? = null
    fun createUI(){
        this.button = factory.createButton()
        this.checkbox = factory.createCheckbox()

        println("Ui Created!")
    }
    fun paint(){
        button?.paint()
        checkbox?.paint()
    }
}
//برنامه تایپ فکتوری رو بسته به متن وارد شده توسط کاربر انتخاب می کنه
//در حالت عینی تر می تونی کدی بنویسی که نوع سیستم عاملت
//رو بخونه و دیگه نیازی نباشه توی ورودی نوع سیستم عامل رو بخونی
//فکتوری متناسب با سیستم عامل در زمان اجرا و معمولا مرحلۀ اولیه سازی
//ایجاد می شه
class ApplicationConfigurator{
    fun main(){
        val factory : GUIFactory? = when(readln()){
            "Windows" -> WinFactory()
            "Mac" -> MacFactory()
            else -> null
        }
        factory?.let {
            val app = Application(it)
            app.apply {
                createUI()
                paint()
            }
        } ?: run{
            println("سیستم عامل نامعتبر است!")
        }
    }
}
fun main() {
    ApplicationConfigurator().main()
}

کجا از دیزاین پترن Abstract Factory استفاده کنیم؟

  • کجا از دیزاین پترن Abstract Factory استفاده کنیم؟ از دیزاین پترن Abstract Factory زمانی استفاده کن که کدت باید با خانواده های مختلف از محصولات مرتبط با هم، کار کنه، اما نمی خوای کدت به کلاس های concrete ی آن محصولات وابستگی داشته باشه – ممکنه از قبل ناشناخته باشن یا فقط بخواهی امکان توسعه پذیری در آینده رو فراهم کنی.

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

  • پیاده سازی دیزاین پترن Abstract Factory رو زمانی در نظر بگیر که کلاسی با مجموعه ای از Factory Method ها داری که مسئولیت اصلی اش رو محو می کنه.

در یک برنامۀ خوب طراحی شده، هر کلاس فقط مسئول یک چیز هست. هنگامی که یک کلاس با چندین نوع محصول سر و کار داره، ممکنه ارزش استخراج factory method در یک کلاس کارخانه ای مستقل یا پیاده سازی کامل دیزاین پترن Abstract Factory داشته باشه.

چگونه دیزاین پترن Abstract Factory رو پیاده سازی کنیم؟

  1. ماتریسی از Type های محصولات متمایز در برابر انواع این محصولات رو ترسیم کن
  2. Interface های محصول انتزاعی رو برای همه Type های محصول اعلام کن. سپس تمام کلاس های محصول concrete رو وادار به پیاده سازی این interface ها کن
  3. اینترفیس Abstract Factory رو با مجموعه ای از method های ایجاد برای همۀ محصولات انتزاعی اعلام کن
  4. مجموعه ای از کلاس های Factory ی concrete، یکی برای هر نوع محصول پیاده سازی کن
  5. کد راه اندازی (initialization) فکتوری رو در جایی از برنامه ایجاد کن. factory بسته به پیکربندی برنامه یا محیط فعلی، باید یکی از کلاس های concrete factory رو پیاده سازی کنه. این شئ factory رو به تمام کلاس هایی که محصولات رو می سازن منتقل کن.
  6. از طریق کد پویش کن و فراخوانی های مستقیم constructor های محصولات رو پیدا کن. آنها رو با فراخوانی متد ایجاد کنندۀ مناسب در یک شو factory جایگزین کن.

برای درک بهتر دیزاین پترن Abstract Factory لازمه سورس کد پروژۀ پیوست این مقاله رو بررسی کنی.

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

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

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

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

  • ممکنه کد پیچیده تر از آن چیزی که باید باشه، باشه. زیرا بسیاری از interface ها و کلاس های جدید همراه با دیزاین پترن Abstract Factory معرفی می شن.

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

بسیاری از دیزاین پترن ها با دیزاین پترن Factory Method آغاز می شن(پیچیده تر و قابل تنظیم تر از وسیلۀ زیر کلاس ها)، و به سوی دیزاین پترن Abstract Factory و Prototype یا Builder فرگشت می یابن(منعطف تر اما پیچیده تر).

  • دیزاین پترن Builder بر روی ایجاد شئ های پیچیده به صورت گام به گام متمرکز هست، دیزاین پترن Abstract Factory مختص ایجاد خانواده ای از شئ های به هم مرتبط هست.
  • دیزاین پترن Abstract Factory بلافاصله محصول رو return می کنه، در حالی که دیزاین پترن Builder بهت امکان می ده قبل از واکشی محصول، مراحل ساخت و ساز اضافی رو اجرا کنی.
  • کلاس های دیزاین پترن Abstract Factory اغلب بر بنیاد مجموعه ای از Factory Method هایند، اما تو همچنین می تونی از Prototype برای ترکیب متد های این کلاس ها استفاده کنی.
  • دیزاین پترن Abstract Factory همچنین می تونه به عنوان جایگزینی برای دیزاین پترن Facade عمل بکنه، برای هنگامی که می خواهی فقط نحوۀ ایجاد شئ های زیرسیستم رو از کد client پنهان کنی
  • تو می تونی از دیزاین پترن Abstract Factory در کنار دیزاین پترن Bridge استفاده کنی. این جفت شدن زمانی سودمند هست که برخی از انتزاعات تعریف شده توسط Bridge فقط می تونن با پیاده سازی های خاص کار کنن. در این مورد، Abstract Factory می تونه این روابط رو کپسوله سازی کنه و پیچیدگی رو از کد client پنهان کنه.
  • دیزاین پترن Abstract Factory و Builder و Prototype می تونن به عنوان Singleton پیاده سازی بشن

یک پروژه کوچک از دیزاین پترن Abstract Factory در کاتلین

چنانکه یاد شد دیزاین پترن Abstract Factory یک دیزاین پترن creational هست که مشکل ایجاد کل خانواده های محصول رو بدون مشخص کردن کلاس های concreteی آنها حل می کنه

دیزاین پترن Abstract Factory یک interface برای ایجاد همۀ محصولات متمایز تعریف می کنه، اما ایجاد محصول واقعی رو به کلاس های factory ی concrete واگذار  می کنه. هر نوع کارخانه با تنوع محصول خاصی مطابقت داره (مثلا کارخانه مبلمان ویکتوریایی، مبل ها و میز قهوه خوری ها و صندلی های ویکتوریایی رو به عنوان تنوع محصولات داره).

کد Client بجای ایجاد مستقیم محصولات با فراخواندن constructor ، متد های ایجاد یک شئ factory رو فراخوانی می کنه. از آنجایی که یک factory مربوط به یک نوع محصول یکتا هست (مثلا ویکتوریایی برای مبلمان)، همۀ محصولات آن سازگار خواهند بود(یعنی صندلی ها و مبلمان و میز های قهوه خوری).

کد client با Factory ها و Product ها فقط از طریق interface های انتراعی آنها کار می کنه. این به کد client اجازه می ده تا با هر نوع محصولی که توسط شئ کارخانه ایجاد شده کار بکنه. تو فقط یک کلاس factory concrete ایجاد می کنی و آن رو به کد client می فرستی.

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

پروژه ای با استفاده از از دیزاین پترن Abstract Factory :

دیزاین پترن Abstarct Factory بسیار رایج است. بسیاری از framework ها و کتابخانه ها از آن برای فراهم سازی راهی برای گسترش و سفارشی سازی اجزای استاندارد خود استفاده می کنن.

خانواده ای از اجزای رابط کاربری چند سکویی و محصولات آنها، موضوع محور پروژۀ کوچک ما خواهد بود:

در این پروژۀ کوچک دکمه ها و checkbox ها محصولات یا همان product های ما هستند. آنها دو گونه هستن : MacOS و ویندوز

Abstract Factory (کارخانۀ انتزاعی) یک interface برای ایجاد دکمه ها و چکباکس ها. دو factory concrete وجود داره که هر دو محصول رو در یک نوع، return می کنن.

کد client با کارخانه ها و محصولات با استفاده از interface های انتزاعی کار می کنه. این باعث می شه که کد client یکسان با انواع مختلفی از محصولات، وابسته به type شئ factory کار کنه.

Button :

package buttons

/*
    Abstract Factory
    فرض می کنه که تو چند خانواده از محصولات که به دو کلاس
    ارث بری (دکمه و چکباکس) ساختار یافته شده اند رو داری
    همۀ محصولات یک خانواده از یک اینترفیس مشترک پیروی می کنن
*/
//این یک اینترفیس مشترک برای خانوادۀ دکمه ها هست
interface Button {
    fun paint()
}

 

MacOSButton : 

package buttons

/*
    همۀ خانواده های محصولات نوع های یکسان دارن (یعنی سیستم عامل های مک و ویندوز)

    این یک نوع دکمۀ سیستم عامل مک هست
*/
class MacOSButton : Button {
    override fun paint() {
        println("تو دکمۀ سیستم عامل مک رو ساختی")
    }
}

 

WindowsButton :

package buttons
/*
    همۀ خانواده های محصولات نوع های یکسان دارن (یعنی سیستم عامل های مک و ویندوز)

    این یک نوع دکمۀ سیستم عامل ویندوز هست
*/
class WindowsButton : Button{
    override fun paint() {
        println("تو دکمۀ سیستم عامل ویندوز رو ساختی")
    }
}

 

Checkbox :

package checkbox

//چکباکس دومین خانواده محصول هست، یک محصول مانند دکمه می باشد
interface Checkbox {
    fun paint()
}

 

MacOSCheckbox :

package checkbox

class MacOSCheckbox : Checkbox{
    override fun paint() {
        println("تو یک چکباکس سیستم عامل مک رو ساختی")
    }
}

 

WindowsCheckbox :

package checkbox

//همۀ خانواده های محصولات نوع های یکسان دارن (یعنی سیستم عامل های مک و ویندوز)
class WindowsCheckbox : Checkbox{
    override fun paint() {
        println("تو یک چکباکس سیستم عامل ویندوز رو ساختی")
    }
}

 

GUIFactory :

package factories

import buttons.Button
import checkbox.Checkbox

//Abstract Factory
//دربارۀ تمام تایپ های انتزاعی محصولات آگاه هست
interface GUIFactory {
    fun createButton() : Button
    fun createCheckbox() : Checkbox
}

 

MacOSFactory :

package factories

import buttons.Button
import buttons.WindowsButton
import checkbox.Checkbox
import checkbox.MacOSCheckbox

//هر
//concrete factory
//از فکتوری پایه ارث بری می کنه و برای ساخت محصولات یک نوع خاص
//مسئول هست
class MacOSFactory : GUIFactory {
    override fun createButton(): Button {
        return WindowsButton()
    }

    override fun createCheckbox(): Checkbox {
        return MacOSCheckbox()
    }
}

 

WindowsFactory :

package factories

import buttons.Button
import buttons.WindowsButton
import checkbox.Checkbox
import checkbox.WindowsCheckbox

//هر
//concrete factory
//از فکتوری پایه ارث بری می کنه و برای ساخت محصولات یک نوع خاص
//مسئول هست
class WindowsFactory : GUIFactory{
    override fun createButton(): Button {
        return WindowsButton()
    }

    override fun createCheckbox(): Checkbox {
        return WindowsCheckbox()
    }
}

 

Application :

package app

import buttons.Button
import checkbox.Checkbox
import factories.GUIFactory

//کاربران فکتوری اهمیتی نمی دن از کدام
//concrete factory
//استفاده می کنن زیرا آنها با کارخانه ها
//و محصولات از طریق اینترفیس های انتزاعی کار می کنن

class Application(factory: GUIFactory) {
    private var checkbox : Checkbox
    private var button : Button

    init {
        checkbox = factory.createCheckbox()
        button = factory.createButton()
    }

    fun paint(){
        button.paint()
        checkbox.paint()
    }
}

 

Demo :

import factories.GUIFactory
import factories.MacOSFactory
import factories.WindowsFactory
import app.Application
import java.util.*

//کلاس دمو، همچیز در اینجا به یکدیگر می پیوندد
object Demo {
    //اپلیکیشن تایپ های فکتوری ها رو جمع آوری می کنه و
    //در زمان اجرا فکتوری رو می سازه
    //(بسته به پیکر بندی یا متغییر های محیطی)
    private fun configureApplication() : Application{
        val app : Application
        val factory : GUIFactory

        val osName = System.getProperty("os.name").lowercase(Locale.getDefault())
        factory = when (osName){
            "mac" -> MacOSFactory()
            else -> WindowsFactory()
        }

        app = Application(factory)
        return app
    }

    fun main(){
        val app : Application = configureApplication()
        app.paint()
    }
}

 

Main :

fun main() {
    Demo.main()
}

 

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

دیزاین پترن Abstract Factory در مهندسی نرم افزار طراحی ای است که راهی برای ایجاد خانواده ای از شئ های مرتبط به هم بدون تحمیل کردن کلاس های concrete آنها، بوسلیۀ کپسوله سازی گروهی از Factory های مجزا که دارای یک موضوع مشترک هستند و بدون مشخص کردن کلاس های concrete ی آنها، فراهم می کند(کپسوله سازی یادآور همان مفهوم encapsulation است).

دیزاین پترن Abstract Factory جزئیات پیاده سازی مجموعه ای از شئ ها را از استفادۀ عمومی آنها جدا می کند و بر ترکیب شئ تکیه می کند، زیرا ایجاد شئ در Method هایی که در interface اِ Factory اجرا می شود ، اجرا می گردد.

دیزاین پترن Abstract Factory یکی از 23 دیزاین پترن توصیف شده در کتاب Design Patterns در سال 1994 است (کتاب Design Patterns 1994 در اینجا). ممکن است برای حل مشکلاتی مانند زیر استفاده شود:

  1. چگونه یک برنامه کاربردی (Application) می تواند مستقل از نحوۀ ایجاد شئ های مورد نیاز خود باشد؟
  2. چگونه یک کلاس می تواند مستقل از نحوۀ ایجاد شئ های آن باشد؟
  3. چگونه می توان خانواده هایی از شئ های مرتبط یا وابسته ایجاد کرد؟

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

Factory مکان یک concrete class در کد است که شئ ها در آن ساخته می شوند. پیاده سازی این دیزاین پترن باعث می شود ایجاد شئ ها را از استفادۀ آنها جدا شود و خانواده هایی از شئ های مرتبط را بدون وابستگی به کلاس های concrete شان، ایجاد کند.

دیزاین پترن Abstract Factory نحوۀ حل چنین مشکلاتی را توضیح می دهد:

  • با تعریف و پیاده سازی یک interface برای ایجاد شئ ها، ایجاد شئ را در یک شئ جداگانه (Factory) کپسوله سازی کنید.
  • بجای ایجاد مستقیم شئ ها، ایجاد شئ را به یک شئ Factory واگذار کنید.

دیزاین پترن Abstract Factory یک کلاس را مستقل از شیوۀ ایجاد شئ هایش می کند. یک کلاس ممکن است با یک شئ Factory پیکربندی گردد که از آن برای ایجاد شئ ها استفاده می کند و شئ Factory در زمان اجرا می تواند تعویض شود.

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

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