The mediator pattern

The mediator pattern is used to facilitate communication between different components in an application. This is done in such a way that individual components are decoupled from each other. In most applications, changes in one component can affect another. Sometimes, there are also cascading effects. A mediator can take the responsibility of getting notified when one component is changed, and it can notify other components about the event so that further downstream updates can be made.

As an example, we can consider the use case of a graphical user interface (GUI). Suppose that we have a screen that contains three fields for our favorite banking application:

  • Amount: Current balance in the account.
  • Interest Rate: Current interest rate expressed as a percentage.
  • Interest Amount: Interest amount. This is a read-only field.

How do they interact with each other? If the amount is changed, then the interest amount needs to be updated. Likewise, if the interest rate is changed, then the interest amount needs to be updated as well.

To model the GUI, we can define the following types for the individual GUI objects onscreen:

abstract type Widget end

mutable struct TextField <: Widget
id::Symbol
value::String
end

The Widget is an abstract type and it can be used as the supertype for all GUI objects. This application only needs text fields, so we just define a TextField widget. A text field is identified by an id, and it contains a value. In order to extract and update the value in the text field widget, we can define functions as follows:

# extract numeric value from a text field
get_number(t::TextField) = parse(Float64, t.value)

# set text field from a numeric value
function set_number(t::TextField, x::Real)
println("* ", t.id, " is being updated to ", x)
t.value = string(x)
return nothing
end

From the preceding code, we can see that the get_number function gets the value from the text field widget and returns it as a floating-point number. The set_number function populates the text field widget with the provided numeric value. Now, we also need to create the application, so we conveniently define a struct as follows:

Base.@kwdef struct App
amount_field::TextField
interest_rate_field::TextField
interest_amount_field::TextField
end

For this example, we will implement a notify function to simulate an event that is sent to the text field widget after the user enters a value. In reality, the GUI platform typically performs that function. Let's call it on_change_event, as follows:

function on_change_event(widget::Widget)
notify(app, widget)
end

The on_change_event function does nothing else but communicate to the mediator (the app) that something has just happened to this widget. As for the app itself, here's how it handles the notification:

# Mediator logic - handling changes to the widget in this app
function notify(app::App, widget::Widget)
if widget in (app.amount_field, app.interest_rate_field)
new_interest = get_number(app.amount_field) * get_number(app.interest_rate_field)/100
set_number(app.interest_amount_field, new_interest)
end
end

As you can see, it simply checks whether the widget that is being updated is either the Amount or Interest Rate field. If so, it calculates a new interest amount and populates the Interest Amount field with the new value. Let's do a quick test:

function test()
# Show current state before testing
print_current_state()

# double principal amount from 100 to 200
set_number(app.amount_field, 200)
on_change_event(app.amount_field)
print_current_state()
end

The test function displays the initial state of the application, updates the amount field, and displays the new state. For the sake of brevity, the source code for the print_current_state function is not shown here, but is available on the book's GitHub site. The output of the test program is shown as follows:

The benefit of using the 2 mediator pattern is that every object can focus on its own responsibility and not worry about the downstream impact. A central mediator takes on the responsibility of organizing activities and handling events and communications.

Next, we shall look at the memento pattern.

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

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