Execute Around Method Pattern

Java programmers are familiar with the synchronized block. When we enter a synchronized block, it obtains a monitor (lock) on the given object. That monitor is automatically released when we leave the block. The release happens even if the code within the block throws an unhandled exception. That kind of deterministic behavior is nice to have in a number of other situations far beyond that specific example.

Thanks to function values, you can implement those constructs in Scala quite easily. Let’s look at an example.

We have a class named Resource that needs to start some transaction automatically and end the transaction deterministically as soon as we’re done using the object. We can rely on the constructor to correctly start the transaction. It’s the ending part that poses the challenge. This falls right into the Execute Around Method pattern (see Kent Beck’s Smalltalk Best Practice Patterns [Bec96]). We want to execute a pair of operations in tandem around an arbitrary set of operations on an object.

We can use function values to implement this pattern in Scala. Here is the code for the Resource class along with its companion object—see Stand-alone and Companion Objects for details on companion objects:

FunctionValuesAndClosures/Resource.scala
 
class​ Resource ​private​() {
 
println(​"Starting transaction..."​)
 
private​ ​def​ cleanUp() { println(​"Ending transaction..."​) }
 
def​ op1() = println(​"Operation 1"​)
 
def​ op2() = println(​"Operation 2"​)
 
def​ op3() = println(​"Operation 3"​)
 
}
 
 
object​ Resource {
 
def​ use(codeBlock: Resource => ​Unit​) {
 
val​ resource = ​new​ Resource
 
try​ {
 
codeBlock(resource)
 
}
 
finally​ {
 
resource.cleanUp()
 
}
 
}
 
}

We’ve marked the constructor of the Resource class private. Thus, we can’t create an instance of this class outside the class and its companion object. This design forces us to use the object in a certain way, thus guaranteeing automatic and deterministic behavior. The cleanUp method is declared private as well. The print statements are used as placeholders for real transaction operations. The transaction starts when the constructor is called and ends when cleanUp is implicitly called. The usable instance methods of the Resource class are methods like op1, op2, and so on.

In the companion object, we have a method named use that accepts a function value as a parameter. In the use method we create an instance of Resource, and within the safeguard of the try and finally blocks, we send the instance to the given function value. In the finally block, we call the private instance method cleanUp of the Resource. Pretty simple, eh? That’s all it took to provide a deterministic call to necessary operations.

Now let’s take a look at how we can use the Resource class. Here’s some example code:

FunctionValuesAndClosures/Resource.scala
 
Resource.use { resource =>
 
resource.op1()
 
resource.op2()
 
resource.op3()
 
resource.op1()
 
}

Here’s the output:

 
Starting transaction...
 
Operation 1
 
Operation 2
 
Operation 3
 
Operation 1
 
Ending transaction...

We invoke the use method of the Resource companion object and provide it with a code block. It sends an instance of Resource to us. By the time we get access to resource, the transaction has been started. We invoke the methods we desire—like op1 and op2—on the instance of Resource. When we’re done, at the time we leave the code block, the cleanUp method of the Resource is automatically called by the use method.

A variation of the previous pattern is described as the Loan pattern (see Appendix 2, Web Resources). Use it if your intent is to deterministically dispose of non-memory resources. The resource-intensive object is considered to be on loan, and we’re expected to return it promptly.

Here’s an example of how to use this pattern:

FunctionValuesAndClosures/WriteToFile.scala
 
import​ java.io._
 
 
def​ writeToFile(fileName: ​String​)(codeBlock : PrintWriter => ​Unit​) = {
 
val​ writer = ​new​ PrintWriter(​new​ File(fileName))
 
try​ { codeBlock(writer) } ​finally​ { writer.close() }
 
}

Now we can use the function writeToFile to write some content to a file:

FunctionValuesAndClosures/WriteToFile.scala
 
writeToFile(​"output/output.txt"​) { writer =>
 
writer write ​"hello from Scala"
 
}

When we run the code, the contents of the file output.txt are as follows:

 
hello from Scala

As a user of the method writeToFile, we don’t have to worry about closing the file. The file is on loan to us for use within the code block. We can write to the PrintWriter instance given to us, and upon return from the block, the file is automatically closed by the method.

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

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