scala command design pattern in scala with real world example

The Command Pattern is a behavioral design pattern in which an object is used to encapsulate all information needed to perform an action or trigger an event at a later time. The command object encapsulates a request by binding together a set of actions on a specific receiver. To achieve this, it packages the actions and the receiver up into an object that exposes just one method, execute(). When called, execute() causes the actions to be invoked on the receiver. From the outside, no other objects really know what actions get performed on what receiver they just know that if they call the execute() method, their request will be serviced.

A command pattern basically have below four components

Command – We can think of this as the interface and its implementations that are being called by the invoker.

Receiver – This is the object that actually knows how commands are executed. Think of this as an object that is being passed to the command and then used in the interface method.

Invoker – It invokes the commands by calling their interface method. As we mentioned earlier, it might not even know what commands are being invoked.

Client – It more or less guides which commands are executed when by using the invoker.

Below is the generic diagram for this pattern

 

Lets say we have scenario where we need to query different databases like a oracle db and hsql db to get different information. We want to completely encapsulate this so the clients can choose which ever database they want to choose and also the query information is completely embedded into the command object and clients just have to choose the right command object and the rest will be taken care by the command object which is also composed with a receiver which in this case is oracle or hsql database.

Lets code the four components of the command pattern

Receivers

We have two receivers now one is oracle and the other is hsql. Dbtype is the receiver interface which will be implemented by our concrete classes oracle and hsql which is composed of the jdbc template.


trait DbType {

def query[T](query:String,parameters:Array[String],rowmapper:RowMapper[T]):Vector[T]

}


class Oracle(jdbcTemplate:JdbcTemplate) extends DbType {

override def query[T](query: String, parameters: Array[String], rowmapper: RowMapper[T]):Vector[T] =
{

jdbcTemplate.query(query, parameters, rowmapper);

}
}


class Hsql(jdbcTemplate:JdbcTemplate) extends DbType {

//def query[T](query:String,parameters:Array[String],rowmapper:RowMapper[T]):Vector[T]

override def query[T](query: String, parameters: Array[String], rowmapper: RowMapper[T]):Vector[T] =
{

jdbcTemplate.query(query, parameters, rowmapper);

}
}

Command

QueryCommand is the command interface which will be implemented by all the commands . For this explanation we have two command object RawTableCommand and RawAggTableCommand which will implement the QueryCommand interface. The command object is composed of the receivers and the query action will be called on the receiver object.


import java.util.List;

trait QueryCommand[T] {

def execute(parameters:Array[String]):Vector[T];

}


class MetricTableCommand(dbType: DbType) extends QueryCommand[MetricMetadata] {

def execute(parameters: Array[String]): Vector[MetricMetadata] =
{
val query: String = "select * from metric_table"

dbType.query(query, parameters, new MetricMetadataMapper())
}

}


class MetricTableCommandSorted(dbType: DbType) extends QueryCommand[MetricMetadata] {

def execute(parameters: Array[String]): Vector[MetricMetadata] =
{
val query: String = "select * from metric_table order by METRIC_NAME desc"

dbType.query(query, parameters, new MetricMetadataMapper())
}

}

Invoker

The invoker invokes the commands by calling their interface method and it might not even know what commands are being invoked. QueryInvoker is composed of the query command and once the clients calls the execute method the QueryInvoker delegates the call to the composed command object.


class QueryInvoker[T](command: QueryCommand[T]) {

def execute(parameters: Array[String]): Vector[T] =
{
command.execute(parameters);
}

}

Below the client class which will call the required action using the QueryInvoker which takes a command object which in turn takes a receiver which can be of dbtype.


object Test extends App {

val queryInvokerOracle: QueryInvoker[MetricMetadata] = new QueryInvoker(new MetricTableCommand(new Oracle(new OracleTemplate())))

val list1: Vector[MetricMetadata] = queryInvokerOracle.execute(Array("para1", "para2"))

val queryInvoker: QueryInvoker[MetricMetadata] = new QueryInvoker(new MetricTableCommand(new Oracle(new HsqlTemplate())))

val list2: Vector[MetricMetadata] = queryInvoker.execute(Array("para1", "para2"))

}

Some domain objects used

case class MetricMetadata(id: Int, metricName: String, metricValueType: String) extends Model {

}


class MetricMetadataMapper extends RowMapper[MetricMetadata] {

def mapRow(rs: ResultSet, rowNum: Int): MetricMetadata =
{
MetricMetadata(rs.getInt("METRIC_METADATA_ID"), rs.getString("METRIC_NAME"), rs.getString("METRIC_VALUE_TYPE"));
}

}