2016-11-11 6 views
0

Ich entwickle einige komplexe Berechnungen, die die 'Schritte' erfordern (Strategie Muster Typ Implementierung) und ich bin mir nicht sicher, der beste Weg, die Schritt Unterberechnungsklassen in den Hauptteil injizieren Klasse.C++ injizieren 'Strategien' durch Konstruktor

Ich habe in richtlinienbasierten Design untersucht, aber lesen Sie das Policy Design ist für 'Kompilierzeit Polymorphismus' nicht Laufzeit. Außerdem war ich mir nicht sicher, wie man Vorlagen verwendet, da einige der Unterberechnungsklassen Konstruktorparameter benötigen.

Ich habe angefangen, virtuelle 'Interface'-Klassen für jeden Schritt zu implementieren und jeden Schritt als unique_ptr in den Konstruktor einzufügen, war mir aber nicht sicher, ob dies der richtige' moderne C++ 'Weg ist.

Ich fing anfänglich an, alle Funktionalität in der Hauptklasse zu implementieren, fand aber, dass es schwierig, wenn nicht unmöglich machte, jeden Schritt einzeln zu testen.

Die Struktur ist ähnlich wie unter:

class CalculationStepA 
{ 
public: 
    // default constructor 

    StepAResult performStep(const input& requiredInput); 
}; 

class CalculationStepBType1 
{ 
public: 
    // default constructor 

    StepBResult performStepB(const stepBInput& requiredInput); 
}; 

class CalculationStepBType2 
{ 
public: 
    CalculationStepBType2(const inputIOnlyNeedForType2& parameters) 
    { 
    // initialize class members from input 
    // need for this calculation type 
    } 

    StepBResult performStepB(const stepBInput& requiredInput); 
}; 

class CalculationStepCType1 
{ 
public: 
    CalculationStepBType2(const inputIOnlyNeedForType1& parameters) 
    { 
    // initialize class members from input 
    // need for this calculation type 
    } 

    StepCResult performStepC(const stepCInput& requiredInput); 
}; 

class CalculationStepCType2 
{ 
public: 
    CalculationStepBType2(const inputIOnlyNeedForType2& parameters) 
    { 
    // initialize class members from input 
    // need for this calculation type 
    } 

    StepCResult performStepB(const stepCInput& requiredInput); 
}; 

class ClassThatUsesAllTheCalculations 
{ 
public: 
    ClassThatUsesAllTheCalculations(/* take required parameters that determine which step types I need */) 
    {} 

    // possible constructor? 
    ClassThatUsesAllTheCalculations(
     std::unique_ptr<IStepACalculationStrategy> stepA, 
     std::unique_ptr<IStepBCalculationStrategy> stepB,  
     std::unique_ptr<IStepCCalculationStrategy> stepC) 
    { 

    } 

    FinalResult executeCalculation(const finalInputRequiredHere& input) 
    { 
    auto stepAresult = stepACalculator(somethingFromInput); 
    // logic to use stepA and determine if we should continue 

    auto stepBresult = stepBCalculator(somethingFromStepAResult); 
    // again, logic to use stepB and determine if we should continue 

    auto stepCresult = stepCCalculator(somethingFromStepBResult); 
    // assemble final result 

    return theFinalResult 
    } 


    // other method needed to setup calculation 


private: 
    TypeForStepACalculation stepACalculator; 
    TypeForStepBCalculation stepBCalculator; 
    TypeForStepCCalculation stepCCalculator; 
}; 

Jede Hilfe auf das beste Design zu bestimmen wäre toll, sehr geschätzt.

+0

Mein Gedanke ist, dass der Versuch, die Top-Level-Klasse zu erstellen, die eine hartcodierte Reihe von Aktionen in eine Klasse komponiert ist zu restriktiv. Ich habe das Gefühl, dass ich versuche, eine Art flüssiges Interface zu verwenden, wo die Funktionen ihre Aktionen ausführen und das Ergebnis zurückgeben, so dass die nächste möglicherweise eine bessere Option ist. Ich habe nicht wirklich genug darüber nachgedacht, um ein konkretes Beispiel zu formulieren, das ist auch schwierig, da dein Beispiel so abstrakt ist. Sie können ein Beispiel aus C# Linq oder Java-Streams nehmen. –

+0

@PaulRooney Interessanter Vorschlag. Ich bin mit Linq vertraut, da ich hauptsächlich C# bei der Arbeit verwende. Ich werde versuchen, etwas in diese Richtung zu bringen. – RobertW

Antwort

0

Was ist mit einer einfachen Vererbung?

struct StepA{ 
    virtual StepAResult perform(StepAParams)=0; 
}; 

struct someStepAImpl : public StepA{ 
    virtual StepAResult perform(StepAParams params) override { 
     //actual implementation 
    } 
}; 

Ob Ihre Berechnung Klasse verwendet dann eine Referenz (hat auf dem Bau festgelegt werden), eine std::reference_wrapper (nicht null, aber kann später geändert werden) oder irgendeine Art von (smart) Zeigern (vielleicht nullptr, don sein Vergessen Sie nicht, das zu überprüfen, aber am einfachsten, wenn es darum geht, die Lebensdauer zu verwalten. Das hängt davon ab, wie Sie es verwenden möchten und wie Sie die Lebensdauer der Objekte verwalten möchten. Ein Beispiel unter Verwendung von unique_ptr wie man es tut sein:

class Calculator 
{ 
public: 
    Calculator(
     std::unique_ptr<StepA> stepA, 
     std::unique_ptr<StepB> stepB,  
     std::unique_ptr<StepC> stepC 
    ) 
    :m_stepA(std::move(stepA)),m_stepB(std::move(stepB)),m_stepC(std::move(stepC)) 
    {} 

    FinalResult executeCalculation(const finalInputRequiredHere& input) 
    { 
     //logic 
     auto stepAresult = stepA->perform(StepAParams); 
     //logic 
     auto stepBresult = stepB->perform(StepBParams); 
     //logic 
     auto stepCresult = stepC->perform(StepAParams); 
     //logic 
     return FinalResult(); 
    } 

private: 
    std::unique_ptr<StepA> m_stepA=nullptr; 
    std::unique_ptr<StepB> m_stepB=nullptr; 
    std::unique_ptr<StepC> m_stepC=nullptr; 
}; 


void somewhereElse(){ 
    std::unique_ptr<StepA> stepa(new someStepAImpl()); 
    std::unique_ptr<StepB> stepa(new someStepBImpl()); 
    std::unique_ptr<StepC> stepa(new someStepCImpl()); 
    Calculator calc(
     std::move(stepa), 
     std::move(stepb), 
     std::move(stepc) 
    ); 
    calc.executeCalculation(...); 
} 
+0

Das hat perfekt funktioniert. Das war die Lösung, auf die ich mich ursprünglich stützte. Ich habe eine Fabrik gebaut, um die erforderlichen Schrittberechnungen zu erstellen und zu injizieren. – RobertW

Verwandte Themen