Singleton (সিঙ্গেলটন) — একটি ক্রিয়েশনাল প্যাটার্ন যা ক্লাসের একক ইনস্ট্যান্স নিশ্চিত করে এবং এতে বৈশ্বিক অ্যাক্সেস প্রদান করে। Singleton মোবাইল ডেভেলপমেন্টে শেয়ার্ড রিসোর্সের জন্য ব্যাপকভাবে ব্যবহৃত হয়: নেটওয়ার্ক ক্লায়েন্ট, ডেটাবেস, সেটিংস ম্যানেজার। প্যাটার্নটি GoF (1994) ক্লাসিক বইয়ে বর্ণিত এবং সবচেয়ে পরিচিত প্যাটার্নগুলোর একটি। আরও জানতে Refactoring Guru: Singleton দেখুন।
মূল পয়েন্ট
Singleton — GoF (Gang of Four) 1994 সালে বর্ণিত একটি ক্রিয়েশনাল ডিজাইন প্যাটার্ন। প্যাটার্নটি দুটি সমস্যা সমাধান করে: এটি ক্লাসের ইনস্ট্যান্সিয়েশনকে একটি অবজেক্টে সীমাবদ্ধ করে এবং সেই অবজেক্টে বৈশ্বিক অ্যাক্সেস প্রদান করে। Singleton রিসোর্সের জন্য উপযোগী যা অনন্য হতে হবে: সেশন ফ্যাক্টরি, ইমেজ ক্যাশ, ডেটাবেস কানেকশন ম্যানেজার, Crashlytics বা Analytics ক্লায়েন্ট।
Singleton বাস্তবায়ন-এ প্রাইভেট কনস্ট্রাক্টর (বাহ্যিক তৈরি বন্ধ করে), একমাত্র ইনস্ট্যান্সসহ স্ট্যাটিক ফিল্ড এবং স্ট্যাটিক অ্যাক্সেস মেথড (shared, instance, getInstance) প্রয়োজন। ক্লায়েন্টরা অবজেক্ট তৈরির চিন্তা না করেই Singleton.shared.method() কল করে। প্যাটার্নটি iOS এবং Android-এ জনপ্রিয়: URLSession.shared, UserDefaults.standard, FirebaseApp.sharedInstance — সবই Singleton। তবে Singleton-এর অতিরিক্ত ব্যবহার Global State অ্যান্টি-প্যাটার্নের দিকে নিয়ে যায়।
Singleton সমস্যা — লুকানো ডিপেন্ডেন্সি (ক্লাসগুলি পরোক্ষভাবে Singleton অবজেক্টের উপর নির্ভর করে), টেস্টিং জটিলতা (অতিরিক্ত প্রচেষ্টা ছাড়া টেস্টে ইনস্ট্যান্স প্রতিস্থাপন করা যায় না), Single Responsibility Principle লঙ্ঘন (Singleton তার ইনস্ট্যান্স এবং বিজনেস লজিক উভয়ই পরিচালনা করে)। আধুনিক মোবাইল ডেভেলপমেন্ট একক ইনস্ট্যান্স পরিচালনার জন্য DI (Dagger, Hilt, Swinject) পছন্দ করে — DI কন্টেইনার অবজেক্টটি একবার তৈরি করে এবং কনস্ট্রাক্টরের মাধ্যমে ইনজেক্ট করে।
Swift Singleton প্রাইভেট ইনিশিয়ালাইজারসহ স্ট্যাটিক shared প্রপার্টির মাধ্যমে বাস্তবায়িত হয়। Swift 3 থেকে, স্ট্যাটিক প্রপার্টির লেজি ইনিশিয়ালাইজেশন থ্রেড-সেফ গ্যারান্টিযুক্ত — কম্পাইলার স্বয়ংক্রিয়ভাবে dispatch_once-এর মাধ্যমে সিঙ্ক্রোনাইজেশন যোগ করে। static let shared = Class() ডিক্লেয়ার করা এবং init() প্রাইভেট করা যথেষ্ট। Swift-এর ইনিশিয়ালাইজেশনের পরে সিঙ্গেল-থ্রেডেড অ্যাক্সেসের জন্য অতিরিক্ত সিঙ্ক্রোনাইজেশন প্রয়োজন হয় না।
final class NetworkManager {
// থ্রেড-সেফ Singleton
static let shared = NetworkManager()
private init() {
URLSessionConfiguration.default.timeoutIntervalForRequest = 30
}
private var cache = NSCache<NSString, NSData>()
func fetchData(from url: URL) async throws -> Data {
let key = url.absoluteString as NSString
if let cached = cache.object(forKey: key) {
return cached as Data
}
let (data, _) = try await URLSession.shared.data(from: url)
cache.setObject(data as NSData, forKey: key)
return data
}
}
// ব্যবহার
let data = try await NetworkManager.shared.fetchData(from: url)
Apple Singleton — iOS SDK-র অনেক অবজেক্ট Singleton ব্যবহার করে: UIApplication.shared, UIScreen.main, FileManager.default, NotificationCenter.default, UserDefaults.standard। Apple শারীরিকভাবে অনন্য পরিষেবাগুলির (এক স্ক্রিন, এক অ্যাপ্লিকেশন) জন্য Singleton ব্যবহার করে। ডেভেলপাররা তাদের নিজস্ব পরিষেবার জন্য এই প্যাটার্ন কপি করে। SwiftUI-তে, Singleton-এ বৈশ্বিক অ্যাক্সেস Environment এবং @EnvironmentObject দ্বারা প্রতিস্থাপিত হয়, যা টেস্টেবিলিটি উন্নত করে।
Kotlin Singleton — সবচেয়ে সহজ উপায়: object কীওয়ার্ড প্রথম অ্যাক্সেসে লেজি ইনিশিয়ালাইজেশনসহ সিঙ্গেলটন ক্লাস ডিক্লেয়ার করে। Kotlin object থ্রেড-সেফ এবং অতিরিক্ত সিঙ্ক্রোনাইজেশন প্রয়োজন হয় না। যদি কনস্ট্রাক্টর প্যারামিটারসহ Singleton প্রয়োজন হয়, lazy ডেলিগেটসহ companion object ব্যবহার করা হয়। Android-এ, Application কন্টেক্সট এবং Application.onCreate()-এর মাধ্যমে ইনিশিয়ালাইজ হওয়া পরিষেবাগুলির জন্য Singleton প্রায়ই প্রয়োজন।
// বিকল্প 1: object — প্যারামিটার ছাড়া সাধারণ Singleton
object AppPreferences {
private val prefs = Application.instance
.getSharedPreferences("app", Context.MODE_PRIVATE)
var isFirstLaunch: Boolean
get() = prefs.getBoolean("first_launch", true)
set(value) = prefs.edit { putBoolean("first_launch", value) }
}
// বিকল্প 2: companion object — প্যারামিটারসহ Singleton
class ApiClient private constructor(baseUrl: String) {
companion object {
@Volatile
private var instance: ApiClient? = null
fun getInstance(baseUrl: String): ApiClient {
return instance ?: this.synchronized {
instance ?: ApiClient(baseUrl).also { instance = it }
}
}
}
fun request(endpoint: String): String { /* ... */ }
}
Android SDK Singleton — Android-এর অনেক সিস্টেম পরিষেবা Singleton বাস্তবায়ন করে: context.getSystemService(), Room.databaseBuilder(), Retrofit.Builder()। উদাহরণের মধ্যে SharedPreferences, MediaPlayer, AudioManager অন্তর্ভুক্ত। Android অ্যাপ্লিকেশনে, Singleton প্রায়ই রিপজিটরি, ম্যানেজার এবং ফ্যাক্টরির জন্য ব্যবহৃত হয়। Google Singleton-কে DI (Hilt, Koin) দিয়ে প্রতিস্থাপনের সুপারিশ করে, যেখানে Singleton স্কোপ (Scope.Singleton বা @Singleton) কন্টেইনার দ্বারা পরিচালিত হয় যখন ক্লাস টেস্টেবল থাকে।
Thread safety — মাল্টিথ্রেডেড পরিবেশে Singleton-এর জন্য একটি গুরুত্বপূর্ণ প্রয়োজনীয়তা। সিঙ্ক্রোনাইজেশন ছাড়া, দুটি থ্রেড একসাথে instance == null চেক করতে পারে এবং দুটি ইনস্ট্যান্স তৈরি করতে পারে। সমাধান হল প্রথম তৈরি করার সময় লক করা এবং ইনিশিয়ালাইজেশনের পরে মুক্তি দেওয়া। Swift-এ, স্ট্যাটিক প্রপার্টি (static let) ডিফল্টভাবে থ্রেড-সেফ। Kotlin-এ, object থ্রেড-সেফ। Kotlin-এ Java-স্টাইলের জন্য, synchronized বা @Volatile + double-check locking ব্যবহার করা হয়।
| ভাষা | পদ্ধতি | থ্রেড নিরাপত্তা | লেজি ইনিশিয়ালাইজেশন |
|---|---|---|---|
| Swift | static let | dispatch_once (স্বয়ংক্রিয়) | হ্যাঁ, প্রথম অ্যাক্সেসে |
| Kotlin object | Object ডিক্লেয়ারেশন | ক্লাস ইনিশিয়ালাইজার থ্রেড-সেফ | হ্যাঁ, প্রথম অ্যাক্সেসে |
| Kotlin companion | synchronized + @Volatile | Double-checked locking | হ্যাঁ, lazy বা synchronized-এর মাধ্যমে |
| Java | synchronized + volatile | Double-checked locking | হ্যাঁ, getInstance()-এ |
Double-checked locking — Singleton-এর লেজি ইনিশিয়ালাইজেশনের জন্য একটি প্যাটার্ন। প্রথম চেক সিঙ্ক্রোনাইজেশন ছাড়া (দ্রুত যদি ইনস্ট্যান্স ইতিমধ্যেই থাকে), দ্বিতীয়টি synchronized-এর ভিতরে (শুধুমাত্র একটি থ্রেড দ্বারা তৈরি)। @Volatile সকল থ্রেডের জন্য পরিবর্তনের দৃশ্যমানতা নিশ্চিত করে। volatile ছাড়া, অন্য থ্রেড আংশিকভাবে তৈরি অবজেক্ট দেখতে পারে। Kotlin-এ, LazyThreadSafetyMode.SYNCHRONIZED-সহ lazy ডেলিগেট স্বয়ংক্রিয়ভাবে double-checked locking বাস্তবায়ন করে।
Dependency Injection — একক ইনস্ট্যান্স পরিচালনার জন্য Singleton-এর একটি বিকল্প। DI কন্টেইনার (Dagger, Hilt, Koin, Swinject) Singleton স্কোপে অবজেক্টটি একবার তৈরি করে এবং কনস্ট্রাক্টরের মাধ্যমে ইনজেক্ট করে। ক্লাস তার Singleton অবস্থা সম্পর্কে জানে না — কন্টেইনার সিদ্ধান্ত নেয়। কোড টেস্টেবল হয়: টেস্টে DI মডিউল mock মডিউল দিয়ে প্রতিস্থাপিত হয়। DI সুবিধা: কনস্ট্রাক্টরে স্পষ্ট ডিপেন্ডেন্সি, ওভাররাইড করার ক্ষমতা, একীভূত লাইফসাইকেল।
কখন Singleton যুক্তিযুক্ত — সিস্টেম-স্তরের অবজেক্ট: Crashlytics, Analytics, Logging। এই পরিষেবাগুলি AppDelegate/Application-এ একবার ইনিশিয়ালাইজ হয় এবং সর্বত্র ব্যবহৃত হয়। তাদের জন্য DI অপ্রয়োজনীয়। Singleton ইমেজ ক্যাশের (NSCache, Coil, Glide) জন্যও সুবিধাজনক, যেখানে বৈশ্বিক অ্যাক্সেস পারফরম্যান্স দ্বারা যুক্তিযুক্ত। বাকি সবকিছুর জন্য, DI পছন্দনীয়: এটি ডিপেন্ডেন্সি দৃশ্যমান করে, টেস্টিং এবং রিফ্যাক্টরিং সহজ করে।
হাইব্রিড পদ্ধতি — টেস্টের জন্য ওভাররাইড করার ক্ষমতাসহ Singleton। Swift-এ, একটি প্রোটোকল + স্ট্যাটিক প্রপার্টি যা টেস্ট প্রতিস্থাপন করতে পারে (যেমন, URLSession-এর জন্য URLProtocol-এর মাধ্যমে)। Kotlin-এ, একটি ইনজেক্টেবল প্রপার্টিসহ open ক্লাস, যেখানে টেস্ট রিফ্লেকশন বা setter-এর মাধ্যমে mock সেট করে। এই পদ্ধতি Singleton-এর সরলতা বজায় রাখে কিন্তু টেস্টিং ক্ষমতা প্রদান করে। Google Android-এর জন্য Hilt সুপারিশ করে, Apple iOS-এর জন্য DI চাপিয়ে দেয় না — পছন্দ টিমের উপর নির্ভর করে।
সচরাচর জিজ্ঞাসিত প্রশ্ন
না, Singleton একটি GoF প্যাটার্ন, কিন্তু এর ঘন ঘন ভুল ব্যবহার এটিকে Global State অ্যান্টি-প্যাটার্নে পরিণত করে। Singleton শারীরিকভাবে অনন্য রিসোর্সের (স্ক্রিন, প্রিন্টার, ফাইল সিস্টেম) জন্য যুক্তিযুক্ত। সমস্যা দেখা দেয় যখন Singleton ডেটা পরিচালনার জন্য ব্যবহৃত হয়: লুকানো ডিপেন্ডেন্সি, টেস্টিং জটিলতা, Single Responsibility Principle লঙ্ঘন। আধুনিক বিকল্প হল Singleton স্কোপসহ DI।
তিনটি পদ্ধতি: (1) প্রোটোকলের মাধ্যমে — Singleton একটি প্রোটোকল বাস্তবায়ন করে, টেস্ট বাস্তবায়ন পরিবর্তন করে; (2) DI-এর মাধ্যমে — Singleton কনস্ট্রাক্টরের মাধ্যমে ডিপেন্ডেন্সি হিসেবে ইনজেক্ট হয়; (3) reset মেথডের মাধ্যমে — Singleton-এর টেস্টে অবস্থা রিসেট করার মেথড থাকে (শুধুমাত্র টেস্ট বিল্ডের জন্য)। প্রথম পদ্ধতি পছন্দনীয়, তৃতীয়টি প্রোডাকশনের জন্য বিপজ্জনক। Swift টেস্টে রানটাইম ম্যানিপুলেশনের মাধ্যমে shared প্রপার্টি প্রতিস্থাপনের অনুমতি দেয়।
Kotlin object একটি ভাষা নির্মাণ যা বাইটকোড স্তরে Singleton তৈরি করে। প্রাইভেট কনস্ট্রাক্টর এবং getInstance()-সহ Java বাস্তবায়নের বিপরীতে, object থ্রেড নিরাপত্তা, লেজি ইনিশিয়ালাইজেশন নিশ্চিত করে এবং ইনহেরিটেন্স নিষিদ্ধ করে। Java Singleton-এর মাল্টিথ্রেডেড পরিবেশে সঠিক কাজের জন্য ম্যানুয়াল সিঙ্ক্রোনাইজেশন (synchronized) এবং volatile প্রয়োজন। Kotlin object Android-এ সবচেয়ে নিরাপদ এবং সংক্ষিপ্ত উপায়।
Singleton ইনহেরিট করা প্যাটার্ন ভঙ্গ করে: যদি Singleton ক্লাস ইনহেরিট করা যায়, তাহলে একটি সাবক্লাস দ্বিতীয় ইনস্ট্যান্স তৈরি করতে পারে, যা অনন্যতা লঙ্ঘন করে। Swift-এ, final class ইনহেরিটেন্স নিষিদ্ধ করে। Kotlin object ইনহেরিট করা যায় না (object sealed)। যদি ভেরিয়েবিলিটিসহ Singleton প্রয়োজন হয়, Singleton স্কোপসহ DI কন্টেইনার ব্যবহার করুন: এটি একক ইনস্ট্যান্স নিশ্চিত করে এবং ইন্টারফেসের মাধ্যমে ইনহেরিটেন্স সমর্থন করে।
প্যারামিটার init(context: Application) বা getInstance(param)-এর মাধ্যমে পাস করা হয়। Kotlin object প্যারামিটার গ্রহণ করে না — ফ্যাক্টরি মেথড getInstance(param)-সহ companion object ব্যবহার করুন। Hilt সমস্যা সমাধান করে: @Singleton + @Inject constructor(context: Application) — DI কন্টেইনার স্বয়ংক্রিয়ভাবে Application কন্টেক্সট ইনজেক্ট করে। Retrofit ক্লায়েন্টের জন্য, প্যারামিটার (baseUrl, interceptors) DI মডিউলে builder-এর মাধ্যমে পাস করা হয়।
সারসংক্ষেপ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন