A deep dive into the critical aspects of becoming an effective technical leader and architect Introduction Venkat Subramaniam, a renowned software developer, consultant, and author, shares valuable insights into becoming an effective technical leader and architect in a recent The […]
programming
Test Driven Development with Frederik Banke
In this podcast we talked about TDD with an expert in the domain. Frederik has been in the industry for more than 15 years and he is an advocate of Clean Coding practices and teaches Software Engineers how to write […]
Is Monolith Architecture Bad? Are Microservices Always Good? Conversation with Anant.
In this conversation we talk to Ananth about his experience with Microservices and Monlithic architectures. We also look at various examples of a good/bad monolith, good/bad micro services. We also talk about our personal experience with different architectural styles and […]
What is Decorator Pattern?
This pattern lets a programmer add more functionality to an object without impacting other objects of the class. So off course without inheritance
Why do you need it?
At times we need our objects to have more functionality based on different use cases. One way to achieve this is via Inheritance, but it is not always possible and it impacts all objects of the class, which might not be suitable.
Let’s build something in Scala to understand why we need it and how we can use Scala Traits to achieve it.
DB
trait represents a Key,Value Database with simple functions for fetching, updating and deleting data.
trait DB { def getData(k: String): String def putData(k: String, v: String): Unit def deleteData(k: String): Unit }
We have a SimpleDB
which implements this trait. It holds key -> value
pairs in memory using a simple mutable
map.
class SimpleDB extends DB { private val dbData = scala.collection.mutable .Map[String, String]() override def putData(key: String, value: String): Unit = { dbData.put(key, value) } override def deleteData(key: String): Unit = { dbData.remove(key) } override def getData(id: String): String = { dbData.get(id).getOrElse("no value found") } }
We would like to separate clients based on their access rights. Some clients may be read-only
access, some may also have write-access
and administrators can have delete-access
as well.
Let’s define some traits for different types of access.
Provides read access only
trait ReadOnly { val db: DB def getData(id: String): String = db.getData(id) }
Provides additional write access
trait Writer extends ReadOnly {
val db: DB
def deleteData(key: String) = db.deleteData(key)
def putData(key: String, value: String) = db.putData(key, value)
}
So now we have a simple database and some interfaces to access data based on access rights.
Let’s define simple clients to access our Database.
class DBClient(override val db: DB) extends ReadOnly
By default all clients will have a read-only
access.
Now how do we create clients with write access as well?
We have two options:
- Create a new client and extend
Writer
trait.- This is ok if you have only two such cases, but what if you have more flavors ? Will you create new clients for each one?
- On the same client extend
Writer
trait as well.- This means there won’t be any read-only client all will have write access as well.
- Also sometimes you won’t have control on changing the class definition, may be because its a third party library?
Third option goes something like this:
//Create a db val simpleDB = new SimpleDB //A read only client val readOnlyClient = new DBClient(simpleDB)
when we create a new client (read-only) using a with
keyword you can add additional feature of other Traits.
val writeableClient = new DBClient(simpleDB) with Writer
What this means is that only this object of DBClient
will have the write-access
without impacting any other object.
Just to have another example lets say to improve the performance of read calls we want to add caching functionality to our clients. How do we do that? Another trait and then mix it up when we need it.
Caching trait can add caching functionality to any client. mutable.Map
‘s getOrElseUpdate
takes a key and function to fetch the value if not already present in the cache. Here super.getData
will call ReadOnly clients getData
function
//Trait which adds a caching functionality trait Caching extends ReadOnly { //caches data val data = scala.collection.mutable.Map[String, String]() override def getData(key: String): String = { data.getOrElseUpdate(key, super.getData(key)) } }
Create a caching client as follows :
val cachingClient = new DBClient(simpleDB) with Caching
Test our clients:
readOnlyClient.getData("1") writeableClient.putData("3", "value of 3") readOnlyClient.getData("3") cachingClient.getData("3") writeableClient.deleteData("3") readOnlyClient.getData("3")
Note : This is just a simple implementation to demonstrate Scala mixin as Decorator pattern. The caching and database implementations are far from ideal and in real world we will have more sophisticated libraries to access database and implement caching. But it can still be taken as a general pattern to wrap underlying clients to add or hide functionality. Cheers, Kaivalya
Playing with Scala – Implementing Filters
Different approaches of implementing a filter function on a collection in Scala.
Functional Refactoring
One of the biggest challenges most software engineers face is, to maintain a badly written code. Refactoring code is a very difficult task, which can break a lot of things in a so called working software. There are only two […]
Hypermedia Driven Rest APIs – HATEOAS
What is HATEOAS ? (Hate-OAS, Hateous or any other pronunciation) Hypermedia as the Engine of Application State It basically means that your application state should be Hypermedia driven. Hypermedia ? – Basically hyperlinks to other resources. Which also means that, […]
Change Elastic Search Mapping
Remapping elastic search index For every change in the mapping or settings of the elastic search index, we have to create a new index as there is no way to edit an existing index. So here are the basic requests […]
Optimistic Boolean Functions
Every developer encounters situations on a day to day basis where he has to write a code that flows based on some condition, and every condition eventually resolves to a Boolean. When conditions become complex enough to be part of […]
Test Driven Bug Fixing – Guidelines
XTreme programming advocates that developers should do things in a certain way, to be able to function smoothly and eventually have a shorter time to market. It is opinionated and strict about developer practices. One of those practices is doing Test Driven Development.
- 1
- 2