OOD: class hierarchy with method arguments forming another hierarchy

  softwareengineering

I’d like to find out how do you guys handle the following situation: you have a class hierarchy, call it H1, with some polymorphic method that is supposed to accept an argument which type forms hierarchy H2 in the following way: the higher the class is in H1, the higher H2 argument it accepts.

+-----------+           +-----------+
|     A     |           |     A'    |
|-----------+           +-----------+
|method(A') |                 ^
+-----------+                 |
      ^                 +-----------+ 
      |                 |     B'    |
+-----------+           +-----------+
|     B     |                 ^
|-----------+                 |
|method(B') |           +-----------+ 
+-----------+           |     C'    |
      ^                 +-----------+
      |
+-----------+
|     C     |
|-----------+
|method(C') |
+-----------+

Before writing any code I mention that I have two protocol classes with methods check that have much in common, but request parameters are specific for concrete arguments — transactions.
The language is scala, but the problem is language-agnostic.
At the first glance it should look like that:

trait BaseMerchantProtocol
{
  def check(transaction: BaseTransaction) = {
    // some common code...
    println(requestParams(transaction))
    // ...and here as well
  }

  protected def requestParams(transaction: BaseTransaction)
}

class SmsCommerceMerchantProtocol extends BaseMerchantProtocol
{
  override protected def requestParams(transaction: SmsCommerceTransaction) = {
    List("result specific for SmsCommerceTransaction class")
  }
}

class TelepayMerchantProtocol extends BaseMerchantProtocol
{
  override protected def requestParams(transaction: TelepayTransaction) = {
    List("result specific for TelepayTransaction class")
  }
}

But of course it does not compile as Liskov substitution principle is violated.

Let’s try this one:

trait IMerchantProtocol {
  def check(transaction: SmsCommerceTransaction)
  def check(transaction: TelepayTransaction)
}

class MerchantProtocol extends IMerchantProtocol
{
  def check(transaction: SmsCommerceTransaction) = {
    doCheck(transaction)
  }

  def check(transaction: TelepayTransaction) = {
    doCheck(transaction)
  }

  private def requestParams(transaction: SmsCommerceTransaction) = {
    List("result specific for SmsCommerceTransaction class")
  }

  private def requestParams(transaction: TelepayTransaction) = {
    List("result specific for TelepayTransaction class")
  }

  private def doCheck(transaction: BaseTransaction) = {
    // some common code...
    println(requestParams(transaction))
    // ...and here as well
  }
}

But that won’t compile as well — and with the same reason: doCheck accepts BaseTransaction, but requestParamss have more strict preconditions.

The only thing that I came up with and that works is the following:

class MerchantProtocol extends IMerchantProtocol
{
  def check(transaction: SmsCommerceTransaction) = {
    doCheck(transaction, requestParams(transaction))
  }

  def check(transaction: TelepayTransaction) = {
    doCheck(transaction, requestParams(transaction))
  }

  private def requestParams(transaction: SmsCommerceTransaction) = {
    List("result specific for SmsCommerceTransaction class")
  }

  private def requestParams(transaction: TelepayTransaction) = {
    List("result specific for TelepayTransaction class")
  }

  private def doCheck(transaction: BaseTransaction, requestParams: List[String]) = {
    // some common code...
    println(requestParams)
    // ...and here as well
  }
}

But I don’t like that all check methods belong to the same class.
How can I split them by classes?

12

There is one piece in OOP toolset, that allows you to enforce this kind of design : constructors.

Implement handling classes, so it accepts the request class inside it’s constructor. This allows you to specify more concrete version of request class, while not breaking any kind of virtual method or class polymorphism. Concrete handler will require concrete request in it’s constructor, while passing it’s generic form to it’s predecessor.

The problem then gets shifted to how to create concrete handler for concrete type of request. You could use “downchast check” or maybe a Visitor pattern, if you want compile-time typechecking. Encapsulating it in Factory class is given. This problem I believe is the core of your concern. If you can ensure correct handler is paired with correct request, then no problems can occur. Using constructors for parameters ensures, that the handler cannot be called incorrectly outside this factory.

1

Theme wordpress giá rẻ Theme wordpress giá rẻ Thiết kế website Kho Theme wordpress Kho Theme WP Theme WP

OOD: class hierarchy with method arguments forming another hierarchy

I’d like to find out how do you guys handle the following situation: you have a class hierarchy, call it H1, with some polymorphic method that is supposed to accept an argument which type forms hierarchy H2 in the following way: the higher the class is in H1, the higher H2 argument it accepts.

+-----------+           +-----------+
|     A     |           |     A'    |
|-----------+           +-----------+
|method(A') |                 ^
+-----------+                 |
      ^                 +-----------+ 
      |                 |     B'    |
+-----------+           +-----------+
|     B     |                 ^
|-----------+                 |
|method(B') |           +-----------+ 
+-----------+           |     C'    |
      ^                 +-----------+
      |
+-----------+
|     C     |
|-----------+
|method(C') |
+-----------+

Before writing any code I mention that I have two protocol classes with methods check that have much in common, but request parameters are specific for concrete arguments — transactions.
The language is scala, but the problem is language-agnostic.
At the first glance it should look like that:

trait BaseMerchantProtocol
{
  def check(transaction: BaseTransaction) = {
    // some common code...
    println(requestParams(transaction))
    // ...and here as well
  }

  protected def requestParams(transaction: BaseTransaction)
}

class SmsCommerceMerchantProtocol extends BaseMerchantProtocol
{
  override protected def requestParams(transaction: SmsCommerceTransaction) = {
    List("result specific for SmsCommerceTransaction class")
  }
}

class TelepayMerchantProtocol extends BaseMerchantProtocol
{
  override protected def requestParams(transaction: TelepayTransaction) = {
    List("result specific for TelepayTransaction class")
  }
}

But of course it does not compile as Liskov substitution principle is violated.

Let’s try this one:

trait IMerchantProtocol {
  def check(transaction: SmsCommerceTransaction)
  def check(transaction: TelepayTransaction)
}

class MerchantProtocol extends IMerchantProtocol
{
  def check(transaction: SmsCommerceTransaction) = {
    doCheck(transaction)
  }

  def check(transaction: TelepayTransaction) = {
    doCheck(transaction)
  }

  private def requestParams(transaction: SmsCommerceTransaction) = {
    List("result specific for SmsCommerceTransaction class")
  }

  private def requestParams(transaction: TelepayTransaction) = {
    List("result specific for TelepayTransaction class")
  }

  private def doCheck(transaction: BaseTransaction) = {
    // some common code...
    println(requestParams(transaction))
    // ...and here as well
  }
}

But that won’t compile as well — and with the same reason: doCheck accepts BaseTransaction, but requestParamss have more strict preconditions.

The only thing that I came up with and that works is the following:

class MerchantProtocol extends IMerchantProtocol
{
  def check(transaction: SmsCommerceTransaction) = {
    doCheck(transaction, requestParams(transaction))
  }

  def check(transaction: TelepayTransaction) = {
    doCheck(transaction, requestParams(transaction))
  }

  private def requestParams(transaction: SmsCommerceTransaction) = {
    List("result specific for SmsCommerceTransaction class")
  }

  private def requestParams(transaction: TelepayTransaction) = {
    List("result specific for TelepayTransaction class")
  }

  private def doCheck(transaction: BaseTransaction, requestParams: List[String]) = {
    // some common code...
    println(requestParams)
    // ...and here as well
  }
}

But I don’t like that all check methods belong to the same class.
How can I split them by classes?

12

There is one piece in OOP toolset, that allows you to enforce this kind of design : constructors.

Implement handling classes, so it accepts the request class inside it’s constructor. This allows you to specify more concrete version of request class, while not breaking any kind of virtual method or class polymorphism. Concrete handler will require concrete request in it’s constructor, while passing it’s generic form to it’s predecessor.

The problem then gets shifted to how to create concrete handler for concrete type of request. You could use “downchast check” or maybe a Visitor pattern, if you want compile-time typechecking. Encapsulating it in Factory class is given. This problem I believe is the core of your concern. If you can ensure correct handler is paired with correct request, then no problems can occur. Using constructors for parameters ensures, that the handler cannot be called incorrectly outside this factory.

1

Theme wordpress giá rẻ Theme wordpress giá rẻ Thiết kế website Kho Theme wordpress Kho Theme WP Theme WP

LEAVE A COMMENT