Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Not so Clean Architecture

Tags:

c++

oop

c++11

I'm trying to code in C++ (C++11) a very simple example according to the Clean Architecture concept described by Uncle Bob Martin here (picture below):

enter image description here

The idea is to read some text by a Controller and print it by a Presenter. I have done something but it doesn't look like it's following the clean flow and the DIP of the blog post.

Among other things, I think the flow is wrong as, for example, the IUseCaseInputPort needs to know about the IUseCaseOutputPort (the read function has the IUseCaseOutputPort as input parameter, creating thus another dependency...).

I would really appreciate if someone could give me some tips about the best way to implement this. Many thanks in advance.

#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;
}

For those interested, a complement to my question as suggested by BЈовић. Very simple example. Just to show the flow of this model.

#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;
}
like image 797
RicLeal Avatar asked May 29 '13 10:05

RicLeal


People also ask

What is meant by clean architecture?

Clean architecture is a software design philosophy that separates the elements of a design into ring levels. An important goal of clean architecture is to provide developers with a way to organize code in such a way that it encapsulates the business logic but keeps it separate from the delivery mechanism.

What is clean architecture example?

Clean architecture is a staple of the modern app development space. Particularly popular for Java and Android developers, this architecture is designed to make it easier to create stable apps even when outer elements such as UI, databases, or external APIs are always changing.

What is the difference between onion architecture and clean architecture?

Clean ArchitectureIt builds on the concepts of Onion Architecture but with somewhat different details of the layers. Instead of “Domain Model”, it refers to the core as “Entities”, but still representing enterprise-wide business rules.

What are the principles of clean architecture?

5 steps to clean architecturePlace abstractions in the inner layers. Put implementation in the outer layers. Point dependencies inwards. Follow test-driven development outside-in.


1 Answers

Among other things, I think the flow is wrong as, for example, the IUseCaseInputPort needs to know about the IUseCaseOutputPort (the read function has the IUseCaseOutputPort as input parameter, creating thus another dependency...).

Yes, this is indeed wrong. A method to get data should not know what is being done with it.

A fix is quite simple. Change IUseCaseInputPort::read method to return the result, instead of calling IUseCaseOutputPort's method :

//
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;
};

One more thing. You should not create input and output objects in the displayInHtmlSomethingFromStdIn() method. Instead you should use some kind of dependency injection. That means, you create these objects outside, and pass them through pointer or reference to the ControllerToDisplayHtml object.

like image 61
BЈовић Avatar answered Sep 21 '22 05:09

BЈовић