Observer — یک الگوی رفتاری که در آن یک شیء (ناشر) چندین مشترک را از تغییرات وضعیت خود مطلع میکند. در توسعه موبایل، Observer اساس مکانیکهای واکنشگرا را تشکیل میدهد: UI در تغییرات داده مشترک میشود و بهطور خودکار بهروز میشود. این الگو در NotificationCenter در iOS و LiveData/Flow در Android پیادهسازی شده است. بیشتر — در Refactoring Guru: Observer.
نکات اصلی
Observer (ناظر) — الگوی رفتاری GoF که وابستگی «یک به چند» بین اشیاء را تعریف میکند. هنگامی که یک شیء (Subject یا Observable) وضعیت خود را تغییر میدهد، تمام اشیاء وابسته (Observers) بهطور خودکار مطلع و بهروز میشوند. این الگو اتصال ضعیف را پیادهسازی میکند: ناشر کلاسهای خاص مشترکان را نمیداند — فقط میداند که آنها رابط Observer را پیادهسازی میکنند.
ساختار Observer شامل رابط Subject با متدهای attach()، detach()، notify() و رابط Observer با متد update() است. ConcreteSubject وضعیت و فهرست مشترکان را ذخیره میکند. ConcreteObserver update() را پیادهسازی میکند و به تغییرات واکنش نشان میدهد. در توسعه موبایل، پیادهسازی کلاسیک GoF به ندرت یافت میشود — مکانیزمهای داخلی جایگزین آن میشوند: NotificationCenter، Combine، Flow، LiveData که همان ایده را با API مدرن پیادهسازی میکنند.
مدل Push vs Pull — در مدل Push، Subject داده را به همه مشترکان ارسال میکند (NotificationCenter.post). در مدل Pull، Subject فقط اطلاع میدهد و مشترک خودش داده را دریافت میکند. Android LiveData از Push استفاده میکند (داده در observe() منتقل میشود)، RxJava/Flow هر دو مدل را پشتیبانی میکنند. انتخاب بستگی به وظیفه دارد: Push برای بهروزرسانی UI سادهتر است، Pull برای حجمهای بزرگ داده که مشترک ممکن است نخواهد دریافت کند، کارآمدتر است.
NotificationCenter — مکانیزم داخلی iOS/macOS برای پیادهسازی Observer. ناشر Notification را از طریق NotificationCenter.default.post(name:, object:, userInfo:) ارسال میکند. مشترک از طریق addObserver(forName:, queue:, using:) ثبتنام میکند. NotificationCenter از اعلانهای نامگذاری شده (Notification.Name) پشتیبانی میکند و میتواند هر دادهای را در userInfo منتقل کند. UIKeyboardWillShowNotification، UIApplicationDidEnterBackgroundNotification — نمونههای سیستمی.
extension Notification.Name {
static let userDidLogin = Notification.Name("userDidLogin")
}
// ناشر
NotificationCenter.default.post(
name: .userDidLogin,
object: nil,
userInfo: ["userId": "123"]
)
// مشترک
class ProfileViewModel {
private var observers: [NSObjectProtocol] = []
func startObserving() {
let observer = NotificationCenter.default.addObserver(
forName: .userDidLogin,
object: nil,
queue: .main
) { [weak self] notification in
guard let userId = notification.userInfo?["userId"] as? String else { return }
// مشترک به رویداد واکنش نشان میدهد
self?.loadProfile(userId: userId)
}
observers.append(observer)
}
func stopObserving() {
observers.forEach { NotificationCenter.default.removeObserver($0) }
observers.removeAll()
}
}
Combine framework — جایگزین واکنشگرای مدرن برای NotificationCenter، از iOS 13 در دسترس است. Publisher (NotificationCenter، URLSession.Timer) — ناشر، Subscriber (sink، assign) — مشترک. Combine برای تبدیل جریان داده عملگرهایی (map، filter، combineLatest) اضافه میکند. @Published — property wrapper که بهطور خودکار مشترکان را از تغییرات مطلع میکند. در MVVM با SwiftUI، Combine جایگزین NotificationCenter برای اتصال ViewModel و View میشود.
KVO (Key-Value Observing) — مکانیزم قدیمی ObjC/Swift برای مشاهده ویژگیهای فردی اشیاء. @objc dynamic var name: String — ویژگی قابل مشاهده. observe(.name) — اشتراک. KVO فقط با کلاسهای سازگار با @objc و میراث ObjC کار میکند. Apple در پروژههای جدید به جای KVO، Combine و @Published را توصیه میکند. KVO برای سازگاری با UIKit در پروژههای ترکیبی همچنان مرتبط است.
LiveData — مؤلفه Android Architecture Components برای پیادهسازی Observer. کلاس Observable که مشترکان را از تغییرات داده مطلع میکند. LiveData چرخه حیات را در نظر میگیرد: مشترکان (LifecycleOwner) بهطور خودکار هنگام نابودی لغو اشتراک میکنند. LiveData مدل Push است: داده در observe() منتقل میشود. LiveData بلوک ساختمانی پایه MVVM در Android قبل از معرفی Jetpack Compose است.
// ViewModel — ناشر
class UserViewModel : ViewModel() {
private val _user = MutableLiveData<User?>(null)
val user: LiveData<User?> = _user
fun loadUser(id: String) {
viewModelScope.launch {
val result = userRepository.getUser(id)
_user.value = result
}
}
}
// Fragment — مشترک
class UserFragment : Fragment() {
private val viewModel: UserViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewModel.user.observe(viewLifecycleOwner) { user ->
// مشترک به تغییرات واکنش نشان میدهد
userName.text = user?.name
userEmail.text = user?.email
}
}
}
StateFlow و SharedFlow — انواع واکنشگرا از Kotlin Coroutines که جایگزین LiveData در Jetpack Compose شدند. StateFlow — state holder قابل مشاهده با مقدار جاری ثابت. SharedFlow — hot flow قابل تنظیم بدون حالت، مناسب برای رویدادهای یکبار مصرف (ناوبری، toastها). هر دو نوع بهطور نزدیک با Compose یکپارچه شدهاند: collectAsState()، collectAsEffect(). StateFlow در پروژههای مدرن Android با Compose اجباری است.
LiveData vs StateFlow — LiveData به Android Lifecycle وابسته است، StateFlow مستقل از پلتفرم است. StateFlow از coroutines، عملگرها (map، filter) پشتیبانی میکند و بدون وابستگیهای Android آزمایش میشود. LiveData برای سازگاری با Java سادهتر است. Google برای پروژههای جدید با Kotlin + Compose StateFlow و برای پشتیبانی از پروژههای قدیمی یا کد Java، LiveData را توصیه میکند.
نشت حافظه (memory leaks) — مشکل اصلی Observer بدون مدیریت صحیح اشتراکها. اگر مشترک (Activity، Fragment، UIViewController) نابود شده باشد اما لغو اشتراک نکرده باشد، ناشر همچنان به آن ارجاع نگه میدارد و garbage collector نمیتواند حافظه را آزاد کند. در Android، LifecycleOwner (Activity/Fragment) باید removeObserver() را فراخوانی کند یا از observe(viewLifecycleOwner) استفاده کند. در iOS — removeObserver در deinit یا disposeBag در Combine.
| پلتفرم | مکانیزم Observer | لغو اشتراک خودکار | لغو اشتراک دستی |
|---|---|---|---|
| iOS | NotificationCenter | خیر | removeObserver() در deinit |
| iOS | Combine (sink) | خیر | store(in: &bag) — DisposeBag |
| iOS | KVO | خیر | removeObserver() در deinit |
| Android | LiveData | بله (LifecycleOwner) | removeObserver() اختیاری |
| Android | StateFlow | از طریق viewModelScope | cancel() Job هنگام لغو اشتراک |
| Android | RxJava | خیر | dispose() در CompositeDisposable |
ارجاع ضعیف (weak reference) در مشترکان — در بلوک اشتراک از [weak self] در Swift و به scope lifecycle در Kotlin ارجاع دهید. LiveData بهطور خودکار اشتراک را از طریق LifecycleOwner مدیریت میکند — اشتراک فقط زمانی فعال است که Lifecycle در وضعیت STARTED یا RESUMED باشد. StateFlow در Compose از collectAsState() با در نظر گرفتن lifecycle استفاده میکند. NotificationCenter در iOS نیاز به [weak self] صریح دارد، زیرا closure به self ارجاع قوی دارد.
Observer (GoF) و Publisher-Subscriber (PubSub) — الگوهای مشابه اما متفاوت. در Observer، ناشر مستقیماً با فراخوانی متدهایشان مشترکان را مطلع میکند. ناشر از مشترکان آگاه است (فهرست را ذخیره میکند). در PubSub، ناشر و مشترک از یکدیگر بیخبرند — واسطهای (Event Bus، Message Queue، NotificationCenter) بین آنها قرار دارد. ناشر پیام را به کانال میفرستد، مشترک به کانال گوش میدهد. PubSub اتصال ضعیفتری دارد.
نمونههای PubSub در توسعه موبایل — NotificationCenter در iOS را میتوان PubSub در نظر گرفت: ناشر مشترکان را نمیشناسد — فقط اعلان را ارسال میکند. EventBus یا Otto در Android (منسوخ شده). SharedFlow با BroadcastChannel — PubSub در دنیای Kotlin. در سیستمهای توزیعشده، PubSub از طریق RabbitMQ، Kafka، Google PubSub پیادهسازی میشود. برای توسعه موبایل، PubSub در معماری ماژولار مفید است، جایی که ماژولها نباید به یکدیگر وابسته باشند.
چه چیزی انتخاب کنیم — برای بهروزرسانی UI (ViewModel → View) از Observer استفاده کنید (LiveData، StateFlow، @Published). برای رویدادهای بینماژولی (احراز هویت، خروج از حساب، تغییر تم) — PubSub (SharedFlow، NotificationCenter، EventBus). Observer در یک صفحه سادهتر و کارآمدتر است، PubSub برای رویدادهای سراسری انعطافپذیرتر اما به دلیل وابستگیهای ضمنی در اشکالزدایی دشوارتر است.
سوالات متداول
StateFlow یک نوع مستقل از پلتفرم از Kotlin Coroutines است، LiveData به Android Lifecycle وابسته است. StateFlow از coroutines و عملگرها پشتیبانی میکند و بدون Android آزمایش میشود. LiveData اشتراک را از طریق LifecycleOwner بهطور خودکار مدیریت میکند. Google برای پروژههای جدید با Kotlin + Compose StateFlow و برای سازگاری با Java LiveData را توصیه میکند.
در closure handler از [weak self] استفاده کنید و removeObserver() را در deinit فراخوانی کنید. به observer (NSObjectProtocol) ارجاع نگه دارید و هنگام نابودی شیء آن را حذف کنید. در Combine از AnyCancellable و store(in:) برای لغو اشتراک خودکار هنگام آزادسازی DisposeBag استفاده کنید.
بله، SwiftUI از ObservableObject با @Published و @StateObject/@ObservedObject پشتیبانی میکند — این یک پیادهسازی داخلی Observer است. @Published بهطور خودکار View را از تغییرات مطلع میکند. Combine الزامی نیست: ObservableObject از Publisher objectWillChange که در SwiftUI تعبیه شده استفاده میکند. Combine برای تبدیل جریانها عملگرهایی اضافه میکند.
SharedFlow — برای رویدادهای یکبار مصرف (ناوبری، toastها، Snackbar) که به مقدار جاری نیاز ندارند. StateFlow — برای وضعیت UI (فهرست داده، وضعیت بارگذاری) که به snapshot جاری نیاز است. SharedFlow ویژگی value ندارد و آخرین مقدار را به مشترکان جدید برنمیگرداند.
KVO یک مکانیزم قدیمی ObjC است، نیاز به @objc dynamic دارد و فقط با کلاسهای ارثبرده از NSObject کار میکند. Combine یک فریمورک مدرن Swift است، نوعامن، با عملگرها و یکپارچگی با SwiftUI. Combine جایگزین KVO و NotificationCenter میشود. Apple برای پروژههای جدید Combine و فقط برای پشتیبانی از کد قدیمی KVO را توصیه میکند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید