Created
July 6, 2012 18:43
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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