Skip to content

Instantly share code, notes, and snippets.

@eboto
Created July 6, 2012 18:43
Show Gist options
  • Save eboto/3061968 to your computer and use it in GitHub Desktop.
Save eboto/3061968 to your computer and use it in GitHub Desktop.
A demonstration of how to make composition a first (or 1.5th) class citizen in Scala OO code
// A demonstration of how to make composition a first (or 1.5th) class
// citizen in Scala OO code, as it is better to favor composition
// over inheritance.
//
// The basic idea is to create a sister trait named "MyTraitComposition" to your
// base trait. The composition trait specifies an abstract member "delegate"
// and delegates the full MyTrait interface to that delegate. Later on,
// concrete implementations can implement MyTrait with MyTraitComposition
// to easily extend the behavior of another existing concrete implementation
// via composition.
//
// This demonstration uses the example of a trait called Blobstore. Ignore
// the unsavory use of null.
//
/**
* A place to store binary data. This is the base trait of which we
* will be making multiple implementations
*/
trait Blobstore {
def put(keyValue: (String, AnyRef))
def get(key: String): AnyRef
def url(key: String): String
}
/**
* Sister trait to our base trait -- this allows different implementations
* to easily DELEGATE rather than INHERIT behavior from each other without
* wasted syntax.
*/
trait BlobstoreComposition extends Blobstore {
protected def blobstoreDelegate: Blobstore
override def put(keyValue: (String, AnyRef)) = {
blobstoreDelegate.put(keyValue)
}
override def get(key: String): AnyRef = {
blobstoreDelegate.get(key)
}
override def url(key: String): String = {
blobstoreDelegate.url(key)
}
}
/**
* First implementation: Let's go straight to S3 for everything
*/
class S3Blobstore extends Blobstore {
def put(keyValue: (String, AnyRef)) {
println("PUT " + keyValue + "INTO S3 ")
}
def get(key: String): AnyRef = {
println("GOT " + key + "FROM S3")
null
}
def url(key: String): String = {
"s3.amazonaws.com/mybucket/" + key
}
}
/**
* Second implementation: delegate to any other blobstore implementation, but
* cache the results in a DB. Notice we only override what we have to override
* (we choose not to override urlOption)
*/
class DBIndexedBlobstore (protected val blobstoreDelegate: Blobstore)
extends Blobstore
with BlobstoreComposition
{
// Notably, we are overriding the trivial BlobstoreComposition implementation
// of get, not the delegate's.
override def put(keyValue: (String, AnyRef)) {
// Cache the results here and then delegate.
dbPut(keyValue)
blobstoreDelegate.put(keyValue)
}
override def get(key: String): AnyRef = {
val fromDb = dbGet(key)
if (fromDb == null) {
blobstoreDelegate.get(key)
} else {
null
}
}
//
// Private members
//
private def dbPut(keyValue:(String, AnyRef)) = {
null
}
private def dbGet(key: String): AnyRef = {
null
}
}
// Scala harness
object DecoratorTest {
def main(args: Array[String]) {
println("")
println("It ran jist fine")
println("")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment