در تکلیف قبلی کلاس های انتزاعی، رابط ها و مفهوم ترکیب بندی معرفی شدند. نمایندگی رابط (Interface delegation) یک تکنیک پیشرفته است که در آن متدهای یک رابط توسط یک شی کمککننده (یا نماینده) پیاده سازی می شوند، که پس از آن توسط کلاس استفاده می شود. این تکنیک زمانی می تواند کاربردی باشد که از یک رابط در مجموعهای از کلاسهای غیرمرتبط استفاده کنید:شما عملکرد رابط مورد نیاز را به یک کلاس کمک کننده جداگانه اضافه میکنید، و هر کدام از کلاس ها یک نمونه از کلاس کمک کننده را برای پیاده سازی عملکرد استفاده می کنند.
در این مثال، شما از نمایندگی رابط برای اضافه کردن عملکرد به کلاس استفاده میکنید.
گام ۱: یک رابط جدید بسازید
۱. داخل AquariumFish.kt کلاس AquariumFish را حذف کنید. به جای ارث بری از کلاس AquariumFish، کلاس Plecostomus و Shark برای هر کدام از کارهای ماهی ها و رنگ هایشان رابط پیاده سازی می کنند.
۲. یک رابط به نام FishColor بسازید که رنگ را به عنوان یک رشته تعریف می کند.
interface FishColor { val color: String }
۳. کلاس Plecostomus را برای پیاده سازی دو رابط تغییر دهید، FishAction و FishColor شما نیاز دارید تا color از FishColor و ()eat از FishAction را برتری دهید.
class Plecostomus: FishAction, FishColor { override val color = "gold" override fun eat() { println("eat algae") } }
۴. کلاس Shark را تغییر دهید تا دو رابط FishAction و FishColor را به جای ارث بری از AquariumFish پیادهسازی کنند.
class Shark: FishAction, FishColor { override val color = "gray" override fun eat() { println("hunt and eat fish") } }
۵. کد کامل شما باید مانند این باشد:
package example.myapp interface FishAction { fun eat() } interface FishColor { val color: String } class Plecostomus: FishAction, FishColor { override val color = "gold" override fun eat() { println("eat algae") } } class Shark: FishAction, FishColor { override val color = "gray" override fun eat() { println("hunt and eat fish") } }
گام ۲: یک کلاس یگانه بسازید
پس از این، شما بخش نمایندگی را با ساختن یک کلاس کمککننده که FishColor را پیاده سازی می کند خواهید ساخت. شما یک کلاس پایه به نام GoldColor می سازید که FishColor را پیاده سازی می کند - تمام کاری که انجام می دهد این است که بگوید رنگ طلایی است.
ساختن نمونه های متعدد از GoldColor دلیلی ندارد، زیرا همه آنها دقیقاً یک کار را انجام میدهند. پس کاتلین به شما اجازه می دهد یک کلاس تعریف کنید به گونه ای که فقط یک نمونه از آن را با استفاده از کلمه کلیدی object به جای class تعریف کنید. کاتلین آن یک نمونه را میسازد، و آن نمونه توسط نام کلاس ارجاع می شود. سپس همه اشیا دیگر میتوانند از این یک نمونه استفاده کنند - هیچ راهی برای ساختن نمونه های دیگر از این کلاس وجود ندارد. اگر با الگوی کلاس های یگانه آشنا هستید این روش ساخت کلاس یگانه در کاتلین است.
۱. در AquariumFish.kt یک شی برای GoldColor بسازید. رنگ را برتری دهید.
object GoldColor : FishColor { override val color = "gold" }
گام ۳: یک رابط نماینده برای FishColor اضافه کنید
حالا شما آماده استفاده از نمایندگی رابط هستید.
۱. در AquariumFish.kt برتری color از Plecostomus را حذف کنید.
۲. کلاس Plecostomus را برای گرفتن رنگ از GoldColor تغییر دهید. این کار را با اضافه کردن by GoldColor به تعریف کلاس انجام می دهید که نمایندگی می سازد. به جای پیاده سازی FishColor از پیاده سازی ارائه شده توسط GoldColor استفاده کنید. پس هر بار به color دسترسی پیدا می کنید آن به GoldColor تفویض شده است.
class Plecostomus: FishAction, FishColor by GoldColor { override fun eat() { println("eat algae") } }
با کلاسی که داریم همه پلوکوها طلایی خواهند بود اما این ماهی ها در واقع رنگ های مختلفی دارند. شما می توانید با اضافه کردن یک پارامتر سازنده با رنگ پیشفرض GoldColor برای Plecostomus این را مشخص کنید.
۳. کلاس Plecostomus را طوری تغییر دهید که fishColor را با سازندهاش بگیرد، و پیش فرض آن را GoldColor تنظیم کند. نمایندگی را از by GoldColor به by fishColor تغییر دهید.
class Plecostomus(fishColor: FishColor = GoldColor): FishAction, FishColor by fishColor { override fun eat() { println("eat algae") } }
گام ۴: یک رابط نماینده برای FishAction اضافه کنید
به همان روش قبل شما میتوانید از نمایندگی رابط برای FishAction استفاده کنید.
۱. در AquariumFish.kt یک کلاس PrintingFishAction بسازید که FishAction را پیاده سازی می کند، که یک رشته به نام food میگیرد و آنچه ماهی میخورد را چاپ میکند.
class PrintingFishAction(val food: String) : FishAction { override fun eat() { println(food) } }
۲. در کلاس Plecostomus تابع برتری داده شده ()eat را حذف کنید، زیرا آن را با یک نمایندگی جایگزین میکنید.
۳. در تعریف Plecostomus، به FishAction نمایندگی PrintingFishAction بدهید و "eat algae" را به آن پاس دهید.
۴. با این نمایندگی ها هیچ کدی داخل بدنه کلاس Plecostomus وجود ندارد، پس آکولاد {} را حذف کنید، زیرا همه سوارکردنها (override) توسط نمایندگی رابط مدیریت شد.
class Plecostomus (fishColor: FishColor = GoldColor): FishAction by PrintingFishAction("eat algae"), FishColor by fishColor
نمودار پایین کلاس های Shark و Plecostomus را نمایش می دهد اعضای آنها از رابط PrintingFishAction و FishColor تشکیل شدند اما پیادهسازی به آن ها تفویض شد.
نمایندگی رابط قدرتمند است، و شما باید فکر کنید وقتی از یک تابع انتزاعی در زبان دیگر استفاده میکنید چگونه از آنها استفاده کنید. این به شما اجازه میدهد تا از ترکیب بندی برای اضافه کردن رفتارها استفاده کنید، به جای تعداد زیادی زیر کلاس که هر کدام از آنها به شیوه خاصی ساخته میشوند.