2013-05-29 31 views
8

sto cercando di codice in C++ (C++ 11) un esempio molto semplice secondo il concetto Clean Architettura descritto da Zio Bob Martin here (foto sotto):non così pulita Architettura

enter image description here

L'idea è di leggere del testo da un Controller e stamparlo da un Presenter. Ho fatto qualcosa ma non sembra che stia seguendo il flusso pulito e lo DIP del post del blog.

Tra le altre cose, penso che il flusso sia sbagliato poiché, ad esempio, IUseCaseInputPort deve conoscere l'IUseCaseOutputPort (la funzione di lettura ha l'IUseCaseOutputPort come parametro di input, creando così un'altra dipendenza ...).

Sarei davvero grato se qualcuno potesse darmi qualche consiglio sul modo migliore per implementarlo. Molte grazie in anticipo.

#include <iostream> 
#include <string> 
#include <memory> 

class IUseCaseOutputPort { 
public: 
    virtual void print(std::string message) = 0; 
    virtual ~IUseCaseOutputPort() {}; 
}; 

// 2 Presenters 
class HtmlPresenter: public IUseCaseOutputPort { 
public: 
    void print(std::string message) { 
     std::cout << "<p>" << message << "</p>" << std::endl; 
    } 
}; 

class TextPresenter: public IUseCaseOutputPort { 
public: 
    void print(std::string message) { 
     std::cout << message << std::endl; 
    } 
}; 

// 
class IUseCaseInputPort { 
public: 
    virtual void read(std::shared_ptr<IUseCaseOutputPort> output) = 0; 
    virtual ~IUseCaseInputPort(){}; 
}; 

// specific UseCaseInteractor 
class UseCaseInteractorForInputFromStdIn: public IUseCaseInputPort { 
public: 
    void read(std::shared_ptr<IUseCaseOutputPort> output) { 
     std::string message; 
     std::cout << "Please input some text!"; 
     std::getline(std::cin, message); 
     output->print(message); 
    } 
}; 

// Controller 
class ControllerToDisplayHtml { 
public: 
    void displayInHtmlSomethingFromStdIn() { 
     input = std::make_shared<UseCaseInteractorForInputFromStdIn>(); 
     std::shared_ptr<HtmlPresenter> output = 
       std::make_shared<HtmlPresenter>(); 
     input->read(output); 
    } 
private: 
    std::shared_ptr<IUseCaseInputPort> input; 
}; 

int main() { 
    ControllerToDisplayHtml c; 
    c.displayInHtmlSomethingFromStdIn(); 
    return 0; 
} 

Per chi fosse interessato, un complemento alla mia domanda, come suggerito dal BЈовић. Esempio molto semplice Solo per mostrare il flusso di questo modello.

#include <iostream> 
#include <string> 
#include <memory> 
#include <fstream> 

class IUseCaseOutputPort { 
public: 
    virtual void print(std::string message) = 0; 
    virtual ~IUseCaseOutputPort() {}; 
}; 

// 2 Presenters 
class HtmlPresenter: public IUseCaseOutputPort { 
public: 
    void print(std::string message) { 
     std::cout << "<p>" << message << "</p>" << std::endl; 
    } 
}; 

class TextPresenter: public IUseCaseOutputPort { 
public: 
    void print(std::string message) { 
     std::cout << message << std::endl; 
    } 
}; 

// 
class IUseCaseInputPort { 
public: 
    virtual std::string read() = 0; 
    virtual ~IUseCaseInputPort(){}; 
}; 

// specific UseCaseInteractor for reading text from the stdin 
class UseCaseInteractorForInputFromStdIn: public IUseCaseInputPort { 
public: 
    std::string read() { 
     std::string message; 
     std::cout << "Please input some text!" << std::endl; 
     std::getline(std::cin, message); 
     return message; 
    } 
}; 

// specific UseCaseInteractor for reading text from a dummy file 
class UseCaseInteractorForInputFromDummyFile: public IUseCaseInputPort { 
public: 
    std::string read() { 
     const std::string filename = "/proc/meminfo"; 
     std::string message = readFile(filename); 
     return message; 
    } 
private: 
    std::string readFile(const std::string filename) { 
     std::string line; 
     std::string lines; 
     std::ifstream myfile(filename); 
     if (myfile.is_open()) { 
      while (myfile.good()) { 
       getline(myfile, line); 
       lines += line + '\n'; 
      } 
      myfile.close(); 
     } else { 
      lines = "Unable to open file"; 
     } 
     return lines; 
    } 
}; 

// Controller 
class ControllerForReading { 
public: 
    std::string readFromStdIn() { 
     input = std::make_shared<UseCaseInteractorForInputFromStdIn>(); 
     std::string out = "This text was read from the stdin:\n"; 
     out += input->read(); 
     return out; 
    } 
    std::string readFromFile() { 
     input = std::make_shared<UseCaseInteractorForInputFromDummyFile>(); 
     std::string out = "This text was read from the a file:\n"; 
     out += input->read(); 
     return out; 
    } 
private: 
    std::shared_ptr<IUseCaseInputPort> input; 
}; 

// main represents the outer shell 
int main() { 
    std::cout << "Main started!" << std::endl; 

    ControllerForReading c; 
    const std::string textFromStdin = c.readFromStdIn(); 
    const std::string textFromFile = c.readFromFile(); 

    auto output = std::make_shared<HtmlPresenter>(); 
    output->print(textFromStdin); 
    output->print(textFromFile); 

    auto output2 = std::make_shared<TextPresenter>(); 
    output2->print(textFromStdin); 
    output2->print(textFromFile); 

    std::cout << "Main ended!" << std::endl; 
    return 0; 
} 
+3

Se la tua domanda riguarda davvero lo stile di codice (piuttosto che un bug), dovresti probabilmente chiedere a http://codereview.stackexchange.com. –

+0

Grazie. Non sembra né bug né codice in stile ... Voglio solo evitare la dipendenza extra che ho introdotto. Se non ricevo alcuna risposta, la pubblicheremo dove hai suggerito. – RicLeal

risposta

4

Tra le altre cose, penso che il flusso è sbagliato, come, ad esempio, l'IUseCaseInputPort ha bisogno di conoscere l'IUseCaseOutputPort (la funzione di lettura ha l'IUseCaseOutputPort come parametro di input, creando così un'altra dipendenza ...).

Sì, questo è davvero sbagliato. Un metodo per ottenere dati non dovrebbe sapere cosa si sta facendo con esso.

Una correzione è abbastanza semplice. Cambiare IUseCaseInputPort::read metodo per restituire il risultato, invece di chiamare il metodo di IUseCaseOutputPort:

// 
class IUseCaseInputPort { 
public: 
    virtual std::string read() = 0; 
    virtual ~IUseCaseInputPort(){}; 
}; 

// specific UseCaseInteractor 
class UseCaseInteractorForInputFromStdIn: public IUseCaseInputPort { 
public: 
    std::string read() { 
     std::string message; 
     std::cout << "Please input some text!"; 
     std::getline(std::cin, message); 
     return message; 
    } 
}; 

// Controller 
class ControllerToDisplayHtml { 
public: 
    void displayInHtmlSomethingFromStdIn() { 
     input = std::make_shared<UseCaseInteractorForInputFromStdIn>(); 
     std::shared_ptr<HtmlPresenter> output = 
       std::make_shared<HtmlPresenter>(); 

     const std::string messageToOutput(input->read()); 
     output->print(messageToOutput); 
    } 
private: 
    std::shared_ptr<IUseCaseInputPort> input; 
}; 

più una cosa. Non è necessario creare oggetti di input e output nel metodo displayInHtmlSomethingFromStdIn(). Invece dovresti usare una specie di iniezione di dipendenza. Ciò significa che si creano questi oggetti all'esterno e li si passa attraverso il puntatore o il riferimento all'oggetto ControllerToDisplayHtml.

+0

Grazie! Adesso è molto meglio! – RicLeal

+0

Grazie per il suggerimento. Questo era solo un test. Pubblicherò un esempio corretto sotto la mia domanda. – RicLeal

Problemi correlati