Этот вопрос адресован тем из нас, кто действительно сталкивался с этой проблемой. Я заинтересован в реальной работе, быстро решения.
Я использую API, который позволяет мне общаться через tcp / ip через соответствующий сокет Posix для получения потоковых данных в реальном времени. Это цены на финансовые инструменты и другие их статистические данные, все цифры. У меня есть один клиент Posix, который управляет этим соединением через сокет, это приложение Qt. Каждая форма (GUI) может запрашивать поток данных и отображать входящие цитаты.
каждый входящий поток принимается через обратный вызов в сокете (теперь я просто печатаю информацию)
void PosixClient::tickPrice( TickerId tickerId, TickType field, double price, int canAutoExecute) {
printf("tradingclient_1: tickPrice: \n");}
Концепция модели, представленная через этот API, заключается в том, что потоки распознаются благодаря tickerId
поле, поэтому, когда новые данные появляются на сокете tickPrice
метод запущен, и я должен назначить / уведомить и т. д. заинтересованные объекты об этом — то есть отправить данные в соответствующую форму графического интерфейса, дифференцировать их по tickerId
,
Как мне реализовать обмен данными, модель подписки для отправки данных в правильные объекты?
моя первая мысль — использовать в клиенте Posix
std::vector<int,my_function> v;
который может отобразить tickerId
для обратного вызова от объекта, который запросил данные. Что-то вроде Обозревателя.
Так что на данный момент у меня есть реализация Observer Pattern. Это основная идея, как это работает:
Наблюдаемое:
#include <cstdlib>
#include <ql/patterns/../patterns/observable.hpp>
#include <iostream>
/*
*
*/
class DataRepository : public QuantLib::Observable{
public:
void run();
int getData();
private:
int data;
};
void DataRepository::run(){
for(int i=0;i<10;++i){
data=i;
notifyObservers();
}
}
int DataRepository::getData(){
return data;
}
наблюдатель:
typedef boost::shared_ptr<DataRepository> pMyObservable;
class myObserver : public QuantLib::Observer{
public:
myObserver(pMyObservable obs, std::string n)
: observable(obs), name(n){
this->registerWith(observable);
}
myObserver(const myObserver &observer)
: Observer(observer),
observable(observer.observable),
name("Copy of "+observer.name){
}
void update(){
data=observable->getData();
std::cout<<"new data: "<<data<<std::endl;
}
private:
int data;
pMyObservable observable;
std::string name;
};
пример:
int main(int argc, char** argv) {
pMyObservable d(new DataRepository);
myObserver obs(d,"count_to_10_data");
d->run();
return 0;
}
результат:
новые данные: 0
новые данные: 1
новые данные: 2
новые данные: 3
новые данные: 4
новые данные: 5
новые данные: 6
новые данные: 7
новые данные: 8
новые данные: 9
RUN SUCCESSFUL (общее время: 93мс)
и вот мой реальный код на данный момент:
#include <ql/patterns/observable.hpp>
#include "Contract.h"#include <boost/function.hpp>
#include "IB_events.h"#include <list>
typedef boost::shared_ptr<IB::Record> rec_ptr;
typedef boost::shared_ptr<IB::TickPriceRecord> tickPriceRec_ptr;
typedef boost::shared_ptr<IB::TickSizeRecord> tickSizeRec_ptr;
typedef boost::shared_ptr<IB::TickStringRecord> tickStringRec_ptr;
class MarketData : public QuantLib::Observable {
public:
MarketData();
MarketData(IB::Event processedEvent, int tickerId, IB::Contract contractDescription):
processedEvent(processedEvent), tickerId(tickerId), contractDescription(contractDescription) {}
virtual ~MarketData();
int getTickerId(){ return tickerId; }
void putRecord(boost::shared_ptr<IB::Record> record){
record_=record;
}
boost::shared_ptr<IB::Record> getRecord(){
return record_;
}
IB::Event getEvent(){
return processedEvent;
}
private:
MarketData(const MarketData& orig);
boost::shared_ptr<IB::Record> record_;
// this MarketData object can handle these events
// any observer can subscribe to one of those events
IB::Event processedEvent;
int tickerId;
IB::Contract contractDescription;
};
в дальнейшем:
typedef boost::shared_ptr<MarketData> pMyObservable;
typedef boost::function<void (int tickerId, boost::shared_ptr<IB::Record> record)> f_action_ptr;
// one MarketDataObserver may observe one tickerId and for one event
// if you want to be notified when many events happened (i.e. TickSize and TickPrice)
// you can subscribe many MarketDataObservers to one and the same MarketData instance
class MarketDataObserver : public QuantLib::Observer{
public:
MarketDataObserver(pMyObservable obs, IB::Event observedEvent, f_action_ptr ptr)
: observable(obs), observedEvent_(observedEvent), f_ptr(ptr){
this->registerWith(observable);
}
MarketDataObserver(const MarketDataObserver &observer)
: Observer(observer),
observable(observer.observable){ // faction_ptr is not copied!
}
// object which subscribed to data stream using this MarketDataObserver
// will be notified about incoming IB::Record
void update() {
if (observable->getEvent() == observedEvent_) { // just to be 100% sure
boost::shared_ptr<IB::Record> data = observable->getRecord();
// here appropriate function is called: myTickPriceUpdate,
// myTickSizeUpdate or myTickStringUpdate depending on what
// subscribing object specified in f_action_ptr ptr
// in MarketDataObserver constructor
f_ptr(observable->getTickerId(), data);
}
}
private:
pMyObservable observable;
f_action_ptr f_ptr;
IB::Event observedEvent_; // the single event in which observer is interested
};
typedef boost::shared_ptr<MarketData> mktData_ptr;
Но это имеет много недостатков. Есть ли лучший / дешевле / быстрее подход? Что я могу улучшить?
Да, это в основном шаблон Observer, но вам нужна более аккуратная модель подписки.
Допустим, каждая форма реализует Observer
интерфейс, содержащий один метод dataChanged(TickerId)
который вызывается, когда данные для этого tickerId обновляются.
Если число форм GUI не очень велико, и при каждом изменении допустимо уведомлять их всех, я бы предложил следующее простое решение: «субъект» (PosixClient
) поддерживает список (возможно std::vector
) подписанных форм. Когда происходит обратный вызов из сокета, клиент уведомляет ВСЕ формы и те формы, которые заинтересованы в TickerId
обновить свои данные. Да, это не оптимально, так как большинство форм не требуют уведомления, но накладные расходы обычно не заметны.
Если существует слишком много форм графического интерфейса пользователя, чтобы уведомить их всех одновременно (хотя я не могу себе представить такой интерфейс), PosixClient
может поддерживать что-то вроде std::multimap
с TickerId
в качестве ключа и Observer*
как значение, поэтому он может уведомить только тех, кто подписался на изменения о данном tickerId.
И я бы посоветовал хранить все данные (цены и т. Д.) В каком-то общем объекте, который можно запрашивать через формы графического интерфейса пользователя, а не отправлять обновленные значения вместе с dataChanged
вызов.
Других решений пока нет …