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.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>+-----------+ +-----------+
| A | | A' |
|-----------+ +-----------+
|method(A') | ^
+-----------+ |
^ +-----------+
| | B' |
+-----------+ +-----------+
| B | ^
|-----------+ |
|method(B') | +-----------+
+-----------+ | C' |
^ +-----------+
|
+-----------+
| C |
|-----------+
|method(C') |
+-----------+
</code>
<code>+-----------+ +-----------+ | A | | A' | |-----------+ +-----------+ |method(A') | ^ +-----------+ | ^ +-----------+ | | B' | +-----------+ +-----------+ | B | ^ |-----------+ | |method(B') | +-----------+ +-----------+ | C' | ^ +-----------+ | +-----------+ | C | |-----------+ |method(C') | +-----------+ </code>
+-----------+           +-----------+
|     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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>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")
}
}
</code>
<code>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") } } </code>
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>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
}
}
</code>
<code>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 } } </code>
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>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
}
}
</code>
<code>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 } } </code>
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

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa

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.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>+-----------+ +-----------+
| A | | A' |
|-----------+ +-----------+
|method(A') | ^
+-----------+ |
^ +-----------+
| | B' |
+-----------+ +-----------+
| B | ^
|-----------+ |
|method(B') | +-----------+
+-----------+ | C' |
^ +-----------+
|
+-----------+
| C |
|-----------+
|method(C') |
+-----------+
</code>
<code>+-----------+ +-----------+ | A | | A' | |-----------+ +-----------+ |method(A') | ^ +-----------+ | ^ +-----------+ | | B' | +-----------+ +-----------+ | B | ^ |-----------+ | |method(B') | +-----------+ +-----------+ | C' | ^ +-----------+ | +-----------+ | C | |-----------+ |method(C') | +-----------+ </code>
+-----------+           +-----------+
|     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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>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")
}
}
</code>
<code>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") } } </code>
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>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
}
}
</code>
<code>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 } } </code>
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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>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
}
}
</code>
<code>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 } } </code>
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

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật