Wednesday, November 26, 2008

On the Use of Template Methods

I find the template method design pattern too easy to get wrong, not trivial to maintain, and downright unnecessary--considering the alternatives--to be worth the trouble. The intent of the template method pattern as postulated by the gang of four is to "define the skeleton of an algorithm in an operation, deferring some steps to subclasses" [1]. In the Applicability section, the authors include a couple more scenarios when the pattern "should", be used:
  • "when common behavior among subclasses should be factored and localized in a common class" [1]

  • when implementing "hook operations": methods that get called at certain points in the execution of an algorithm that children may override.

I am yet to see a convincing case when the template method is the best solution for any object-oriented design problem. Here's why:

Easy To Get Wrong and Suboptimal Even When Implemented Properly

  1. When using template methods as concrete steps in the skeleton of an algorithm, the parent class--the one containing the operation skeleton--must be explicit in what step methods children must override and what methods/steps children may override. A way of communicating this in Java is having must-override methods be abstract (or pure virtual in C++) while having may-override methods have an non-final implementation in the parent class. Parents' providing default implementations, however minimal or convenience no-ops, to methods child classes must override anyway is unnecessary and confusing to readers and clients of the code.

  2. The concrete implementation of template methods in the concrete class must be final. Otherwise, allowing further inheritance of a concrete class that provides template method implementations, creates a maintenance mess as the only way to ever extends behavior is through further inheritance.


Putting 1 and 2 together we get the following:


public class FancyReportSender {
public void doSend() {
try {
// Do fancy work
Connection c = openConnection();
if (isConnectionOpen()) {
// Do more fancy work
// Send data using connection c
}
} finally {
closeConnection();
}
}
abstract protected Connection openConnection();
abstract protected boolean isConnectionOpen();
abstract protected void closeConnection();
}

public final class SerialCommFancyReportSender extends FancyReportSender {
protected Connection openConnection() {
// open and return a serial port connection
}
protected boolean isConnectionOpen() {
// return if connection was open fine ...
}
protected void closeConnection() {
// close it ...
}
}
Code Listing 1: Standard template method implementation

Some immediate problems with the approach above is that we have coupled the sender with the serial connection piece in a IS-A relationship. Besides just the standard composition-wins-over-inheritance argument, we see that we have coupled the responsibility of doing the sending with the responsibility of opening a serial type connection in the SerialCommFancyReportSender class. As a result, should we need to open a serial connection in a context other than the FancyReportSender, our SerialCommFancyReportSender is not of much use.

Furthermore, subclassing is the only way to provide these concrete connection-management steps: SerialCommFancyReportSender must extend the FancyReportSender class--as opposed to, say, implementing an interface--and this fact further restricts the class that might provide that concrete behavior.

And lastly, in practice, the line between what must and what may be overriden in a child class using template methods can get blurry over time. When common functionality exists for a given step, future developers maintaining the code might be tempted to push that commonality to the common base and expect client code to call super. Things can get sloppy very quick.

Consider the following composition alternative to template methods:

public class FancyReportSender {
private final IConnectionProvider m_connectionProvider;
public FancyReportSender (IConnectionProvider connectionProvider) {
m_connectionProvider = connectionProvider;
}
public void doSend() {
try {
Connection c = m_connectionProvider.openConnection();
if (m_connectionProvider.isConnectionOpen()) {
// do stuff with connection c
}
} finally {
m_connectionProvider.closeConnection();
}
}
}

public final class SerialConnectionProvider implements IConnectionProvider {
public Connection openConnection() {
// open and return connection on a serial port
}
public boolean isConnectionOpen() {
// return if connection was open fine ...
}
public void closeConnection() {
// close it ...
}
}

public interface IConnectionProvider {
Connection openConnection();
boolean isConnectionOpen();
void closeConnection();
}
Code Listing 2: Composition Alternative to Template Methods

This composition alternative addresses the problems above and leaves greater flexibility for clients providing the concrete steps.

Not Trivial To Maintain
Since template methods set the tone of class extension as the way of extending class behavior, in practice one often sees the application of template methods deteriorate to something like the following:

Diagram 1: Template Methods Pave the Way to Vertical Design

As a result, the class hierarchy can get very vertical, very fast. Concrete template method implementations often end up on a various "floors" in the hierarchy, and, at coding time, inspecting a piece of code in CommConnectionFancySender, say, presents a challenge to determine on what floor a method implementation that will get called at run-time, really lives.

In such a design, class attributes end up spread all over the hierarchy, and there is no protection against a future developer maintaining the code and reducing data visibility of parent data from private to protected in order to make it accessible to children.

While debugging, what we see is that stepping-into a method can cause the program counter to jump up and down the hierarchy, binding to the right method: at any given instant, for a class in such a design, a method call can "land" in a parent, a child or in the class itself.

For a project of a reasonable size, we end up with a maintenance gem on our hands.

Template Methods as Hook Operations

If we recall the GOF definition of hook operations, these are operations that get called by the parent at times when they wish to give control to their children classes. Therefore, by their very nature hook operations promote temporal cohesion, as clients often end up sticking code in the hook implementation that is there mostly because of when it needs to get called: code that ends up in concrete versions of the various Eclipse RCP advisors, such as WorkbenchWindowAdvisor.preWindowRestore() are a good case in point. Plus, when implementing hook methods as template methods, we design in the unnatural restriction that hooking in can happen only by class extension.

As an alternative to hook methods, a plain callback interface can be used: at the very least, it won't require class extending to insert an hook. But a callback interface will still keep the temporal cohesion problem.

Some variation of the observer-subject pattern is probably a better fit: it will allow orthogonal operations to register separate concrete observers, and get notified separately, thereby avoiding the temporal cohesion in a single method.

Final Thought

Template methods are not evil. But I, for one, have not come across a case when they were ever the best design decision.

Monday, November 17, 2008

Clojure meets Behavior-Driven Development

While studying Clojure and doodling with possible usages of its smooth interop with Java, FEST came to mind: a clever, fluent-inteface GUI-driven functional testing framework for Swing.

Clojure brings to the table a dynamic, highly expressive language that reads as a script but is compiled to Java bytecode on the fly. FEST brings festivities and testing to the picture: it provides fluent Java interfaces for driving Swing UIs, as well as an assertions library. My curiosity was piqued at giving BDD a shot at this. Here's my notes:

My proof-of-concept case tests a simple login frame: prosaic, but illustrates the idea. We are given a requirement that the "Sign in" button should be enabled only when a username and password are provided. The "Cancel" button must always be enabled. The user story in BDDspeak and Clojure
(ns conducta.login-sample)
(use 'conducta.bdd)
(use 'conducta.ui)

(User-story "As a system user, I want to be able to submit my credentials, so that I can access my account.")

(defn Enter-credentials-submit-enabled-scenario
"The 'Submit' button is disabled by default. When the user types in their username and password, the 'Submit' button turns enabled."
[username password role signin]

(Given "that the sign-in button is disabled")
(require-disabled! signin)

(When "username and password are typed in")
(enter-text "bobsmith" username)
(enter-text "bobspassword" password)

(And "user is selected")
(select-item "User" role)

(Then "sign-in button should become disabled")
(require-enabled! signin)
)


(defn Login-button-disabled-on-clearing-scenario
"Having enabled the 'Submit' button on entering username and the password, the user deletes the username. The 'Submit' button must then turn back to disabled"
[username password signin]

(require-disabled! signin)
(enter-text "bobsmith" username)

(When "password is entered and then removed")
(enter-text "bobspassword" password)
(delete-text password)

(Then "sign-in button should turn disabled")
(require-disabled! signin)
)

(defn User-cancels-login-scenario
"'Cancel' button is enabled whether we have a username or password entered."
[username password cancel]
(require-enabled! cancel)
(enter-text "bobsmith" username)
(require-enabled! cancel)
(delete-text username)
(require-enabled! cancel)
(enter-text "bobspassword" password)
(require-enabled! cancel)
(delete-text password)
(require-enabled! cancel)
)

Code Listing 1: login-sample.clj

Good points about this approach:
  1. Terse, reads intuitively
  2. Phrased in natural imperative sentences: non-technical staff must have no problem reading or even creating/maintaining this
  3. Runs as JUnit (see more below). As such, it benefits from all ANT tools, IDE integration, and can be hooked with the continious build.
  4. It is a Clojure script: interactive, can be tested from the Repl, plus extendability comes cheap. For example, extending it with higher level constructs using macros and functions--first class citizens in Clojure--does not require parser changes (unlike an ad-hoc DSL). To illustrate, say we need to write an integration test that does something after login. So development can create the following login function
  5. (defn login
    "Authenticates a test user"
    []
    (enter-text "user01" username)
    (enter-text "tkv123" password)
    (select-item "User" role-selector)
    (click submit)
    )

    So, non-technical staff reading the scenario now sees

    (login)
    (select-item "My Account" navigator-bar)
    ;; ... and so on.
    The "log in" action got promoted to a higher-level abstraction. Duplication and verbosity were avoided with a minimum effort.
  6. The script is potentially UI-toolkit- and UI-component-agnostic. Code like
    (select-item "User" role)
    binds at runtime, and can execute as "select 'User' from a combo box","select a radio button, labeled 'User'" and so on. Also, select-item could dispatch to a Swing, SWT or web bot call.
  7. A user reading Code Listing 1 need not know a thing about JUnit or fixtures. That is managed by the JUnit portion of this solution (Code Listing 2) which is dev-land.
The output from running the user story in REPL:

.user=> (. junit.textui.TestRunner run conducta.FESTTest)
As a system user, I want to be able to submit my credentials, so that I can acce
ss my account.
.
Starting scenario: The 'Submit' button is disabled by default. When the user
types in their username and password, the 'Submit' button turns enabled.
Given that the sign-in button is disabled
When username and password are typed in and user is selected
Then sign-in button should become disabled
.
Starting scenario: Having enabled the 'Submit' button on entering username a
nd the password, the user deletes the username. The 'Submit' button must then tu
rn back to disabled
When password is entered and then removed
Then sign-in button should turn disabled
.
Starting scenario: 'Cancel' button is enabled whether we have a username or
password entered.

Time: 15.685

OK (3 tests)
Output Listing 1: Running login-sample.clj in the REPL

The JUnit Test Case that will drive the user story is listed below. First, I stubbed a FESTTest.class with

user=> (gen-and-save-class "." "conducta.FESTTest" :extends junit.framework.Test
Case :methods [["testSubmitEnabledOnCredentials" [] (Void/TYPE)]["testDisabledOn
ClearingText" [] (Void/TYPE)]["testCancelEnablement" [] (Void/TYPE)] ])

Next, generated FESTTest.clj that will
  • Create FEST FrameFixture in setUp() method, and child widgets' fixtures
  • Call individual scenarios in every test* method
  • Tear down the fixture in cleanUp()

(ns conducta)
(use 'conducta.ui)
(use 'conducta.login-sample)
(use 'conducta.bdd)

(import '(org.fest.swing.fixture FrameFixture))
(import '(com.foo MyFrame))

(def frameFixture)
(def start-scenario-prefix " Starting scenario: ")

;; set up the frame fixture
(defn FESTTest-setUp [_]
(def frameFixture (FrameFixture. (MyFrame.)))
(.show frameFixture)
(def username (widget (text frameFixture "username")))
(def role (widget (combo frameFixture "selector")))
(def password (widget (text frameFixture "password")))
(def signin (widget (button frameFixture "signInButton")))
(def cancel (widget (button frameFixture "cancelButton")))
)

;; Actual tests
(defn FESTTest-testSubmitEnabledOnCredentials [_]
;; TODO: Change to using a lazy seq and evaluate
(println)
(println start-scenario-prefix
(get ^#'Enter-credentials-submit-enabled-scenario :doc))
(Enter-credentials-submit-enabled-scenario username password role signin)
)

(defn FESTTest-testDisabledOnClearingText [_]
(println)
(println start-scenario-prefix
(get ^#'Login-button-disabled-on-clearing-scenario :doc))
(Login-button-disabled-on-clearing-scenario
username password signin)
)


(defn FESTTest-testCancelEnablement [_]
(println)
(println start-scenario-prefix (get ^#'User-cancels-login-scenario :doc))
(User-cancels-login-scenario username password cancel)
)

;; Other GUI tests ... Test domain-agnostic GUI things: tab traversal order,
;; resizing, etc.

;; clean-up code
(defn FESTTest-tearDown [_] (.cleanUp frameFixture))

Code Listing 2: The JUnit test case implemented in Clojure and driving the GUI using FEST

Finally, the code that dispatches Clojure to FEST APIs or potentially other UI driving frameworks, like SWTBot.

(ns conducta.ui)
(import '(org.fest.swing.fixture FrameFixture))

(defmulti widget :Component)
(defn combo [window name] {:Component :combo :window window :name name})
(defn text [window name] {:Component :text :window window :name name})
(defn button [window name] {:Component :button :window window :name name})

;; TODO pull code below in a separate fest-swing.clj
(defmethod widget :combo [c] (. (:window c) comboBox (:name c)))
(defmethod widget :text [t] (. (:window t) textBox (:name t)))
(defmethod widget :button [b] (. (:window b) button (:name b)))

;; Text related
(defn enter-text [text widget] (.enterText widget text))
(defn delete-text [widget] (.deleteText widget))
(defn select-item [item widget] (.selectItem widget item))

;; General
(defn click [widget] (.click widget))
(defn focus [widget] (.focus widget))

;; require*
(defn require-enabled! [widget] (.requireEnabled widget))
(defn require-disabled! [widget] (.requireDisabled widget))

Code Listing 3: ui.clj the code that bridges to FEST

Project Conducta is in prototype stages. I have not added the code to source control yet, but will do so over the weekend. It is set up with Apache2.0 license.

Feedback, ideas on idiomatic Clojure are always welcome.