mercredi 8 juin 2022

Mediator design pattern right choice?

i am asking me if this is a good design. I have my own variables which will be persisted on a server with legacy protocol. Is this the way to go in modern c++? Can i improve code with template, CRTP or std::variant?

I tried std::variant but with std::refrence_wrapper<connected_int32/connected_string> in std::vector i was not able to use std::find_if.

#include <algorithm>
#include <cstring>
#include <iostream>
#include <memory>
#include <string>
#include <string_view>
#include <vector>

class mediator_interface
{
public:
    virtual ~mediator_interface() noexcept = default;

    void value_to_server(const uint32_t id, const std::vector<uint8_t> &value) noexcept { value_to_server_impl(id, value); }
    void value_to_parameter(const uint32_t id, const std::vector<uint8_t> &value) noexcept { value_to_parameter_impl(id, value); }

private:
    virtual void value_to_server_impl(const uint32_t id, const std::vector<uint8_t> &value) = 0;
    virtual void value_to_parameter_impl(const uint32_t id, const std::vector<uint8_t> &value) = 0;
};

class parameter
{
public:
    std::string_view topic() const noexcept { return topic_; }
    void set_topic(const std::string_view topic) noexcept { topic_ = topic; }

    uint32_t id() const noexcept { return id_; }
    void set_id(const uint32_t id) noexcept { id_ = id; }

    void set_mediator(mediator_interface *mediator_ptr) noexcept { mediator_ptr_ = mediator_ptr; }

    virtual void set_value_impl(const std::vector<uint8_t> &value) = 0;
    virtual void print_impl() = 0;

protected:
    std::string_view topic_{""};
    uint32_t id_{UINT32_MAX};
    mediator_interface *mediator_ptr_{nullptr};
};

class connected_int32 final : public parameter
{
public:
    connected_int32(const std::string_view topic) noexcept { topic_ = topic; }

    int32_t value() const noexcept { return data_; }
    void set_value_impl(const int32_t value) noexcept {
        data_ = value;
        if (mediator_ptr_) {
            std::vector<uint8_t> tmp(sizeof(data_));
            memcpy(tmp.data(), &data_, sizeof(data_));
            mediator_ptr_->value_to_server(id_, tmp);
        }
    }
    void set_value_impl(const std::vector<uint8_t> &value) noexcept { std::memcpy(&data_, value.data(), sizeof(data_)); }
    void print_impl() { std::cout << "id: " << id_ << " --> value: " << data_ << std::endl; }
    
private:
    int32_t data_ = 2;
};

class connected_string final : public parameter
{
public:
    connected_string(const std::string_view topic) noexcept { topic_ = topic; }

    std::string value() const noexcept { return data_; }
    void set_value_impl(const std::string_view value) noexcept {
        data_ = value;
        if (mediator_ptr_) {
            mediator_ptr_->value_to_server(id_, { data_.begin(), data_.end() });
        }
    }
    void set_value_impl(const std::vector<uint8_t> &value) noexcept { data_ = std::string(value.begin(), value.end()); }
    void print_impl() { std::cout << "id: " << id_ << " --> value: " << data_ << std::endl; }
    
private:
    std::string data_ = "two";
};

class mediator final : public mediator_interface
{
public:
    mediator() = default;
    ~mediator() = default;

    void register_parameter(parameter *param) {
        parameters_.push_back(param);
        param->set_mediator(this);
    }

    bool receive_id(const std::string_view topic, uint32_t id) {
        auto param = std::find_if(
            std::begin(parameters_),
            std::end(parameters_),
            [&](const parameter *const p)
            {
                return p->topic() == topic;
            });

        if (param != std::end(parameters_))
        {
            (*param)->set_id(id);
            return true;
        }

        return false;
    }

    void value_to_parameter_impl(const uint32_t id, const std::vector<uint8_t> &value) noexcept override {
        auto param = std::find_if(
            std::begin(parameters_),
            std::end(parameters_),
            [&](const parameter *const p)
            {
                return p->id() == id;
            });

        if (param != std::end(parameters_))
        {
            (*param)->set_value_impl(value);
        }
    }

    void print_all() {
        for (auto &e : parameters_) { e->print_impl(); }
    }

private:
    std::vector<parameter*> parameters_;

    void value_to_server_impl(const uint32_t id, const std::vector<uint8_t> &value) noexcept override {
        std::cout << "Sending data from id(" << id << ") to server..." << std::endl;
    }
};

int main()
{
    connected_int32 my_int{"TstInt"};
    connected_string my_string{"TstString"};

    mediator medi;

    medi.register_parameter(&my_int);
    medi.register_parameter(&my_string);
    medi.print_all();

    medi.receive_id("TstInt", 123);
    medi.receive_id("TstString", 321);
    medi.print_all();

    //User updates variables, persisting to server...
    my_int.set_value_impl(1);
    my_string.set_value_impl("one");
    medi.print_all();

    //Getting update from server...
    medi.value_to_parameter_impl(123, { 0, 0, 0, 0});
    medi.value_to_parameter_impl(321, { 'z', 'e', 'r', 'o'});
    medi.print_all();
}

Aucun commentaire:

Enregistrer un commentaire