Welcome to follow the official wechat account: FSA Full stack action 👋

One, foreword

  • Observer model
    • What it does: Defines a one-to-many dependency that allows one or more observer objects to listen on a topic object. In this way, when the observed state changes, the corresponding observers need to be notified, so that these observer objects can be automatically updated.
    • Core operations:
      • The observer (subscriber) adds or removes status listening for the observed (topic)
      • When the observed (topic) state changes, the event is notified to all observers, who perform the response logic

Use observer mode

  • Example: Monitor stock price movements
  • Important: Implement the observer pattern using Java apis or custom

1. Implement the Observer pattern using Java API

The Java standard library provides apis for the common Observer pattern:

  • java.util.Observable: Observed (subject)
    • setChanged(): marks status updates
    • addObserver()Add observer
    • deleteObserver()Delete observer
    • countObservers(): Gets the number of observers
    • notifyObservers(): Notify all observers
    • notifyObservers(Object arg): Notify all observers (with parameter ARG)
  • java.util.Observer: Observer (subscriber)

Using the Java API, you can implement the function of monitoring stock price changes:

import java.util.Observable
import java.util.Observer

/** * The subject **@author GitLqr
 */
class StockSubject : Observable() {
    fun changeStockPrice(price: Int) {
        this.setChanged() // Identifies status updates
        this.notifyObservers(price) // Inform all observers of the current stock price}}/** * Observer (subscriber) **@author GitLqr
 */
class StockDisplay(val name: String) : Observer {
    override fun update(o: Observable? , price:Any?). {
        println("$name receive stock price : $price") // Note that price is of type Any?}}/ / use
val subject = StockSubject()
subject.addObserver(StockDisplay("observer 1"))
subject.addObserver(StockDisplay("observer 2"))
subject.changeStockPrice(200)

/ / output
// observer 2 receive stock price : 200
// observer 1 receive stock price : 200
Copy the code

Note: Before notifying subscribers in a topic through notifyObservers(), setChanged() needs to be called to indicate status updates in order for them to be notified properly, which is something to be aware of when implementing observer patterns using Java APIS.

The API provided by Java already covers the full implementation of the Observer pattern, so we only need to focus on the business itself and do not have to implement the pattern ourselves. However, the API provided by Java is a generic implementation, as you can see from the above example. StockDisplay.update(o: Observable? , price: Any?) The price parameter is of type Any? , which leads to the following problems:

  • Parameter judgment: because the parameter type isAny?, so the development has to decide whether the parameter is null and the actual type of the parameter.
  • Single notification entry point: actual business requirements are more complex, whilejava.util.ObserverThere is only one notification entryupdate(o: Observable? , arg: Any?), so we had to separate response logic in the method, such as stock price rises and falls, which made the code bloated.

2, custom implementation of the observer mode

Although Java provides a ready-made observer pattern API, in practice, we usually implement the observer pattern in order to have better control over the code structure:

/** * Callback interface (decouple business notification entry) **@author GitLqr
 */
interface StockUpdateListener {
    fun onRise(price: Int)
    fun onFall(price: Int)
}

/** * The subject **@author GitLqr
 */
class StockSubject {
    val listeners = mutableSetOf<StockUpdateListener>()
    var price: Int = 0

    fun subscribe(observer: StockUpdateListener) {
        listeners.add(observer)
    }

    fun unsubscribe(observer: StockUpdateListener) {
        listeners.remove(observer)
    }

    fun changeStockPrice(price: Int) {
        val isRise = price > this.price
        listeners.forEach { if (isRise) it.onRise(price) else it.onFall(price) }
        this.price = price
    }
}

/** * Observer (subscriber) **@author GitLqr
 */
class StockDisplay : StockUpdateListener {
    override fun onRise(price: Int) {
        println("The latest stock price has rise to $price")}override fun onFall(price: Int) {
        println("The latest stock price has fell to $price")}}/ / use
val subject = StockSubject()
subject.subscribe(StockDisplay())
subject.changeStockPrice(200) // The latest stock price has rise to 200
Copy the code

As you can see, customizing the observer pattern makes the code structure simpler and more intuitive.

3. Improved observer mode

  • Example: Monitor stock price movements
  • Important: Delegate propertiesDelegates.observable()

The Kotlin standard library introduces observable delegate property, which is used to monitor the change of XXX property by Delegates (), so it can be used to improve the above custom observer mode:

import kotlin.properties.Delegates

/** * Observer mode improved: use delegate attribute to listen for value changes and notify **@author GitLqr
 */
class StockSubject {
    val listeners = mutableSetOf<StockUpdateListener>()

    var price: Int by Delegates.observable(0) { prop, old, new ->
        val isRise = new > old
        listeners.forEach { if (isRise) it.onRise(price) else it.onFall(price) }
    }

    fun subscribe(observer: StockUpdateListener) {
        listeners.add(observer)
    }

    fun unsubscribe(observer: StockUpdateListener) {
        listeners.remove(observer)
    }

    // fun changeStockPrice(price: Int) { ... }
}

/ / use
val subject = StockSubject()
subject.subscribe(StockDisplay())
subject.price = 250 // The latest stock price has rise to 200
Copy the code

After using Delegates.Observable (), StockSubject decreased the changeStockPrice() method by one. In use, notifications can be triggered once the price attribute is assigned, which is obviously more user-friendly (intuitive, one less method to remember).

Four, supplement

The Kotlin standard library introduces Delegates that can be observed, in addition to Delegates.observable(), vetoable() is useful for Delegates to veto property assignment when they do not want the monitored property to be modified at will:

import kotlin.properties.Delegates

var value: Int by Delegates.vetoable(0) { prop, old, new ->
    // The attribute is assigned only when the new value is greater than 0
    new > 0
}

/ / use
value = 1
println(value) / / 1
value = -1
println(value) // 1 (failed to assign)
Copy the code

If this article is helpful to you, please click on my wechat official number: FSA Full Stack Action, which will be the biggest incentive for me. The public account not only has Android technology, but also iOS, Python and other articles, which may have some skills you want to know about oh ~