單件模式是一種較為簡單的創建型模式,在日常的開發中也常常使用。目的是在應用程序中保證某個類的對象僅能有一個。在C++的世界中,因為它不是純面向對象的語言,它支持全局變量的特性,所以在許多的場合它可以用全局變量來替代。非常典型的例子就是在MFC的應用程序中的theAPP對象,它就是一個全局的唯一對象。類似這樣的例子在C++的程序中有許多,在C++中存在這樣的現象是有一定的原因。這里先介紹一個基于C++實現的單件模式例子。
1
#pragma once
2
#include <deque>
3
4
/**/////////////////////////////////////////////////////////////////////////// 5
// Forward declaration
6
//
7
//
8
/**/////////////////////////////////////////////////////////////////////////// 9
class CSendJob;
10
class JobDeque;
11
12
/**//// <summary>
13
/// 本類為作業啟動信息儲存體
14
/// 此類僅為JobDeque所使用,不宜讓其它客戶使用
15
/// </summary>16
class JobStartInformation
17

{
18
friend class JobDeque;
19
public:
20
~JobStartInformation()
21
{
22
//Don`t delete the m_pJob pointer.
23
}
24
25
private:
26
JobStartInformation(CSendJob *pJob, BOOL bManual)
27
:m_pJob(pJob),
28
m_bManualStart(bManual)
29
{
30
}
31
32
public:
33
CSendJob* GetJob() const
{ return m_pJob; }
34
BOOL IsManualStart() const
{ return m_bManualStart; }
35
36
private:
37
BOOL m_bManualStart;
38
CSendJob *m_pJob;
39
};
40
41
class JobDeque
42

{
43
// Constructor and Destructor
44
public:
45
private:
46
virtual ~JobDeque(void);
47
private:
48
JobDeque(void);
49
JobDeque(const JobDeque &);
50
JobDeque& operator=(const JobDeque& );
51
52
// Methods:
53
public:
54
void Start( CSendJob *pJob, BOOL bManualStart);
55
void Completed( CSendJob* pJob);
56
private:
57
void Launch();
58
private:
59
USHORT m_usRunAmount;
60
std::deque<JobStartInformation > m_jobDeque;
61
62
public:
63
static JobDeque* GetJobDeque();
64
static void DestroyJobDeque();
65
66
private:
67
/**////<summary>
68
/// 可以并行執行的作業數目
69
///</summary>
70
static const USHORT m_susParallelAmount;
71
static JobDeque *m_spJobDeque;
72
73
#ifdef _DEBUG
74
void Validate();
75
#else
76
void Validate()
77
{
78
}
79
#endif
80
};
81
1
#include "StdAfx.h"
2
#include <algorithm>
3
#include "SendJob.h"
4
#include "JobDeque.h"
5
using namespace std;
6
7
// JobDeque.cpp
8
/**////<summary>
9
/// 這里僅對全局唯一的作業對象地址作比較
10
///</summary> 11
bool operator==(const JobStartInformation& lo, const JobStartInformation& ro) throw()
12

{
13
if(&lo == &ro)
14
{
15
return true;
16
}
17
18
if(NULL ==lo.GetJob() && NULL == ro.GetJob())
19
{
20
return true;
21
}
22
if(NULL == lo.GetJob() || NULL == ro.GetJob())
23
{
24
return false;
25
}
26
return lo.GetJob() == ro.GetJob();
27
}
28
29
bool operator!=(const JobStartInformation& lo, const JobStartInformation& ro) throw()
30

{
31
return !(lo == ro);
32
}
33
34
bool operator<(const JobStartInformation& lo, const JobStartInformation& ro) throw()
35

{
36
if(lo == ro)
37
{
38
return true;
39
}
40
return lo.GetJob() < ro.GetJob();
41
}
42
43
44
/**/////////////////////////////////////////////////////////////////////////// 45
// 靜態變量
46
const USHORT JobDeque::m_susParallelAmount = 1;
47
JobDeque* JobDeque::m_spJobDeque = NULL;
48
49
/**/////////////////////////////////////////////////////////////////////////// 50
// Constructor and Destructor
51
//
52
//
53
//
54
/**/////////////////////////////////////////////////////////////////////////// 55
JobDeque::JobDeque(void)
56

{
57
m_usRunAmount = 0;
58
}
59
60
JobDeque::~JobDeque(void)
61

{
62
}
63
64
/**/////////////////////////////////////////////////////////////////////////// 65
//
66
//
67
//
68
/**/////////////////////////////////////////////////////////////////////////// 69
void JobDeque::Start( CSendJob *pJob, BOOL bManualStart)
70

{
71
ATLASSERT(pJob && _T("Argument of pJob is NULL in JobDeque::Start."));
72
if(!pJob)
73
{
74
return;
75
}
76
77
JobStartInformation jobInfo(pJob, bManualStart);
78
deque<JobStartInformation >::iterator itFinder = find(m_jobDeque.begin(), m_jobDeque.end(), jobInfo);
79
if(itFinder == m_jobDeque.end())
80
{
81
m_jobDeque.push_back(jobInfo);
82
pJob->SetSyncState(CSendJob::COPYING);
83
}
84
Launch();
85
return;
86
}
87
88
void JobDeque::Completed( CSendJob* pJob )
89

{
90
ATLASSERT(pJob && _T("Argument of pJob is NULL in JobDeque::Start."));
91
if(!pJob)
92
{
93
return;
94
}
95
--m_usRunAmount;
96
Validate();
97
Launch();
98
return;
99
}
100
101
void JobDeque::Launch()
102

{
103
if(m_usRunAmount < m_susParallelAmount && m_jobDeque.size() > 0)
104
{
105
m_jobDeque.front().GetJob()->Send(m_jobDeque.front().IsManualStart());
106
m_jobDeque.pop_front();
107
++m_usRunAmount;
108
Validate();
109
}
110
}
111
112
JobDeque* JobDeque::GetJobDeque()
113

{
115
if (NULL == m_spJobDeque)
116
{
117
if 進入互斥區成功
118
{
119
if(NULL == m_spJobDeque)
120
{
121
m_spJobDeque = new JobDeque();
122
}
退出斥區
123
}
132
}
133
return m_spJobDeque;
134
}
135
136
void JobDeque::DestroyJobDeque()
137

{
138
if(m_spJobDeque)
139
{進入互斥區
140
delete m_spJobDeque;
141
m_spJobDeque = 0;
退出互斥區
142
}
143
}
144
145
#ifdef _DEBUG
146
void JobDeque::Validate()
147

{
148
if(m_susParallelAmount < m_usRunAmount || m_usRunAmount < 0)
149
{
150
ATLASSERT(_T("The counter in JobDeque work error!"));
151
}
152
}
153
#endif
154
在這里就省略全局實例的的例子,在上面的例子中不僅有取得實例的靜態函數,還有銷毀實例的靜態函數。客戶可以任何地方及任何時間調用獲得單件的函數,但是用戶只能且必須在應用程序的最后出口處調用釋放實例的函數。也曾看過C++實現的單件模式的例子,例子中沒有銷毀函數。這與C#,JAVA這樣的純面向對象實現的例子非常地相似。可是C++中并沒有自動垃圾回收裝置,所以還是要我們自己在某個地方銷毀new出來的這個全局實例對象。在上面可以看出C++實現的單件模式不及全局變量來得簡單而高效。我持有這個觀點是基于以下兩個方面的考慮
(1) 利用全局變量的方案來產生全局唯一實例時不用考慮多線程的情況,在上面的例子中產生實例時考慮了多線程情況。
(2) 利用全局變量的方案產生的實例對象會在程序退出時自動地銷毀對象,而單件模式需明確地delete實例。
這里并沒有斷言全局變量就優于單件模式,出于如下的考慮可能又會選用單件模式
(1) 全局變量的方式使代碼的耦合度提高了,而且需事先總體設計好。須保證開發人員和后續的維護人員不能再去產生的新的實例,theApp全局實例是MFC世界基礎的知識了。
(2) 單件實例的構造函數的參數中需要自定義對象實例時也會迫使開發人員選用單件模式。
我認為兩種方案都有優點和缺點,不能說一個方案絕對地優于另外一個方案。究竟選用哪種方案取決于應用的環境的約束以及開發人員的偏好。這里闡述了一些個人的觀點,希望能起到拋磚引玉的作用。