Skip to content

Instantly share code, notes, and snippets.

@akirillov
Last active July 13, 2016 08:41
Show Gist options
  • Save akirillov/178edb406e6a7368eebdf942f92d4ff3 to your computer and use it in GitHub Desktop.
Save akirillov/178edb406e6a7368eebdf942f92d4ff3 to your computer and use it in GitHub Desktop.
Example of stackable traits in Scala
type Payload = Array[Byte]
trait Driver {
def send(payload: Payload)
}
//a component which depends on Driver interface
class Sender(val driver: Driver){
def send(message: String) = driver.send(message.getBytes)
}
object Sender{
def apply(driver: Driver): Sender = new Sender(driver)
}
//3rd-party implementation of the Driver
class NetworkDriver extends Driver{
override def send(payload: Payload): Unit = {
println("..transferring..")
Thread.sleep(50)
}
}
//now developers decided to log the payload
trait PayloadLogging extends Driver {
abstract override def send(payload: Payload): Unit ={
println(s"sending the payload: ${payload.mkString}")
super.send(payload)
}
}
//and measure the time spent in method
trait TimeLogging extends Driver {
abstract override def send(payload: Payload): Unit ={
val start = System.currentTimeMillis()
super.send(payload)
print(s"time spent: ${System.currentTimeMillis() - start}ms")
}
}
//legacy implementation
Sender(new NetworkDriver).send("hello")
/*output:
..transferring..
*/
//mixing in payload logging
Sender(new NetworkDriver with PayloadLogging).send("hello")
/*output:
sending payload: 104101108108111
..transferring..
*/
//mixing in payload and time logging
Sender(new NetworkDriver with PayloadLogging with TimeLogging).send("hello")
/*output:
sending payload: 104101108108111
..transferring..
time spent: 56ms
*/
trait Writer {
def write(msg: String)
}
class ConsoleWriter extends Writer {
override def write(msg: String): Unit = println(msg)
}
trait UppercaseFilter extends Writer {
abstract override def write(msg: String): Unit = {
super.write(msg.toUpperCase)
}
}
trait AdFilter extends Writer {
abstract override def write(msg: String): Unit = {
super.write(msg.replace("ad", "[ad blocked]"))
}
}
class Publisher(val writer: Writer){
def publish(msg: String) = writer.write(msg)
}
object Publisher{
def apply(writer: Writer): Publisher = new Publisher(writer)
}
Publisher(new ConsoleWriter).publish("my awesome ad")
/*output:
my awesome ad
*/
Publisher(new ConsoleWriter with UppercaseFilter).publish("my awesome ad")
/*output:
MY AWESOME AD
*/
Publisher(new ConsoleWriter with UppercaseFilter with AdFilter).publish("my awesome ad")
/*output:
MY AWESOME [AD BLOCKED]
*/
Publisher(new ConsoleWriter with AdFilter with UppercaseFilter).publish("my awesome ad")
/*output:
MY AWESOME AD
ad replacement is case sensistive, so after UppercaseFilter is Applied
there's no 'ad' anymore
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment