Section 5/171 menit
5. Best Practice: Desain Actor
5. Best Practice: Desain Actor
BP 5.1: Buat Method Actor Sependek Mungkin
Method actor yang panjang = lock yang dipegang lama = lebih banyak contention. Hop in, do the minimum, hop out.
swift
// ❌ Method actor yang melakukan terlalu banyak
actor OrderStore {
var orders: [Order] = []
func placeOrder(_ order: Order) async throws {
let validated = try await validate(order) // I/O lama
let priced = try await price(validated) // I/O lama
let processed = try await charge(priced) // I/O lama
orders.append(processed)
}
}
// ✓ Side effect I/O di luar actor, mutasi state di akhir
struct OrderService {
let store: OrderStore
func placeOrder(_ order: Order) async throws {
let validated = try await validate(order)
let priced = try await price(validated)
let processed = try await charge(priced)
await store.append(processed) // hanya hop saat dibutuhkan
}
}
actor OrderStore {
private(set) var orders: [Order] = []
func append(_ order: Order) { orders.append(order) }
}
BP 5.2: Hindari Method Actor yang Mengembalikan Reference Mutable
Actor melindungi state internal. Tapi kalau kamu mengembalikan reference ke object mutable, isolasi pecah.
swift
// ❌ Bocor reference mutable
actor UserStore {
private var users: [UUID: NSMutableArray] = [:]
func purchases(for id: UUID) -> NSMutableArray {
return users[id] ?? NSMutableArray() // caller bisa mutate di luar actor
}
}
// ✓ Return value type / snapshot
actor UserStore {
private var users: [UUID: [Purchase]] = [:]
func purchases(for id: UUID) -> [Purchase] {
return users[id] ?? [] // value type, copy on assign
}
}
BP 5.3: Pisahkan "Producer Actor" dan "Consumer Actor"
Kalau satu actor menerima event dan juga di-query, sering lebih baik dipisah.
swift
// ✓ Pisah responsibility
actor EventIngester {
private let store: EventStore
init(store: EventStore) { self.store = store }
func ingest(_ e: Event) async { await store.append(e) }
}
actor EventStore {
private var events: [Event] = []
func append(_ e: Event) { events.append(e) }
func recent(limit: Int) -> [Event] { Array(events.suffix(limit)) }
}
BP 5.4: Gunakan nonisolated untuk Method yang Tidak Akses State
Method yang hanya menggunakan parameter / let properties tidak butuh hop. Tandai nonisolated.
swift
actor Calculator {
private var history: [Double] = [] // mutable, butuh isolation
let precision: Int = 4 // immutable
func compute(_ x: Double, _ y: Double) -> Double {
let result = (x + y).rounded(to: precision)
history.append(result)
return result
}
// Tidak akses history, tidak butuh hop
nonisolated func formatPrecision() -> String {
return "\(precision) digits"
}
}
BP 5.5: Batch Operasi Saat Mungkin
Banyak await ke method actor untuk operasi kecil = banyak hop. Sediakan API batch.
swift
// ❌ Hop per item
actor Logger {
var entries: [String] = []
func log(_ s: String) { entries.append(s) }
}
for msg in messages {
await logger.log(msg) // N hop
}
// ✓ Satu hop
extension Logger {
func logAll(_ items: [String]) {
entries.append(contentsOf: items)
}
}
await logger.logAll(messages)