Модераторы: Се ля ви
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> ищу паттерн проектирования 
V
    Опции темы
CompWorm
  Дата 9.12.2013, 23:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Допеределыватель
***


Профиль
Группа: Участник Клуба
Сообщений: 1689
Регистрация: 6.12.2004
Где: /

Репутация: нет
Всего: 31



Привет! 
пытаюсь подобрать паттерн для решения такой задачи:

к приложению подключаются сервисы в виде dll, а так же сценарии, тоже в виде dll.
и те и другие реализованы в виде фабрики, т.е. отдают приложению объект, соответствующий интерфейсам IService, IScenario.
объект IScenario должен уметь выполнять команды IService, но сценарий будет создаваться пользователями под некоторые сервисы, то есть сценарий будет полностью совместим лишь с некоторыми сервисами, для которых создавался. 
у сервисов могут быть как общеиспользуемые команды, так и специфические.
нужно учитывать, что в новых версиях сервиса могут добавляться новые команды, и сценарий должен быть готов к этому.

нужен паттерн для общения сервиса со сценарием.

первая идея - создать в IScenario метод
Object execute (String command, Object[] parameters)
и
list[] getSupportedCommands()
не знаю что это за паттерн такой ))) тут проблема очевидна - код никак не подсказывает ни сколько параметров нужно передать для выполнения команды, ни их тип, ни наличие возвращаемого объекта, ни его тип. кроме того, списки команд для разработчика сценария тоже скрыты.

контрактный паттерн тоже не очень понятно как тут применить: количество команд-методов класса заранее неизвестно...

командный паттерн смотрится не лучше - сценарий может вернуть свои исполняемые объекты, но какой в этом смысл, если у многих сценариев есть одни и те же команды...

идеи ?  smile 


Это сообщение отредактировал(а) CompWorm - 9.12.2013, 23:15


--------------------
PM MAIL   Вверх
ksnk
Дата 9.12.2013, 23:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


прохожий
****


Профиль
Группа: Комодератор
Сообщений: 6855
Регистрация: 13.4.2007
Где: СПб

Репутация: нет
Всего: 386



Ну а чистосценарные решения не подходят? Поставить туда скриптовый язык достаточной продвинутости. lua или скажем javascript. Для любителей этого дела и php можно покрутить-пооблизываться.

Ядрышко на скриптах, для объединения в едину кучу. Новый сервис - новый пакет скриптов с диктуемым ядром интерфейсом.




--------------------
Человеку свойственно ошибаться, программисту свойственно ошибаться профессионально ! user posted image
PM MAIL WWW Skype   Вверх
CompWorm
Дата 10.12.2013, 01:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Допеределыватель
***


Профиль
Группа: Участник Клуба
Сообщений: 1689
Регистрация: 6.12.2004
Где: /

Репутация: нет
Всего: 31



Цитата(ksnk @  9.12.2013,  23:50 Найти цитируемый пост)
Ну а чистосценарные решения не подходят?

это не совсем "игровой" сценарий. сервисом будет общий набор инструментов для работы с сетевыми протоколами, а сценарий - обработчики пакетов. короче я упростил чтоб в детали не вдаваться.
Цитата(ksnk @  9.12.2013,  23:50 Найти цитируемый пост)
Поставить туда скриптовый язык достаточной продвинутости. lua или скажем javascript. Для любителей этого дела и php можно покрутить-пооблизываться.

пишу ядро на QT, пробовал LUA для сценариев, так как у них есть биндинг и можно объекты красиво передавать, но LUA под виндой биндится только если QT на M$ компиляторе (религия не позволяет им пользоваться ). поэтому забил и сделал через dll. 
JavaScript использовать тоже не позволяет религия, PHP не уверен как совместить с QT без лишних зависимостей.
Цитата(ksnk @  9.12.2013,  23:50 Найти цитируемый пост)
Новый сервис - новый пакет скриптов с диктуемым ядром интерфейсом.

основная идея - поставлять ядро с базовым набором сервисов (инструментов) а пользователи чтобы писали свои сценарий, которые можно было бы юзать с этими инструментами по мере совместимости. сервисы тоже могут писать пользователи, но это будет происходить в разы реже.
тут отношение много ко многим : в сервисе (например текстовый редактор) можно проанализировать разные пакеты, формируемые разными сценариями, и один и тот же сценарий можно проанализировать разными инструментами.

интерфейс между сервисом и сценарием получается весьма динамичным, поэтому твой тезис про пакет скриптов не работает. 


--------------------
PM MAIL   Вверх
Lipetsk
Дата 10.12.2013, 08:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


в форме ;)
*


Профиль
Группа: Участник
Сообщений: 180
Регистрация: 28.1.2009
Где: Липецк

Репутация: нет
Всего: 5



Цитата(CompWorm @  10.12.2013,  01:33 Найти цитируемый пост)
 LUA под виндой биндится только если QT на M$ компиляторе

биндинг можно написать свой

PM   Вверх
CompWorm
  Дата 11.12.2013, 01:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Допеределыватель
***


Профиль
Группа: Участник Клуба
Сообщений: 1689
Регистрация: 6.12.2004
Где: /

Репутация: нет
Всего: 31



ksnkLipetsk, тема не про LUA, а про паттерн.  smile 
даже если я реализую всё через скрипты, вопрос про интерфейс общения сервиса со сценарием останется открытым.


--------------------
PM MAIL   Вверх
Guinness
Дата 11.12.2013, 08:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 310
Регистрация: 21.6.2009
Где: Зеленоград

Репутация: 1
Всего: 10



Цитата(CompWorm @  11.12.2013,  02:24 Найти цитируемый пост)
про интерфейс общения сервиса со сценарием останется открытым

А что если создать объект Command?
Код

class Command
{
    public:
        /* ... */
        list<Argument> neededArguments();
        bool setArguments(const list<Argument> &args);
        RetType execute();
    private:
        list<Argument> m_args
};

class Argument
{
    public:
        // тут общая информация - название, обязательный или нет и т.п.
        ArgInfo info();
        // тут enum, можно что-нибудь и другое
        ArgType type();

        string value();
        // можно object, если это C# или Java
        void setValue(const string &str);
}

В принципе, класс Argument можно разбить и преобразовать к ArgumentType. И хранить все это дело в map<ArgType, Object>. Но это всё для статически типизированных языков - тут приходится придумывать костыли, ну или я не слишком ещё искушен в написании нормальных программ. Вообще, тут сильно зависит от языка и технологий, которые используются. Наверняка, это можно как-то прикрутить с помощью скриптовых языков, но я это никогда не делал и потому не знаю об этом)
PM MAIL   Вверх
CompWorm
  Дата 11.12.2013, 21:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Допеределыватель
***


Профиль
Группа: Участник Клуба
Сообщений: 1689
Регистрация: 6.12.2004
Где: /

Репутация: нет
Всего: 31



Цитата(Guinness @  11.12.2013,  08:22 Найти цитируемый пост)
А что если создать объект Command?

Guinness, да, да, похоже на правду, но тут две проблемы я сразу вижу:
1) нет способа проверить совместимость сценария с сервисом. то есть сервис тут не знает, что умеет сценарий и просто бомбит его командами.
в общем им понюхатся надо как-то ))
2) прикинь, на каждую команду по классу аргументов создавать придется... что-то громоздко выходит, не?
Цитата(Guinness @  11.12.2013,  08:22 Найти цитируемый пост)
Вообще, тут сильно зависит от языка и технологий, которые используются.

c++ для ориентировки))

я щас обжовываю другую мысль - сделать что-то типа контракта, но не в классической форме, так как уж очень макросы я не люблю, а в плюсах через них обычно предлагается. итак, идея создать общий для всех класс, аля абстрактный адаптер. он создается на стороне сервиса и инициализируется, условно, неким списком, описиваюшим команды и их аргументы, который получаем из сценария.

в результате, из сервиса мы зовем 
Код

IScenario scenarioObject = dll.getInstance();
Adapter* scenarioAdapter = new Adapter ( scenarioObject );
try{
    bool isFun = scenarioAdapter->makeSomeFun(42);
}catch (...) {LOG("this scenario doesn't support    bool makeSomeFun(int)    ");}

тут сигнатура bool makeSomeFun(int) заранее (compile-time) не прописана ни в Adapter,  ни в IScenario, но должна быть исполнима scenarioObject'ом.
если scenarioObject не способен выполнить эту команду, то Adapter или scenarioObject бросает исключение.

полагаю, без магии и макросов в Адаптере тут не обойтись, но зато писать сценарии и сервисы будет наглядно.
вот как такую хрень на c++/QT реализовать, или может кто ведел что похожее ?


--------------------
PM MAIL   Вверх
Guinness
Дата 12.12.2013, 08:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 310
Регистрация: 21.6.2009
Где: Зеленоград

Репутация: 1
Всего: 10



Цитата(CompWorm @  11.12.2013,  22:27 Найти цитируемый пост)
1) нет способа проверить совместимость сценария с сервисом. то есть сервис тут не знает, что умеет сценарий и просто бомбит его командами.
в общем им понюхатся надо как-то ))

Можно попробовать развить твою изначальную идею со списком команд. У сценария есть список команд, которые он может выполнить, и у сервиса есть список команд, которые ему необходимы для работы. В этом случае для каждого сервиса пишется свой "адаптер", который принимает на вход список команд сценария или сам сценарий. И уже потом предоставляет прозрачный интерфейс для сервиса как у тебя показано ниже.

Цитата(CompWorm @  11.12.2013,  22:27 Найти цитируемый пост)
2) прикинь, на каждую команду по классу аргументов создавать придется... что-то громоздко выходит, не?

Почему? Я думал про написание аналога QVariant. Но раз мы работаем с Qt, то всё нормально. Просто его и возьмем, т.е. получится как-то так:
Код

class Argument
{
    public:
        // тут общая информация - название, обязательный или нет и т.п.
        ArgInfo info();

        QMetaType::Type type();
        QVariant value();
        void setValue(const QVariant &val);
}

Цитата(CompWorm @  11.12.2013,  22:27 Найти цитируемый пост)
тут сигнатура bool makeSomeFun(int) заранее (compile-time) не прописана ни в Adapter,  ни в IScenario, но должна быть исполнима scenarioObject'ом.

Честно говоря, я не представляю, как это можно сделать в плюсах. Что-то подобное можно сделать на Python.
Хотя, слушай, в Qt есть мета-объектная система. Если не ошибаюсь, то там можно динамически добавлять свойства к объекту, только вот не знаю поможет это или нет в данном вопросе.
Цитата(CompWorm @  11.12.2013,  22:27 Найти цитируемый пост)
но зато писать сценарии и сервисы будет наглядно

У сервиса в этом случае получается строго ограниченный интерфейс для вызова команд сценария. Как не крути, т.к. ты в коде заранее указываешь что и как ему вызывать.

Это сообщение отредактировал(а) Guinness - 12.12.2013, 08:06
PM MAIL   Вверх
CompWorm
Дата 12.12.2013, 20:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Допеределыватель
***


Профиль
Группа: Участник Клуба
Сообщений: 1689
Регистрация: 6.12.2004
Где: /

Репутация: нет
Всего: 31



Guinness, спасибо+, пойду пересплю с новой информацией. попозже отпишу.

Цитата(Guinness @  12.12.2013,  08:04 Найти цитируемый пост)
Цитата(CompWorm @  11.12.2013,  22:27 Найти цитируемый пост)
тут сигнатура bool makeSomeFun(int) заранее (compile-time) не прописана ни в Adapter,  ни в IScenario, но должна быть исполнима scenarioObject'ом.
Честно говоря, я не представляю, как это можно сделать в плюсах.

забыл сказать, сервис конечно знает эту сигнатуру и может ее передать для анализа в адаптер. 
то есть нужно адаптеру сравнить сигнатуры он сервиса и сценария и сбиндит их.. аля сигнал слот как-то.


--------------------
PM MAIL   Вверх
Guinness
Дата 13.12.2013, 22:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 310
Регистрация: 21.6.2009
Где: Зеленоград

Репутация: 1
Всего: 10



Цитата(CompWorm @  12.12.2013,  21:20 Найти цитируемый пост)
спасибо+, пойду пересплю с новой информацией. попозже отпишу.

Рад, что чем-то помог. Но тут, я думаю, ещё много напильником придётся допиливать до работоспособной схемы)
PM MAIL   Вверх
CompWorm
Дата 16.12.2013, 21:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Допеределыватель
***


Профиль
Группа: Участник Клуба
Сообщений: 1689
Регистрация: 6.12.2004
Где: /

Репутация: нет
Всего: 31



с магией QT получилось следующее:
(это переписанный код на нашу терминологию, мог что-то лишнее стереть или добавить...)

i_scenario.h
Код

class IScenario: public QObject
{
    Q_OBJECT
public:
    virtual QSharedPointer< QMap<QString, const char*> > getSerialInterface()    = 0;
...
};

a_scenario_adapter.h (абстракция адаптера к сценарию. адаптер будет поставляться вместе с  сервисом.)
Код

#include "i_scenario.h"
class AScenarioAdapter : public IScenario
{
    Q_OBJECT
public slots:
    virtual void unsupportedSlot() = 0;
};

serial_connector.h (может IsA HasA)
Код

class SerialConnector
{
public:
    virtual  ~SerialConnector();
    void SerialConnector::connect(const QObject *sender, const QMap<QString, const char*> *senderFunctionality, 
const QObject *receiver, const QMap<QString, const char*> *receiverFunctionality, const char *unsupportedSlot);
    void disconnect();
private:
    const QObject *mSender;
    const QObject *mReceiver;
};

serial_connector.cpp
Код

#include "serial_connector.h"
SerialConnector::~SerialConnector()
{
    disconnect();
}
void SerialConnector::connect(const QObject *sender, const QMap<QString, const char*> *senderFunctionality,
 const QObject *receiver, const QMap<QString, const char*> *receiverFunctionality, const char *unsupportedSlot)
{
    mSender = sender;
    mReceiver = receiver;
    foreach (QString serialKey, senderFunctionality->keys())
    {
        if (receiverFunctionality->contains(serialKey))
            QObject::connect(mSender, (*senderFunctionality)[serialKey], mReceiver, (*receiverFunctionality)[serialKey]);
        else
            QObject::connect(mSender, (*senderFunctionality)[serialKey], mSender, unsupportedSlot);
    }
}
void SerialConnector::disconnect()
{
    if (mSender != NULL)
    {
        if (mReceiver != NULL)
            mSender->disconnect(mReceiver);
        else
            mSender->disconnect();
    }
}

scenario_adapter.h (через этот адаптер сервис будет общаться с неизвестным сценарием)
Код

#include "a_scenario_adapter.h"
#include "serial_connector.h"
class ScenarioAdapter : public AScenarioAdapter, public SerialConnector
{
    Q_OBJECT
public:
    ScenarioAdapter(IScenario *scenario);
    virtual QSharedPointer< QMap<QString, const char*> > getSerialInterface();
    // фасад команд, поддерживаемых сервисом:
    void makeBlackjack();
    void makeSomeFun();
signals:
    void makeBlackjackSignal();
    void makeSomeFunSignal();
public slots:
    virtual void unsupportedSlot();
private:
    IScenario                                        *mScenario;
    QSharedPointer<QMap<QString, const char*> >        mScenarioFunctionality;
};

scenario_adapter.cpp
Код

#include "scenario_adapter.h"
ScenarioAdapter::ScenarioAdapter(IScenario* scenario):
    mScenario(scenario)
{
    mScenarioFunctionality= QSharedPointer<QMap<QString, const char*> > (new QMap<QString, const char*>());
    mScenarioFunctionality.data()->insert("UUID~1", SIGNAL(makeBlackjackSignal()));
    mScenarioFunctionality.data()->insert("UUID~2", SIGNAL(makeSomeFunSignal()));
    SerialConnector::connect(this,mScenarioFunctionality.data(),mScenario,mScenario->getSerialInterface().data(), SLOT(unsupportedSlot()));
}
QSharedPointer<QMap<QString, const char*> > ScenarioAdapter::getSerialInterface()
{return mScenario->getSerialInterface();}

void ScenarioAdapter::unsupportedSlot(){//тут можно исключения бросать }

void ScenarioAdapter:: makeBlackjack()
{
    emit makeBlackjackSignal();
}

void ScenarioAdapter::makeSomeFun()
{
    emit makeSomeFunSignal();
}

some_scenario.h
Код

#include "i_scenario.h"
class SomeScenario: public IScenario
{
    Q_OBJECT
public:
    virtual QSharedPointer< QMap<QString, const char*> > getSerialInterface();
    ...
public slots:
     void makeSomeFun();
     void makeWhores();
};

some_scenario.cpp
Код

#include "some_scenario.h"
...
QSharedPointer< QMap<QString, const char*> > SomeScenario::getSerialInterface()
{
    QMap<QString, const char*>* map = new QMap<QString, const char*>();
    map->insert("UUID~2", SLOT(makeSomeFun()));
    map->insert("UUID~3", SLOT(makeWhores()));
    return QSharedPointer< QMap<QString, const char*> > (map);
}

void SomeScenario::makeSomeFun()
{
    qDebug() << "making some fun!";
}

void SomeScenario::makeWhores()
{
    qDebug() << "here are some whores!";
}


some_service.h (ну и собственно, как оно используется)
Код

#include "i_service.h"
#include "scenario_adapter.h"

class SomeService: public IService
{
    Q_OBJECT
public:
    virtual void setScenario(IScenario* scenario);
    ...
private:
   ScenarioAdapter *mScenarioAdapter;
};

some_service.cpp
Код

#include "some_service.h"
void SomeService::setScenario(IScenario* scenario);
{
    mScenarioAdapter = new ScenarioAdapter(scenario);
    mScenarioAdapter->makeBlackjack(); // unsupportedSlot()
    mScenarioAdapter->makeSomeFun(); // works.
}

итак, я написал свой вариант с блэкджеком и шлюхами. общая идея - подцепить поддерживаемые слоты сценария к сигналам адаптера, который поставляется с сервисом, ориентируясь на UUID.
в результате шлюхи ("UUID~3") не востребованы, блэкджек ("UUID~1") запитан на  unsupportedSlot() (откуда по желанию может бросить исключение), но счастье ("UUID~2") будет.
работает, смотрится нативно, но...
минус этого решения - emit doSomething() не возвращает ничего  smile 

позже попробую на колбеках повторить, чтобы победить эту проблему.


Это сообщение отредактировал(а) CompWorm - 16.12.2013, 21:59


--------------------
PM MAIL   Вверх
Guinness
Дата 17.12.2013, 07:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 310
Регистрация: 21.6.2009
Где: Зеленоград

Репутация: 1
Всего: 10



Цитата(CompWorm @  16.12.2013,  22:29 Найти цитируемый пост)
минус этого решения - emit doSomething() не возвращает ничего  smile 
позже попробую на колбеках повторить, чтобы победить эту проблему.

Можно попробовать boost::signals2 или хотя бы идею реализации колбеков взять оттуда.

Это сообщение отредактировал(а) Guinness - 17.12.2013, 07:14
PM MAIL   Вверх
CompWorm
  Дата 17.12.2013, 21:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Допеределыватель
***


Профиль
Группа: Участник Клуба
Сообщений: 1689
Регистрация: 6.12.2004
Где: /

Репутация: нет
Всего: 31



нет, все совсем просто. забыл вчера отписаться  smile 

some_scenario.h
Код

#include "i_scenario.h"
class SomeScenario: public IScenario
{
    Q_OBJECT
public:
    virtual QSharedPointer< QMap<QString, const char*> > getSerialInterface();
    ...
Q_INVOKABLE QString makeSomeFun();
Q_INVOKABLE bool makeWhores();
};

scenario_adapter.cpp
Код

QString ScenarioAdapter::makeSomeFun()
{
    QString result = "error";
    QMetaObject::invokeMethod(mScenario, "makeSomeFun", Qt::DirectConnection,
                              Q_RETURN_ARG(QString, result ));
    return result;
}

ничего коннектить не надо.
на чистом C++ без макросов или boost не получится, но делать надо тоже самое - вернуть массив пойнтеров на мемберы.
всем спасибо, тема закрыта. 

Это сообщение отредактировал(а) CompWorm - 17.12.2013, 21:55


--------------------
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Системный анализ, проектирование и UML"
Се ля ви

Форум "Системный анализ, проектирование и UML" предназначен для обсуждения вопросов, так или иначе связанных с этапами жизненного цикла автоматизированных (программных, информационных, автоматических) систем:

• предпроектные обследования объектов автоматизации;

• разработка концепции создания систем;

• моделирование бизнес-процессов (в т.ч. на UML);

• проектирование архитектуры систем;

• управление проектами;

• управление качеством;

• CASE-средства;

• реинжиниринг.


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Се ля ви.

 
3 Пользователей читают эту тему (3 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Системный анализ, проектирование и UML | Следующая тема »


 




[ Время генерации скрипта: 0.1617 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.