Schema strategico e relazione "È una"

2

Immagina, ho classe IAlgo che è un'interfaccia. Ne ho ricavato e ho implementato il suo unico metodo chiamato matchCount in diverse implementazioni: AlgoA1 , AlgoA2 , AlgoA3 , AlgoB1 , AlgoB2 .

class IAlgo
{
    virtual int matchCount(T1* p1, T2* p2) = 0;
}

class AlgoA1 : public IAlgo
{
    virtual int matchCount(T1* p1, T2* p2) override
    {
         // impl here
    }
}

La differenza tra AlgoAN e AlgoBK è che la categoria A è interessata a tutti i parametri di matchCount e la categoria B è interessata solo al primo param e il resto può essere nullo - categoria B non li usa.

Quindi, come algoritmo incapsulato, li sto usando nel modello di strategia. Dopo qualche tempo, il proprietario del prodotto dice che desidera un nuovo tipo di algoritmi ( AlgoC category) che aggiunge un nuovo parametro al metodo matchCount - ad esempio T3* . Quindi dovremmo tornare indietro e modificare l'intera gerarchia di IAlgo e tutte le sottoclassi avranno il seguente formato:

    virtual int matchCount(T1* p1, T2* p2, T3* p3) = 0;

Questo è un problema di progettazione e sembra che io abbia avuto un algoritmo incapsulato per usarlo come strategia, ma non ha funzionato. Come devo risolvere questo problema? Il mio problema è il fatto che le famiglie AlgoA , AlgoB e AlgoC non sono correlate e non dovrebbero derivare da IAlgo e non hanno una relazione "IS A"? Devo avere interfacce diverse IAlgoA , IAlgoB e IAlgoC per famiglie diverse? O tutto questo va bene e ho bisogno di qualche altra soluzione?

    
posta Narek 20.08.2015 - 09:48
fonte

4 risposte

6

Come indicato nei commenti, non esiste un uso misto di algoritmi delle diverse famiglie.

Se non vi è alcun caso d'uso in cui un IAlgo può riferirsi a un'istanza AlgoAN o a AlgoBK , non c'è motivo di avere un'interfaccia condivisa per le varie famiglie di algoritmi.

La miglior soluzione è introdurre una nuova interfaccia per ogni famiglia di algoritmi:

class IAlgoA
{
    virtual int matchCount(T1* p1, T2* p2) = 0;
};

class IAlgoB
{
    virtual int matchCount(T1* p1) = 0;
};

Quindi si rilascia l'interfaccia IAlgo esistente e si correggono i percorsi in cui è stato utilizzato per fare riferimento alle nuove interfacce specifiche della famiglia.
A questo punto, l'aggiunta di una terza, quarta o quinta famiglia di algoritmi non interferisce con le famiglie esistenti, poiché non devono più conformarsi a un'interfaccia condivisa.

    
risposta data 20.08.2015 - 11:51
fonte
2

Che ne dici di semplificare la chiamata a virtual int matchCount() rimuovendo tutti gli argomenti e istanziando ogni strategia con gli argomenti del metodo matchCount nella tua domanda. Quindi inserisci la strategia concreta nel tuo contesto.

Eccoalcunimodiperutilizzarelestrategie(nonsonosicurodicomefunzionineltuoveroproblemaperchénoncisonodettaglinellatuadomanda):

    
risposta data 21.08.2015 - 17:24
fonte
0

Argomenti opzionali. La dichiarazione dei nuovi argomenti come facoltativi consente di aggiungere parametri e non interrompere il codice esistente.

sovraccarichi. Dividi l'interfaccia della strategia in più metodi.

Messaggi. Crea una gerarchia di oggetti argomento e passali alla strategia anziché ai parametri "allentati". Il codice di chiamata maturo continuerà a inviare i messaggi e la strategia matura continuerà a funzionare senza interruzioni. Aggiungi nuovi tipi di messaggi, se necessario.

    
risposta data 20.08.2015 - 10:27
fonte
0

Ho riscontrato questo problema esatto. Non ero soddisfatto del modo in cui l'ho risolto, quindi non ho risposto subito alla tua domanda. Ma d'altra parte, nessuna delle altre risposte qui.

La mia soluzione:

class IAlgo
{
    virtual int matchCount(AlgoParameters *) = 0;
}

class AlgoA1 : public IAlgo
{
    virtual int matchCount(AlgoParameters * algoParameters) override
    {
        a1 = algoParameters.a1;
        a2 = algoParameters.a2;
    }
}

class AlgoB1 : public IAlgo
{
    virtual int matchCount(AlgoParameters * algoParameters) override
    {
        b1 = algoParameters.b1;
        b2 = algoParameters.b2;
    }
}

class AlgoParameters 
{
public:
    AParam1 a1;
    AParam2 a1;

    BParam1 b1;
    BParam2 b1;

    CParam1 c1;
    CParam2 c1;
}

Come vedi ho semplicemente incapsulato tutti i parametri per tutti gli algoritmi in una struttura dati. No, questa non è una soluzione piacevole . Non ti suggerisco di usarlo . Ma ti permette di fare una chiamata come questa:

IAlgo GetAlgorithm(EAlgorithmType eAlgoType)
{
    switch(eAlgoType){
    case eAlgoA1:
        return AlgoA1();
    case ...
    }
}
AlgoParameters SetAlgorithmParameters(EAlgorithmType eAlgoType)
{
    AlgoParameters algoParam;
    switch(eAlgoType){
    case eAlgoA1:
        algoParam.a1 = ...;
        algoParam.a2 = ...;
    case ...
    }
}
int UseAlgorithm(IAlgo algoritm, AlgoParameters parameters)
{
    return algorithm.matchCount(&parameters);
}

Violare molti principi nello sviluppo del software (ad esempio Open / Close-Principle). Ma funziona ed è facilmente espandibile per nuovi algoritmi.

    
risposta data 20.08.2015 - 13:10
fonte

Leggi altre domande sui tag