The observer pattern

The observer pattern is useful for registering observers to an object so that all state changes in that object trigger the sending of notifications to the observers. In a language that supports first-class functions—for example, Julia—such functionality can be implemented easily by maintaining a list of functions that can be called before or after the state changes of an object. Sometimes, these functions are called hooks.

The implementation of the observer pattern in Julia may consist of two parts:

  1. Extend the setproperty! function of an object to monitor state changes and notify observers.
  2. Maintain a dictionary that can be used to look up the functions to call.

For this demonstration, we will bring up the bank account example again:

mutable struct Account
id::Int
customer::String
balance::Float64
end

Here is the data structure for maintaining observers:

const OBSERVERS = IdDict{Account,Vector{Function}}();

Here, we have chosen to use IdDict instead of the regular Dict object. IdDict is a special type that uses Julia's internal object ID as the key of the dictionary. To register observers, we provide the following function:

function register(a::Account, f::Function)
fs = get!(OBSERVERS, a, Function[])
println("Account $(a.id): registered observer function $(Symbol(f))")
push!(fs, f)
end

Now, let's extend the setproperty! function:

function Base.setproperty!(a::Account, field::Symbol, value)
previous_value = getfield(a, field)
setfield!(a, field, value)
fs = get!(OBSERVERS, a, Function[])
foreach(f -> f(a, field, previous_value, value), fs)
end

This new setproperty! function not only updates the field for the object, but also calls the observer functions with both the previous state and the current state after the field has been updated. For testing purposes, we will create an observer function as follows:

function test_observer_func(a::Account, field::Symbol, previous_value, current_value)
println("Account $(a.id): $field was changed from $previous_value to $current_value")
end

Our test function is written as follows:

function test()
a1 = Account(1, "John Doe", 100.00)
register(a1, test_observer_func)
a1.balance += 10.00
a1.customer = "John Doe Jr."
return nothing
end

When running the test program, we get the following output:

From the output, we can see that the test_observer_func function was called every time a property is updated. The observer pattern is an easy thing to develop. Next, we will look at the state pattern.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.42.164