OOD: gerarchia di classi con argomenti di metodo che formano un'altra gerarchia

1

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?

    
posta Zapadlo 24.07.2014 - 20:58
fonte

1 risposta

1

C'è un pezzo in set di strumenti OOP, che ti permette di imporre questo tipo di design: costruttori.

Implementa le classi di gestione, quindi accetta la classe di richiesta all'interno del costruttore. Ciò consente di specificare una versione più concreta della classe di richiesta, senza interrompere alcun tipo di metodo virtuale o polimorfismo di classe. Il gestore concreto richiederà una richiesta concreta nel suo costruttore, mentre passerà il suo modulo generico al suo predecessore.

Il problema viene quindi spostato su come creare il gestore concreto per il tipo concreto di richiesta. Potresti usare "downchast check" o forse un pattern Visitor, se vuoi il controllo della compilazione in tempo reale. L'incapsulamento in classe Factory è dato. Questo problema credo sia il nocciolo della vostra preoccupazione. Se è possibile assicurarsi che il gestore corretto sia associato alla richiesta corretta, non si possono verificare problemi. L'utilizzo dei costruttori per i parametri garantisce che il gestore non possa essere chiamato in modo errato al di fuori di questo stabilimento.

    
risposta data 28.07.2014 - 10:38
fonte

Leggi altre domande sui tag