It's often desirable to build wrappers around existing objects, implementing the same interface. As an example one can wrap a java.sql.Connection object with a wrapper that measures timing data (how long the underlying java.sql.Connection spent in blocking calls). In fact, it's often useful to build a sequence of wrappers, each wrapper handling one specific layer of functionality. In the normal Java paradigm, this involves a lot of duplicate code - each method must be reimplemented at each wrapper layer.

Another place where code is often duplicated is when building compound objects that inherit from multiple traits. Consider a VideoAdvertisement class which must implement an AdvertisementLike interface - it's useful to build a constructor for VideoAdvertisement which takes (Advertisement, Video) as arguments, and most of the constructor will simply copy fields from Advertisement onto VideoAdvertisement.

Enter the HasXIsX pattern.

Consider an underlying AdvertisementLike trait:

trait AdvertisementLike {
  def advertiser: Advertiser
  def campaign: Campaign
  def target: URL
}

Now consider the VideoAdvertisement:

case class VideoAdvertisement(advertiser: Advertiser, campaign: Campaign, target: URL, video: Video)
       extends AdvertisementLike

Depending on how the data storage layer is set up, we probably need to build a constructor like this:

object VideoAdvertisement {
  def apply(advertisement: AdvertisementLike, video: Video) =
    new VideoAdvertisement(advertisement.advertiser, advertisement.campaign, advertisement.target, video)
}

A lot of redundant code is created if we have multiple advertisement types - FlashAdvertisement, PopupAdvertisement, etc. The HasXIsX pattern can solve this:

trait HasAdvertisementIsAdvertisement extends AdvertisementLike {
  protected val advertisement: AdvertisementLike
  def advertiser: Advertiser = advertisement.advertiser
  def campaign: Campaign = advertisement.campaig
  def target: URL = advertisement.target
}

Then we redefine VideoAdvertisement as follows:

case class VideoAdvertisement(advertisement: AdvertisementLike, video: Video)
       extends HasAdvertisementIsAdvertisement

If we similarly had a HasVideoIsVideo trait, it would be very easy for VideoAdvertisement to be both an AdvertisementLike and a VideoLike.

This method is also very handy for building wrappers. For example, I build a ConnectionWrapper class in my Tiramisu SQL library:

trait ConnectionWrapper extends java.sql.Connection {
  protected val conn: java.sql.Connection

  protected def methodWrap[T](f: =>T): T = f
  ...etc...

  def abort(x: java.util.concurrent.Executor): Unit = methodWrap { conn.abort(x) }
  def close(): Unit = methodWrap { conn.close() }
  def commit(): Unit = methodWrap { conn.commit() }

  protected def wrapPreparedStatement(stmt: java.sql.PreparedStatement) = stmt
  def prepareStatement(x: String): java.sql.PreparedStatement =
    wrapPreparedStatement(methodWrap { conn.prepareStatement(x) })

  ...etc...
  }

This saves me the trouble of re-wrapping every method when I build the TimingSqlConnection class:

class TimingSqlConnection(protected val conn: java.sql.Connection) extends ConnectionWrapper {

  //new functionality
  def getTime: Long = timeCounter.get

  private def time[T](f: =>T): T = {...timing code...}

  override protected def methodWrap[T](f: =>T): T = time(f)

  protected case class TimingPreparedStatement(protected val stmt: java.sql.PreparedStatement) extends PreparedStatementWrapper {
    override def methodWrap[T](f: =>T): T = time(f)
    override def wrapResultSet(rs: java.sql.ResultSet) = TimingResultSet(rs)
  }

  override protected def wrapPreparedStatement(stmt: java.sql.PreparedStatement) = TimingPreparedStatement(stmt)
  ...etc...
}

I.e., I get to avoid manually writing:

def close(): Unit = time(conn.close())
def commit(): Unit = time(conn.commit())
...repeat for every method

Take a look at the utils folder of the Tiramisu repo, where I use this pattern to build a couple of handy wrappers.

Anyway, this is just a simple pattern I've found to save me a lot of code. Nothing groundbreaking, just a nice little use of scala traits.


Subscribe to the mailing list


Comments

comments powered by Disqus