در مواقعی ما نیاز داریم تنها یک instance از شئ یا مقادیر مورد نظرمون داشته باشیم ، در چنین مواقعی الگوی Singleton راه چاره ماست ، این الگو در جاوا به صورتهای مختلف پیاده سازی میشه ولی در کاتلین به شدت راحت تره ، پیاده سازی های گوناگون اون رو با هم بررسی میکنیم :
شی مورد نظرمون رو به صورت static (وقتی مقدار به صورت static تعریف بشه فقط یک instance در تمامی برنامه از اون وجود داره) به این شکل تعریف میکنیم :
به این صورت هست که هر زمانی که ما هر تابع و پارامتری رو از کلاس SingletonClass صدا بزنیم ، instance مورد نظرمون ساخته میشه ، این از نظرهایی بد میشه ، چون مثلا زمانی که ما به جای SINGLE_INSTANCE دو تا پارامتر داشته باشیم هر کدوم رو که صدا بزنیم اون یکی هم درون کلاس ساخته میشه و حافظه بیخودی بهش اختصاص داده میشه ، برای جلوگیری از این قضیه میتونیم به صورت lazy پیاده سازی کنیم و میشه حالت دوم :
ولی همین حالت هم یک مشکلی داره ، فرض کنید برنامهای نوشتید که از دو تا Thread مختلف قراره به این کلاس دسترسی داشته باشه ، اون وقت این برنامه ممکنه هم زمان در حالتی که هنوز instance ما null هست بیاد اون رو مقداردهی کنه و دوباره مقداردهی بشه (یعنی هر دو Thread با هم در یک زمان به شرط null بودن میرسن و مساله پیش میاد) برای حل این مشکل حالت سوم رو ایجاد میکنن :
کلمه کلیدی synchronized مشکل هم زمانی رو برطرف میکنه ، در واقع وقتی شما از این عبارت استفاده میکنید همزمان دو Thread نمیتونن به قسمت مربوطه ورود پیدا کنن و یکیشون lock یا قفل میشه ، زمانی که کار Thread اول تموم شد اجازه ورود به Thread دوم داده میشه ، و دوباره درون عبارت synchronized شده شرط null چک میشه که اگه Thread اول اون رو از حالت null درآورد دیگه Thread دوم کاری انجام نده ، حالت چهارم زمانیه که شما کلاسِ static رو هم دخیل کنید :
وقتی شما کلاسی رو به صورت static داخل کلاس دیگه بیارید تا زمانی که متد و تابعی از کلاس بیرونی (SingletonClass) اون رو صدا نزنه داخل حافظه بارگذاری نمیشه (اما در پارامتر به صورت static هر زمان که هر متد یا تابعی از کلاس بیرونی صدا زده بشه باقی پارامترهای static هم در حافظه بارگذاری میشن) ، در این حالت قضیه همزمانی Thread ها یه Thread safe بودن هم رعایت شده (جاوا این کار رو خودش انجام میده) ، آخرین حالت استفاده از Enum هاست :
در الگوی Singleton شما باید constructor رو به صورت private دربیارید و متدها و توابعی مثل getIntance رو برای دسترسی به مقادیر مورد نظرتون بسازید ، تمامی این مسائل در Enum ها رعایت شده ، شما نمیتونید از Enum شئ بگیرید و میتونید به SINGLE_INSTANCE با صدازدنش دسترسی داشته باشید .
اما در کاتلین قضیه به شدت فرق میکنه ، ما در کاتلین object و companion object داریم ، مقداری که تحت عنوان object تعریف میشن به صورت lazy حساب میشن و زمانی که بار اول بخواید بهشون دسترسی داشته باشید ساخته میشن اما مقادیری که در companion object ساخته میشن هر زمانی که کلاس مورد نظرتون به هرشکلی صدا زده بشه و در حافظه بارگذاری بشه ساخته میشن ، به این کد دقت کنید :
در این کد عبارت by lazy دو کار انجام میده ، یکی اینکه به صورت lazy و با رعایت null check شئ مورد نظر رو میسازه (یعنی اون چندخطی که تو مثالهای جاوا زدیم رو یکجا میاره) و یکی اینکه Thread safe هست و نیازی نیست مثل جاوا از چیزی مثل synchronized استفاده کنید ولی کار رو بسیار میشه راحت تر هم انجام داد :
همین یک خط کد کل کارایثی مثل check null و thread safe و ... رو انجام میده و همه این موارد رو کاتلین خودش پیادهسازی میکنه (حالا شما باز بشین جاوا بزن :/) ، در همه کدهای بالا صرفا خود کلاس تعریف شده و شما میتونید داخل SingletonClass مقداری که میخواید رو تعریف کنیم و بهشون دسترسی داشته باشید و لزومی نداره در مثال های جاوا اونا رو به صورت static تعریف کنید بلکه فقط instance کلیِ شما باید static باشه و باقی موارد (مثلا یه پارامتر به اسم area از جنس int) به صورت عادی در کلاس SingletonClass تعریف میشن .
باقی مقالات در مورد الگویهای طراحی رو در این مقاله بخونید .
من رو در لینکدین و اینستاگرام دنبال کنید ???
اگه دوست داشتید میتونید به صفحه Spotify بنده هم برید و موسیقی های منو گوش بدید ???