Mi piacerebbe scoprire come gestite la seguente situazione: avete una gerarchia di classi, chiamatela H1, con un metodo polimorfico che dovrebbe accettare un argomento che digita la gerarchia di forme H2 nel seguente modo: più alta è la classe in H1, l'argomento H2 più alto accetta.
+-----------+ +-----------+
| A | | A' |
|-----------+ +-----------+
|method(A') | ^
+-----------+ |
^ +-----------+
| | B' |
+-----------+ +-----------+
| B | ^
|-----------+ |
|method(B') | +-----------+
+-----------+ | C' |
^ +-----------+
|
+-----------+
| C |
|-----------+
|method(C') |
+-----------+
Prima di scrivere qualsiasi codice, dico che ho due classi di protocollo con metodi check
che hanno molto in comune, ma i parametri di richiesta sono specifici per argomenti concreti - transazioni.
Il linguaggio è scala, ma il problema è indipendente dalla lingua.
A prima vista dovrebbe assomigliare a questo:
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")
}
}
Ma ovviamente non viene compilato poiché il principio di sostituzione di Liskov viene violato.
Proviamo questo:
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
}
}
Ma questo non verrà compilato ugualmente - e con lo stesso motivo: doCheck
accetta BaseTransaction
, ma requestParams
s ha precondizioni più rigide.
L'unica cosa che mi è venuta in mente e che funziona è la seguente:
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
}
}
Ma non mi piace che tutti i metodi check
appartengano alla stessa classe.
Come posso dividerli per classi?