The state pattern

The state pattern is used in situations where an object behaves differently depending on its internal state. A networking service is a great example. A typical implementation for a network-based service is to listen to a specific port number. When a remote process connects to the service, it establishes a connection, and they use it to communicate with each other until the end of the session. When a network service is currently in a listening state, it should allow a new connection to be opened; however, no data transmission should be allowed until after the connection is opened. Then, after the connection is opened, we should be able to send data. By contrast, we should not allow any data to be sent through the network connection if the connection is already closed.

In Julia, we can possibly implement the state pattern using multiple dispatch. Let's first define the following types that are meaningful for network connections:

abstract type AbstractState end

struct ListeningState <: AbstractState end
struct EstablishedState <: AbstractState end
struct ClosedState <: AbstractState end

const LISTENING = ListeningState()
const ESTABLISHED = EstablishedState()
const CLOSED = ClosedState()

Here, we have leveraged the singleton type pattern. As for the network connection itself, we can define the type as follows:

struct Connection{T <: AbstractState,S}
state::T
conn::S
end

Now, let's develop a send function, which is used to send a message via a connection. In our implementation, the send function does not do anything else except gather the current state of the connection and forward the call to a state-specific send function:

# Use multiple dispatch 
send(c::Connection, msg) = send(c.state, c.conn, msg)

# Implement `send` method for each state
send(::ListeningState, conn, msg) = error("No connection yet")
send(::EstablishedState, conn, msg) = write(conn, msg * " ")
send(::ClosedState, conn, msg) = error("Connection already closed")

You may recognize this as the Holy Trait pattern. For unit testing, we can develop a test function for creating a new Connection with the specified message and sending a message to the Connection object:

function test(state, msg)
c = Connection(state, stdout)
try
send(c, msg)
catch ex
println("$(ex) for message '$msg'")
end
return nothing
end

Then, the testing code simply runs the test function three times, once for each possible state:

function test()
test(LISTENING, "hello world 1")
test(CLOSED, "hello world 2")
test(ESTABLISHED, "hello world 3")
end

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

Only the third message was sent successfully, because the connection was in the ESTABLISHED state. Now, let's take a look at the strategy pattern.

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

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