Ali Shobeyri
Ali Shobeyri
خواندن ۵ دقیقه·۵ سال پیش

الگوی طراحیِ Singleton (جاوا و کاتلین)

در مواقعی ما نیاز داریم تنها یک instance از شئ یا مقادیر مورد نظرمون داشته باشیم ، در چنین مواقعی الگوی Singleton راه چاره ماست ، این الگو در جاوا به صورت‌های مختلف پیاده سازی میشه ولی در کاتلین به شدت راحت تره ، پیاده سازی های گوناگون اون رو با هم بررسی می‌کنیم :

تنها یک instance از شئ مورد نظر در کل برنامه وجود داره
تنها یک instance از شئ مورد نظر در کل برنامه وجود داره


Class-level Member (Eager Initialization Method)

شی مورد نظرمون رو به صورت static (وقتی مقدار به صورت static تعریف بشه فقط یک instance در تمامی برنامه از اون وجود داره) به این شکل تعریف می‌کنیم :

https://gist.github.com/sasssass/68bb3350648e50634a019d59d83d424a

به این صورت هست که هر زمانی که ما هر تابع و پارامتری رو از کلاس SingletonClass صدا بزنیم ، instance مورد نظرمون ساخته میشه ، این از نظرهایی بد میشه ، چون مثلا زمانی که ما به جای SINGLE_INSTANCE دو تا پارامتر داشته باشیم هر کدوم رو که صدا بزنیم اون یکی هم درون کلاس ساخته میشه و حافظه بیخودی بهش اختصاص داده میشه ، برای جلوگیری از این قضیه می‌تونیم به صورت lazy پیاده سازی کنیم و میشه حالت دوم :

Class-level Member (Lazy Initialization Method)

https://gist.github.com/sasssass/c3b938b78f04e0448bd41fcca4db344e

ولی همین حالت هم یک مشکلی داره ، فرض کنید برنامه‌ای نوشتید که از دو تا Thread مختلف قراره به این کلاس دسترسی داشته باشه ، اون وقت این برنامه ممکنه هم زمان در حالتی که هنوز instance ما null هست بیاد اون رو مقداردهی کنه و دوباره مقداردهی بشه (یعنی هر دو Thread با هم در یک زمان به شرط null بودن میرسن و مساله پیش میاد) برای حل این مشکل حالت سوم رو ایجاد میکنن :

Class-level Member (Lazy Initialization with double lock Method)

https://gist.github.com/sasssass/ba6b5a506225349da9775d6a294e4c1f

کلمه کلیدی synchronized مشکل هم زمانی رو برطرف می‌کنه ، در واقع وقتی شما از این عبارت استفاده می‌کنید هم‌زمان دو Thread نمی‌تونن به قسمت مربوطه ورود پیدا کنن و یکیشون lock یا قفل میشه ، زمانی که کار Thread اول تموم شد اجازه ورود به Thread دوم داده میشه ، و دوباره درون عبارت synchronized شده شرط null چک میشه که اگه Thread اول اون رو از حالت null درآورد دیگه Thread دوم کاری انجام نده ، حالت چهارم زمانیه که شما کلاسِ static رو هم دخیل کنید :

By using nested Inner class (Lazy Load method)

https://gist.github.com/sasssass/eb1774c9ebb86876fbf53c4da62e273e

وقتی شما کلاسی رو به صورت static داخل کلاس دیگه بیارید تا زمانی که متد و تابعی از کلاس بیرونی (SingletonClass) اون رو صدا نزنه داخل حافظه بارگذاری نمیشه (اما در پارامتر به صورت static هر زمان که هر متد یا تابعی از کلاس بیرونی صدا زده بشه باقی پارامترهای static هم در حافظه بارگذاری میشن) ، در این حالت قضیه هم‌زمانی Thread ها یه Thread safe بودن هم رعایت شده (جاوا این کار رو خودش انجام میده) ، آخرین حالت استفاده از Enum هاست :

By using Enums

https://gist.github.com/sasssass/c218b83996d13e64c029e2824d86e140

در الگوی Singleton شما باید constructor رو به صورت private دربیارید و متدها و توابعی مثل getIntance رو برای دسترسی به مقادیر مورد نظرتون بسازید ، تمامی این مسائل در Enum ها رعایت شده ، شما نمی‌تونید از Enum شئ بگیرید و می‌تونید به SINGLE_INSTANCE با صدازدنش دسترسی داشته باشید .

الگوی Singleton در Kotlin

اما در کاتلین قضیه به شدت فرق می‌کنه ، ما در کاتلین object و companion object داریم ، مقداری که تحت عنوان object تعریف میشن به صورت lazy حساب میشن و زمانی که بار اول بخواید بهشون دسترسی داشته باشید ساخته میشن اما مقادیری که در companion object ساخته میشن هر زمانی که کلاس مورد نظرتون به هرشکلی صدا زده بشه و در حافظه بارگذاری بشه ساخته میشن ، به این کد دقت کنید :

https://gist.github.com/sasssass/eed41b56e2b65800b277256e5dcda65c

در این کد عبارت by lazy دو کار انجام میده ، یکی اینکه به صورت lazy و با رعایت null check شئ مورد نظر رو می‌سازه (یعنی اون چندخطی که تو مثال‌های جاوا زدیم رو یکجا میاره) و یکی اینکه Thread safe هست و نیازی نیست مثل جاوا از چیزی مثل synchronized استفاده کنید ولی کار رو بسیار میشه راحت تر هم انجام داد :

https://gist.github.com/sasssass/47c437d25a49f69df91e968bc3f08ac3

همین یک خط کد کل کارایثی مثل check null و thread safe و ... رو انجام میده و همه این موارد رو کاتلین خودش پیاده‌سازی می‌کنه (حالا شما باز بشین جاوا بزن :/) ، در همه کدهای بالا صرفا خود کلاس تعریف شده و شما می‌تونید داخل SingletonClass مقداری که می‌خواید رو تعریف کنیم و بهشون دسترسی داشته باشید و لزومی نداره در مثال های جاوا اونا رو به صورت static تعریف کنید بلکه فقط instance کلیِ شما باید static باشه و باقی موارد (مثلا یه پارامتر به اسم area از جنس int) به صورت عادی در کلاس SingletonClass تعریف میشن .

باقی مقالات در مورد الگوی‌های طراحی رو در این مقاله بخونید .

من رو در لینکدین و اینستاگرام دنبال کنید ???

اگه دوست داشتید می‌تونید به صفحه Spotify بنده هم برید و موسیقی های منو گوش بدید ???


javakotlindesign patternandroidاندروید
برنامه نویس اندروید - https://www.linkedin.com/in/iryebohs/
شاید از این پست‌ها خوشتان بیاید