Russian Qt Forum
Декабрь 14, 2017, 18:13 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 [2] 3   Вниз
  Печать  
Автор Тема: Нетипичное наследование  (Прочитано 1639 раз)
ecspertiza
Супер
******
Offline Offline

Сообщений: 1005


С уважением, мастер конфетного цеха!


Просмотр профиля
« Ответ #15 : Октябрь 11, 2017, 19:13 »

Вот такие две штуки получились
Код:
#include <iostream>
#include <vector>

class BaseCommand {

    public:
        enum CommandType {
            Unknown,
            TypeA,
            TypeB
        };

        BaseCommand() = default;

        virtual void execute() = 0;
        virtual BaseCommand::CommandType commandType() = 0;
};

class CommandA : public BaseCommand {

    public:
        CommandA() = default;

        void execute() override {
            std::cout << "execure CommandA" << std::endl;
        }

        BaseCommand::CommandType commandType() override {
            return BaseCommand::TypeA;
        }
};

class CommandB : public BaseCommand {

    public:
        CommandB() = default;

        void execute() override {
            std::cout << "execure CommandB" << std::endl;
        }

        BaseCommand::CommandType commandType() override {
            return BaseCommand::TypeB;
        }
};

template <class>
struct CommandProperty;

template<>
struct CommandProperty<CommandA>
{
    static const bool is_single = false;
    static const BaseCommand::CommandType m_type = BaseCommand::TypeA;
};


template<>
struct CommandProperty<CommandB>
{
    static const bool is_single = true;
    static const BaseCommand::CommandType m_type = BaseCommand::TypeB;
};

std::vector<BaseCommand *> m_arr;

template<class Command>
bool checkCommand()
{
    bool is_single = CommandProperty<Command>::is_single;

    if (is_single) {
        for (std::vector<BaseCommand *>::iterator it = m_arr.begin() ; it != m_arr.end(); ++it)
            if ((*it)->commandType() == CommandProperty<Command>::m_type) {
                return false;
            }
    }

    return true;
}

int main(int argc, char *argv[])
{
    if (checkCommand<CommandA>())
            m_arr.push_back(new CommandA);

    if (checkCommand<CommandA>())
            m_arr.push_back(new CommandA);

    if (checkCommand<CommandB>())
            m_arr.push_back(new CommandB);

    if (checkCommand<CommandB>())
            m_arr.push_back(new CommandB);

    for (std::vector<BaseCommand *>::iterator it = m_arr.begin() ; it != m_arr.end(); ++it)
        (*it)->execute();

    return 0;
}

Код:
#include <iostream>
#include <vector>

enum CommandType {
    Unknown = 0,
    TypeA = 1,
    TypeB = 2
};

class ICommand {
public:
    virtual void execute() = 0;
    virtual CommandType commandType() = 0;
};

template<bool isSingle,
         CommandType type>
class BaseCommand : public ICommand {

    public:

        BaseCommand() = default;

        static bool is_single() {return isSingle;}
        static CommandType CommandTypeB(){return type;}
};

class CommandA : public BaseCommand<false, CommandType::TypeA> {

    public:
        CommandA() = default;

        void execute() override {
            std::cout << "execure CommandA" << std::endl;
        }

        CommandType commandType() override {
            return CommandTypeB();
        }
};

class CommandB : public BaseCommand<true, CommandType::TypeB> {

    public:
        CommandB() = default;

        void execute() override {
            std::cout << "execure CommandB" << std::endl;
        }

        CommandType commandType() override {
            return CommandTypeB();
        }
};

std::vector<ICommand *> m_arr;

template<class Command>
bool checkCommand()
{
    bool is_single = Command::is_single();

    if (is_single) {
        for (std::vector<ICommand *>::iterator it = m_arr.begin() ; it != m_arr.end(); ++it)
            if (((ICommand*)(*it))->commandType() == Command::CommandTypeB()) {
                return false;
            }
    }

    return true;
}

int main(int argc, char *argv[])
{
    if (checkCommand<CommandA>())
            m_arr.push_back(new CommandA);

    if (checkCommand<CommandA>())
            m_arr.push_back(new CommandA);

    if (checkCommand<CommandB>())
            m_arr.push_back(new CommandB);

    if (checkCommand<CommandB>())
            m_arr.push_back(new CommandB);

    for (std::vector<ICommand *>::iterator it = m_arr.begin() ; it != m_arr.end(); ++it)
        (*it)->execute();

    return 0;
}

Записан

ViTech
Крякер
****
Online Online

Сообщений: 370



Просмотр профиля
« Ответ #16 : Октябрь 11, 2017, 20:43 »

Оффтоп. Вместо такой вереницы:
Код
C++ (Qt)
   for (std::vector<ICommand *>::iterator it = m_arr.begin() ; it != m_arr.end(); ++it)
       (*it)->execute();
можно так:
Код
C++ (Qt)
   for (ICommand * command : m_arr)
       command->execute();
Записан

Пока сам не сделаешь...
sergek
Программист
*****
Offline Offline

Сообщений: 508


Мы должны приносить пользу людям!


Просмотр профиля
« Ответ #17 : Октябрь 11, 2017, 22:38 »

тогда уж так:
Код:
    for (auto command : m_arr)
            command->execute();
Подмигивающий
Записан

Qt 5.9.1 Qt Creator 4.4.1
Win7, Win10, Ubuntu 14.04
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 9854


Просмотр профиля
« Ответ #18 : Октябрь 12, 2017, 08:15 »

В текущем коде не нравится то, что команды создаются до того как произошла проверка на запуск. Вопрос как в данном случае можно выполнить эту проверку до того как создан экземпляр команды?
Чтобы выполнить проверку нужны те или иные данные для каждого типа команды (класса). Поэтому создавать какой-то класс все равно придется, и развивать его тоже (на виртуалах или темплейтах). Такой класс мне кажется искусственным, лучше обойтись одним классом "команда", т.е. по сути оставить как было. А проверку можно сделать в конструкторе, если не проходит - взвести флажок и быстренько выйти.
Код
C++ (Qt)
CommandA * ca = new CommandA;
if (ca->IsValid()) ...
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3846



Просмотр профиля
« Ответ #19 : Октябрь 12, 2017, 08:20 »

А проверку можно сделать в конструкторе
Как в конструкторе команды сделать проверку, что там есть в очереди команда? Передавать в конструктор команды очередь команд? Улыбающийся

взвести флажок и быстренько выйти.
Код
C++ (Qt)
CommandA * ca = new CommandA;
if (ca->IsValid()) ...
Удобно, чО. Улыбающийся
Записан
ecspertiza
Супер
******
Offline Offline

Сообщений: 1005


С уважением, мастер конфетного цеха!


Просмотр профиля
« Ответ #20 : Октябрь 12, 2017, 10:04 »

Передавать в конструктор команды очередь команд? Улыбающийся

И да будет спагетти :-)
Записан

Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 9854


Просмотр профиля
« Ответ #21 : Октябрь 12, 2017, 11:24 »

И да будет спагетти :-)
Какое спагетти? Откуда  Непонимающий 
Код
C++ (Qt)
   CommandA *ca = new CommandA;
   if (checkCommand(ca)) {
       ca->execute();
   }else {
       delete ca;
}
Вот я смотрю на этот кусочек и в упор не вижу - ну что же тут такого плохого? Да, создали экземпляр, он выполнил проверку - совершенно логично и ессно, он может и должен проверять, у него для этого все есть. Ну может else delete выглядит чуть неуклюже - так это легко уладить хотя бы однострочным inline. Но вместо этого мы лепим еще какую-то хренотень которая что-то решает за класс команды. Хотя тот же is_single прямо-таки просится быть членом класса команда.

И я даже знаю почему так делается Улыбающийся  Хочется блеснуть владением темплейтами - и все, объективных оснований никаких
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3846



Просмотр профиля
« Ответ #22 : Октябрь 12, 2017, 11:35 »

Вот я смотрю на этот кусочек и в упор не вижу - ну что же тут такого плохого?
Вот это и плохо. Не всегда конструктор ничего не делает.
Для чего конструировать объект, который не нужен и потом его разрушать, если достаточно статических данных?
Записан
ecspertiza
Супер
******
Offline Offline

Сообщений: 1005


С уважением, мастер конфетного цеха!


Просмотр профиля
« Ответ #23 : Октябрь 12, 2017, 11:59 »

Old, согласен, об этом и был основной вопрос топика.
Igors, пример который есть выше, он просто описывает проблему. В реале же, это цела структура. Создания, проверки, регистрирования команд, на выполнение. У нас есть менеджер который проверят возможность исполнения команды, при необходимости регистрирует ее и запускает на исполнение. В вашем примере, нам придется в каждую команду передать массив действий, например для того что бы проверить команды на дублирование. Получается, что каждая отдельная операция будет знать о соседних. Зачем ей это? Это уже нарушение архитектуры. Команда должна знать только о своих свойствах и действиях которые ей необходимо выполнить. Остальное должен разруливать менеджер (в примере выше это main и checkExecutable).

Шаблоны же в данном случае помогают нам решить несколько проблем
1. Конфигурирование свойств команды на этапе ее описания. Мы не ломаем ООП. Как в случае, со статичными методами для каждого дочернего класса.
2. Проверка этих свойств без создания самой команды.
« Последнее редактирование: Октябрь 12, 2017, 12:01 от ecspertiza » Записан

ViTech
Крякер
****
Online Online

Сообщений: 370



Просмотр профиля
« Ответ #24 : Октябрь 12, 2017, 12:56 »

тогда уж так:
Код:
    for (auto command : m_arr)
            command->execute();
Подмигивающий

Хорошо, конечно, всё на компилятор скинуть, но это может привести к тому, что разработчики думать совсем перестанут. Тогда уж так:
Код:
    for (auto & command : m_arr)
            command->execute();
Подмигивающий

Но, на мой взгляд, читабельность страдает. Всё хорошо в меру Улыбающийся.
Записан

Пока сам не сделаешь...
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2463


Просмотр профиля
« Ответ #25 : Октябрь 12, 2017, 17:05 »

auto&& тогда уж и не думать)
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 9854


Просмотр профиля
« Ответ #26 : Октябрь 12, 2017, 17:32 »

Шаблоны же в данном случае помогают нам решить несколько проблем..
Простой пример с is_single можно делать как угодно, напр мапой. Вопрос в дальнейшей расширяемости, гибкости и удобстве. И тут, полагаю, Вас привлекло это
Код
C++ (Qt)
template<class Command>
bool checkCommand()
{
...
}
 
Мол, если возникнет какая-то специфика для др команды - мы специализируем для нее шаблон, и все дела! (поправьте если не так понял). Ну то смотря как (или куда) проверки разрастутся. У checkCommand должны быть данные команды чтобы он мог что-то решать. А как их получить если экземпляра нет? На одних статиках далеко не уехать.

В вашем примере, нам придется в каждую команду передать массив действий, например для того что бы проверить команды на дублирование. Получается, что каждая отдельная операция будет знать о соседних. Зачем ей это?
С этим согласен. Но можно без затей создать экземпляр и подать его менеджеру - пусть он решает. Не вижу зачем (упорно) избегать создания экземпляра - это уже обошлось недешево, код заметно усложнился.
Записан
m_ax
Neo
******
Offline Offline

Сообщений: 1752



Просмотр профиля
« Ответ #27 : Октябрь 12, 2017, 20:47 »

Цитировать
auto&& тогда уж и не думать)
А чем это лучше auto & ?
На сколько я понимаю, (возможно я не прав) это вызовет копирующий конструктор..

Цитировать
Мол, если возникнет какая-то специфика для др команды - мы специализируем для нее шаблон, и все дела!
Да, именно так. Это тот самый случай, когда необходимо разруливать (развлетвлять) поведение системы в зависимости только от пренадлежности её к определённому типу, минуя необходимость создавать объект класса.. Это классический паттерн класс харрактеристик (type traits). И да, он работает (и как это видно из контекста) в тех случаях, когда  "checkCommand не обязан иметь внутренние данные команды чтобы он мог что-то решать."  Улыбающийся Короче, всё решается в зависимости только от типа.
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3846



Просмотр профиля
« Ответ #28 : Октябрь 12, 2017, 20:54 »

Короче, всё решается в зависимости только от типа.
Скукатень. А где здесь развесистые switch'и? Улыбающийся
Записан
sergek
Программист
*****
Offline Offline

Сообщений: 508


Мы должны приносить пользу людям!


Просмотр профиля
« Ответ #29 : Октябрь 12, 2017, 20:55 »

А чем это лучше auto & ?
При чем тут лучше/хуже? Если нет цели изменить содержимое m_arr, зачем нужно создавать ссылку на элемент контейнера? Так что в данном случае просто auto будет достаточно.

ps. прошу прощения за офтоп.
Записан

Qt 5.9.1 Qt Creator 4.4.1
Win7, Win10, Ubuntu 14.04
Страниц: 1 [2] 3   Вверх
  Печать  
 
Перейти в:  

Страница сгенерирована за 0.157 секунд. Запросов: 23.