Contents

QuantLib设计模式之:单例模式

什么是单例模式

在量化开发编程中经常使用单例模式,因为该模式可以使得一个对象在程序中只存在一个实例,比如需要定价的所有金融产品都是基于同一条曲线进行折现;为了方便我们使用,在QuantLib中也提供了单例模式的基类,用户只需要继承基类即可实现单例模式,例如:

1
2
3
4
5
6
7
8
class Foo : public Singleton<Foo>
{
 friend class Singleton<Foo>;
private:
 Foo(){}
public:

};

使用场景解析

这里我们分析使用单例存储构建全局的一个缓存,在缓存中存储不同类型的对象,这些对象在整个程序运行中都只有一份供其他的对象使用。

我们可以定义一个ObjectRepository对象,其中包含的方法包括:

  • void addObject(const std::string& id, const boost::any& obj, bool overwrite = true)
  • void deleteObject(const std::string& id)
  • void deleteAllObjects()
  • bool objectExists(const std::string& id)
  • unsigned int numberObjects() const
  • template boost::optional getObject(const std::string& id, Error& err)
  • template boost::optional getObject(const std::string& id)
  • template unsigned int getObjectCount()

这里使用了C++编程中两个有意思的特性,入参boost::any表示可以传入任何的对象,而获取的对象的方法getObject返回的对象使用了optional,这样避免了需要返回指定的类型,最后,程序使用了全局的map指针来存储对象。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#pragma once
#include <boost/optional.hpp>
#include <boost/optional/optional_io.hpp>
#include <ql/patterns/singleton.hpp>
#include <boost/any.hpp>
#include <ql/errors.hpp>
#include <map>
#include <string>
#include <iostream>
#include <ostream>

using namespace QuantLib;

class ObjectRepository : public QuantLib::Singleton<ObjectRepository>
{
 friend class QuantLib::Singleton<ObjectRepository>;

public:
 enum Error { NoError, CastingFailed, IdNotFound };

 // function to add objects

 // ---------------------------------------------------
 // add and delete objects
 void addObject(const std::string& id, const boost::any& obj, bool overwrite = true)
 {
  it_ = (*rep_).find(id);

  if (it_ != (*rep_).end())
  {
   if (overwrite == true)
   {
    (*rep_)[id] = obj;
   }
   else
   {
    QL_FAIL("Can not overwrite object.");
   }
  }
  else
  {
   (*rep_)[id] = obj;
  }
 }

 void deleteObject(const std::string& id)
 {
  it_ = (*rep_).find(id);

  if (it_ != rep_->end())
  {
   rep_->erase(it_);
  }
 }

 void deleteAllObjects()
 {
  (*rep_).clear();
 }

 bool objectExists(const std::string& id)
 {
  it_ = (*rep_).find(id);

  if (it_ != (*rep_).end())
  {
   return true;
  }
  else
  {
   return false;
  }
 }

 // ---------------------------------------------------
 unsigned int numberObjects() const
 {
  return (*rep_).size();
 }

 // function to get objects by id
 template <class T>
 boost::optional<T> getObject(const std::string& id, Error& err)
 {
  err = Error(NoError);

  it_ = (*rep_).find(id);

  if (it_ == rep_->end())
  {
   err = Error(IdNotFound);
   return boost::optional<T>();
  }

  T* ptrT = boost::any_cast<T>(&it_->second);

  if (ptrT == NULL)
  {
   err = Error(CastingFailed);
   return boost::optional<T>();
  }

  return boost::optional<T>(*ptrT);
 }

 template <class T>
 boost::optional<T> getObject(const std::string& id)
 {
  Error err = Error(NoError);
  return getObject<T>(id, err);
 }

 template <class T>
 unsigned int getObjectCount()
 {
  unsigned long res = 0;

  it_ = (*rep_).begin();

  while (it_ != (*rep_).end())
  {
   T* tmpObj = boost::any_cast<T>(&it_->second);

   if (tmpObj != NULL)
   {
    res++;
   }

   it_++;
  }

  return res;
 }

private:
 static boost::shared_ptr<std::map<std::string, boost::any>> rep_;
 std::map<std::string, boost::any>::iterator it_;
 Error err_;
};

boost::shared_ptr<std::map<std::string, boost::any>> ObjectRepository::rep_(new std::map<std::string, boost::any>());

std::ostream& operator<<(std::ostream& out, const ObjectRepository::Error& err)
{
 if (err == ObjectRepository::NoError)
 {
  return out << "NoError";
 }
 else if (err == ObjectRepository::CastingFailed)
 {
  return out << "CastingFailed";
 }
 else if (err == ObjectRepository::IdNotFound)
 {
  return out << "IdNotFound";
 }
 else
 {
  return out << "UnknownError";
 }
}

下面是测试的函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
namespace
{
 void addVariable()
 {
  double myDbl1 = 2.123;
  ObjectRepository::instance().addObject("dbl1", myDbl1);
 }
}

void DesignPatterns::testingSingleton1()
{
 addVariable();

 std::string myStr1("myStr1");
 std::vector<int> myVec1(6, 2);

 ObjectRepository::instance().addObject("str1", myStr1);
 ObjectRepository::instance().addObject("vec1", myVec1);

 std::string myDblId("dbl1");

 ObjectRepository::Error myErr;

 boost::optional<double> myDblGet = ObjectRepository::instance().getObject<double>(myDblId, myErr);

 std::cout << "Object exits?: " << ObjectRepository::instance().objectExists(myDblId) << std::endl;
 std::cout << "getObject double return: " << myDblGet << std::endl;
 std::cout << "dereferenced double return: " << *myDblGet << std::endl;
 std::cout << "Error: " << myErr << std::endl;
 std::cout << "-------------------------------" << std::endl;

 boost::optional<std::vector<int>> myVecGet =
  ObjectRepository::instance().getObject<std::vector<int>>("vec1");

 //std::cout << "getObject vector return: " << myVecGet << std::endl;

 BOOST_FOREACH(int x, *myVecGet) std::cout << x << std::endl;

}

最终的输出结果为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Object exits?: 1
getObject double return:  2.123
dereferenced double return: 2.123
Error: NoError
-------------------------------
2
2
2
2
2
2