Create a gist now

Instantly share code, notes, and snippets.

/* This is when a refactoring really pays off.
* In order to make your code more modular, avoid hard-coding assumptions (or refactor them away).
* The most fundamental, anti-modular assumption in Object-Oriented software is the concrete type of objects.
* Any time you write "new MyClass" in your code (or in Ruby you've hardcoded
* an assumption about the concrete class of the object you're allocating. These makes it impossible, for example,
* for someone to later add logging around method invocations of that object, or timeouts, or whatever.
* In a very dynamic language like Ruby, open classes and method aliasing mitigate this problem, but
* they don't solve it. If you manipulate a class to add logging, all instances of that class will have
* logging; you can't take a surgical approach and say "just objects instantiated in this context".
* There are standard design patterns to mitigate this, namely Dependency Injection and Factories.
* By taking a Factory (a function that manufactures objects) as a parameter to a function (or
* a constructor) you allow a programmer to later change his mind about what Factory to provide; and
* this means the programmer can change the concrete types of objects as his heart desires.
* There is another pattern, the Decorator pattern, that makes this even more powerful. Instead of
* instantiating class A instead of class B, you instantiate an A with a B, where A delegates all
* of its methods to B except where it needs to add "decorations" such as logging.
// In this example, I have a Query object, with methods like #execute(). I want to add timeouts around all queries. I start
// by creating a QueryProxy that routes all method invocations through an over-ridable method: #delegate:
abstract class QueryProxy(query: Query) extends Query {
def select[A](f: ResultSet => A) = delegate(
def execute() = delegate(query.execute())
def cancel() = query.cancel()
protected def delegate[A](f: => A) = f
// Then, to implement timeouts, I create a Query Decorator:
class TimingOutQuery(timeout: Duration, query: Query) extends QueryProxy(query) {
override def delegate[A](f: => A) = {
try {
Timeout(timeout) {
} {
} catch {
case e: TimeoutException =>
throw new SqlTimeoutException
// The implementation of the Timeout function is provided here for the sake of curiosities. It uses threads and is weird but cool.
object Timeout {
val timer = new Timer("Timer thread", true)
def apply[T](timeout: Duration)(f: => T)(onTimeout: => Unit): T = {
@volatile var cancelled = false
val task = if (timeout.inMillis > 0) Some(schedule(timeout, { cancelled = true; onTimeout })) else None
try {
} finally {
task map { t =>
if (cancelled) throw new TimeoutException
private def schedule(timeout: Duration, f: => Unit) = {
val task = new TimerTask() {
override def run() { f }
timer.schedule(task, timeout.inMillis)
// The thing that ties this all together is to make sure that nobody that needs to instantiate a Query object
// ever calls "new Query" directly. Provide instead a Factory:
class TimingOutQueryFactory(queryFactory: QueryFactory, timeout: Duration) extends QueryFactory {
def apply(connection: Connection, query: String, params: Any*) = {
new TimingOutQuery(timeout, queryFactory(connection, query, params: _*))
// Of course, this Factory takes another Factory, allowing Factories to be composed (so we have Factory Decorators that
// make Decorated Queries! So meta! Actually, "meta" in Greek means nothing like "meta" in English. "Meta" plus
// the dative means "after" so Aristotle's Metaphysics is actually just a book "after [the book on] physics". Anyway.
// The "root" QueryFactory in this onion is the simplest QueryFactory and it just calls "new Query" etc.
// The thing that made me excited tonight, though, is that I had to add a new feature: per-query timeouts. We had a global
// 3-second timeout and this was proving to be stupid given that our most common query has a latency of 0.5ms and a standard
// deviation of 2ms. If you have a global timeout you set your timeout around your most expensive query not your most
// common query. But for a production system, rare expensive queries are less likely to cause performance problems than frequent
// cheap queries getting slightly less cheap. So anyway, how many lines of code is it to make per-query timeouts??
class PerQueryTimeoutSimpleTimingOutQueryFactory(queryFactory: QueryFactory, timeouts: Map[String, Duration]) extends QueryFactory {
def apply(connection: Connection, query: String, params: Any*) = {
new TimingOutQuery(timeouts(query), queryFactory(connection, query, params: _*)) // YAY!
// BOOM. 1 LOC. That's what modularity means in practice.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment