記錄個人常用的Design Pattern,之前面試被問到有用過哪些,瞬間一片空白🥹
以下有寫iOS應該都用過,只是可能講不出這算哪個Pattern
詳情在前一篇學習筆記
確保一個class只能創建一個實例,並提供全域訪問。一般我會用在單一狀態管理上,例如:資料庫連結、登入狀態管理
通常包含以下特點:
範例:管理登入狀態的Manager。
class LoginManager {
static let shared = LoginManager()
var isLogin = false
private init() {}
func login() {
isLogin = true
}
func logout() {
isLogin = false
}
}
一個物件把一些職責讓另一個物件來執行。這個Apple官方自己也很常用。
範例:UITableViewDelegate,把TableView的一些行為(如:點擊)給ViewController來代理
class VC1: UIViewController{
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
tableView.delegate = self
}
}
extension VC1: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// 點擊後讓VC做一些事
}
}
這個Apple官方也很常用,例如:鍵盤彈起(UIKeyboardWillShowNotification)收合(UIKeyboardWillHideNotification),或是AppDelegate各種生命週期通知。
這種通知是全域的,發布者不需要知道訂閱者的任何資訊。
一種機制,允許一個物件可以註冊監聽其他物件指定屬性的變化。
這邊要介紹一下Combine
的KVOpublisher(for: \.KeyProperty)
,我覺得在UIKit上超好用ㄉ
範例:監聽UITabBarController的TabBar.isHidden
import Combine
class MyTabBarController: UITabBarController {
private var subscriptions = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
setBinding()
}
private func setBinding() {
self.tabBar.publisher(for: \.isHidden).sink { [weak self] hidden in
if !hidden {
// 如果Tabbar可見,在Bar上show一條InfoView
self?.showInfoView()
} else {
// 反之隱藏
self?.hideInfoView()
}
}
}.store(in: &subscriptions)
}
兩個都可以拿來在App裡通訊傳值,但使用時機略有不同
也可以。通常是一對一,如果要一對多,自己寫Array來保存多個觀察者。通常我會用在有明確行為,又只有兩三個地方需要接受這個訊息的情況下。
範例:Socket連線,當Socket連線狀態產生變化時,要通知底下所有有註冊他的委託對象(聊天室列表&聊天室頁面)
// 觀察者
class WeakSocketObserver {
weak var value: SocketDelegate?
init (value: SocketDelegate) {
self.value = value
}
}
// 定義Socket連線狀態的協議
protocol SocketDelegate: AnyObject {
// 連線成功
func didConnected()
// 其他狀態省略
// ...
}
Socket本人(委託人)
import SocketIO
@objc class MySocket: NSObject {
@objc static let shared = MySocket()
private var manager: SocketManager()
private var socket: SocketIOClient!
// 所有觀察者
private var observers: [WeakSocketObserver] = []
private override init() {
super.init()
// ...
socket = manager.defaultSocket
socket.on(clientEvent: .connect) { (data, ack) in
// 連線成功時,通知所有觀察者
self.observers.forEach { (observer) in
observer.value?.didConnected()
}
}
}
func tryConnect() {
// 開始連線
}
}
加入委託對象(觀察者)
// 第一個觀察者-聊天列表
class ChatroomListVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Socket加入觀察者
MySocket.shared.add(WeakSocketObserver(value: self))
// 嘗試連線
MySocket.shared.tryConnect()
}
}
extension ChatroomListVC: SocketDelegate {
func didConnected() {
// 我連線了!
}
}
// 第二個觀察者-聊天室
class ChatroomVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Socket加入觀察者
MySocket.shared.add(WeakSocketObserver(value: self))
// 嘗試連線
MySocket.shared.tryConnect()
}
}
extension ChatroomVC: SocketDelegate {
func didConnected() {
// 我連線了!
}
}
Socket連線後,底下的聊天室跟聊天列表都會收到通知。
參考資料:
ChatGPT
https://wilden-chen.gitbook.io/swift-design-patterns