单例模式的特点:
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其它对象提供这一实例。
Singleton的设计方式大体上分为两种:
1.使用静态方法创建单件,我们将用Pascal(定制)实现
2.利用C#特有机制创建单件,我们将用C#来实现
下面我们看看利用C#特有机制创建单件的过程。为了方便起见我们在以前的SimFactory例子上作一些修改看看Singleton的实现过程。
UML图:
具体代码:
1
using
System;
2
3
namespace
Singleton
4
{
5
/**//**//**//// <summary>
6
///============== Program Description==============
7
///Name:Singleton.cs
8
///Objective:Singleton
9
///Date:2006-04-26
10
///Written By coffee.liu
11
///================================================
12
/// </summary>
13
class Class1
14
{
15
/**//**//**//// <summary>
16
/// 应用程序的主入口点。
17
/// </summary>
18
[STAThread]
19
static void Main(string[] args)
20
{
21
Thinking tk1,tk2,tk3;
22
PersonSimFactory PSF=new PersonSimFactory();
23
Person PP=PSF.GetPerson("Yellow");
24
tk1=PP.GetTk;
25
PP.GetInfo();
26
Console.WriteLine(".");
27
PP=PSF.GetPerson("Black");
28
tk2=PP.GetTk;
29
PP.GetInfo();
30
Console.WriteLine(".");
31
PP=PSF.GetPerson("Write");
32
tk3=PP.GetTk;
33
PP.GetInfo();
34
if ((tk1==tk2)&&(tk2==tk3)&&(tk1==tk3))
35
Console.WriteLine("we are the same Thinking");
36
37
}
38
}
39
/**//**//**//// <summary>
40
/// 每个人都有思维
41
/// Thinking类被定为sealed类即不能被其他类继承
42
/// 我们把instance定义成了 static readonly属性,
43
/// 如果类中的static属性被任何方法使用时,.NET Framework将对这个属性进行初始化,
44
/// 于是在初始化Instance属性的同时Thinking类实例得以创建和装载。
45
/// 而私有的构造函数和readonly(只读)保证了Thinking不会被再次实例化,
46
/// 从而实现了Singleton的目的。
47
/// </summary>
48
49
public sealed class Thinking
50
{
51
public static readonly Thinking instance=new Thinking();
52
private Thinking()
{}
53
54
public static Thinking Instance
55
{
56
get
57
{
58
return instance;
59
}
60
}
61
public void Say()
{
62
Console.WriteLine("I have a Thinking");
63
}
64
65
}
66
67
public class Person
68
{
69
protected Thinking tk;
70
protected string sex,race;
71
public Thinking GetTk
72
{
73
get
{return tk;}
74
}
75
public string GetSex()
76
{
77
return sex;
78
}
79
public string GetRace()
80
{
81
return race;
82
}
83
public virtual void GetInfo()
84
{
85
}
86
}
87
88
public class YellowPerson:Person
89
{
90
public YellowPerson()
91
{
92
sex="Man";
93
race="Yellow";
94
}
95
public YellowPerson(string Ysex)
96
{
97
sex=Ysex;
98
race="Yellow";
99
}
100
public override void GetInfo()
101
{
102
Console.WriteLine("the "+race+" Person Info:"+sex);
103
tk=Thinking.Instance;
104
tk.Say();
105
}
106
}
107
108
public class BlackPerson:Person
109
{
110
public BlackPerson()
111
{
112
sex="Man";
113
race="Black";
114
}
115
public BlackPerson(string Bsex)
116
{
117
sex=Bsex;
118
race="Black";
119
}
120
public override void GetInfo()
121
{
122
Console.WriteLine("the "+race+" Person Info:"+sex);
123
tk=Thinking.Instance;
124
tk.Say();
125
}
126
}
127
128
public class WritePerson:Person
129
{
130
public WritePerson()
131
{
132
sex="Man";
133
race="Write";
134
}
135
public WritePerson(string Wsex)
136
{
137
sex=Wsex;
138
race="Write";
139
}
140
public override void GetInfo()
141
{
142
Console.WriteLine("the "+race+" Person Info:"+sex);
143
tk=Thinking.Instance;
144
tk.Say();
145
}
146
}
147
public class PersonSimFactory
148
{
149
public PersonSimFactory()
{}
150
public Person GetPerson(string RaceType)
151
{
152
if (RaceType=="Yellow")
153
return new YellowPerson();
154
else
155
if (RaceType=="Black")
156
return new BlackPerson();
157
else
158
if (RaceType=="Write")
159
return new WritePerson();
160
else
161
return new YellowPerson();
162
163
}
164
}
165
}
166

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

162

163

164

165

166

并且这样实现的Thinking类是线程安全的。
下面我们看看如何用Pascal语言,以类似静态方法来实现:
1
program singleton;
2
//==============
Program Description
==============
3
//
Name:singleton.dpr
4
//
Objective:singleton
5
//
Date
:
2006
-
04
-
27
6
//
Written By coffee.liu
7
//================================================
8
{$APPTYPE CONSOLE}
9
10
uses
11
SysUtils;
12
type Thinking
=
Class public
class
13
public
14
Class functionclass Function GetInstance()function GetInstance():Thinking;
15
作为Delphi来说,不能将contructor设为private,如果设为private,编译器将自动将
16
constructor更正为public
17
我们在这里重载NewInstance静态方法来控制构造函数防止其创建出多个实例
18
Class functionclass Function NewInstance()function NewInstance:Tobject;override;
19
procedure FreeInstance;override;
20
public procedure Say;
21
end;
22
type Person=Class protectedclass
23
protected
24
sex:string;
25
race:string;
26
tk:Thinking;
27
Property GetThinking()property GetThinking:Thinking read tk;
28
public Function GetSex()function GetSex():string;
29
public Function GetRace()function GetRace():string;
30
public procedure GetInfo;virtual;abstract;
31
32
end;
33
type YellowPerson=Class (class(Person)
34
constructor YellowPerson();
35
public procedure GetInfo;override;
36
end;
37
type BlackPerson=Class (class(Person)
38
constructor BlackPerson();
39
public procedure GetInfo;override;
40
end;
41
type WritePerson=Class (class(Person)
42
constructor WritePerson();
43
public procedure GetInfo;override;
44
end;
45
type PersonSimFactory=Class constructorclass
46
constructor PersonSimFactory();
47
public Function GetPerson()function GetPerson(RaceType:string):Person;
48
end;
49
var
50
GlobalThinking:Thinking=nil;
51
PSF:PersonSimFactory;
52
PP:Person;
53
{ Person }
54
55
Function Person()function Person.GetRace: string;
56
begin
57
result:=race;
58
end;
59
60
Function Person()function Person.GetSex: string;
61
begin
62
result:=sex;
63
end;
64
65
{ YellowPerson }
66
67
constructor YellowPerson.YellowPerson;
68
begin
69
sex:='Man';
70
race:='Yellow';
71
end;
72
procedure YellowPerson.GetInfo;
73
begin
74
inherited;
75
WriteLn('the '+race+' Person Info:'+sex);
76
end;
77
{ WritePerson }
78
79
procedure WritePerson.GetInfo;
80
begin
81
inherited;
82
WriteLn('the '+race+' Person Info:'+sex);
83
end;
84
85
constructor WritePerson.WritePerson;
86
begin
87
sex:='Man';
88
race:='Write';
89
end;
90
91
{ BlackPerson }
92
93
constructor BlackPerson.BlackPerson;
94
begin
95
sex:='Man';
96
race:='Black';
97
end;
98
99
procedure BlackPerson.GetInfo;
100
begin
101
inherited;
102
WriteLn('the '+race+' Person Info:'+sex);
103
end;
104
105
{ PersonSimFactory }
106
107
Function PersonSimFactory()function PersonSimFactory.GetPerson(RaceType: string): Person;
108
begin
109
110
if RaceType='Yellow' then
111
result:=YellowPerson.YellowPerson
112
else
113
if RaceType='Black' then
114
result:=BlackPerson.BlackPerson
115
else
116
if RaceType='Write' then
117
result:=WritePerson.WritePerson
118
else
119
result:=YellowPerson.YellowPerson;
120
121
end;
122
123
constructor PersonSimFactory.PersonSimFactory;
124
begin
125
inherited;
126
end;
127
{ Thinking }
128
129
procedure Thinking.FreeInstance;
130
begin
131
inherited;
132
这里赋值nil是必要的,作为delphi来说一个对象被释放之后,它的实例对应的变量并不会自动设定为nil
133
这里涉及到VMT的实现机理,具体情况请查看相关帮助
134
GlobalThinking:=nil;
135
end;
136
137
Class functionclass Function Thinking()function Thinking.GetInstance: Thinking;
138
begin
139
if not Assigned(GlobalThinking)then
140
GlobalThinking:=Thinking.Create();
141
result:= GlobalThinking;
142
end;
143
144
Class functionclass Function Thinking()function Thinking.NewInstance: Tobject;
145
begin
146
if not Assigned(GlobalThinking)then
147
GlobalThinking:=Thinking(inherited NewInstance);
148
result:=GlobalThinking;
149
end;
150
procedure Thinking.Say;
151
begin
152
WriteLn('I have a thinking!');
153
end;
154
var
155
tk1,tk2,tk3:Thinking;
156
begin
157
PSF:=PersonSimFactory.PersonSimFactory;
158
PP:=PSF.GetPerson('Yellow');
159
PP.GetThinking.Say;
160
tk1:=PP.GetThinking;
161
PP.GetInfo;
162
WriteLn('









..');
163
PP:=PSF.GetPerson('Black');
164
PP.GetThinking.Say;
165
tk2:=PP.GetThinking;
166
PP.GetInfo;
167
WriteLn('









..');
168
PP:=PSF.GetPerson('Write');
169
PP.GetThinking.Say;
170
tk3:=PP.GetThinking;
171
PP.GetInfo;
172
WriteLn('



..error


.');
173
PP:=PSF.GetPerson('Write11');
174
PP.GetThinking.Say;
175
PP.GetInfo;
176
if (tk1=tk2)and(tk2=tk3)then
177
WriteLn('we are the same thinking!');
178
end.

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

162












163

164

165

166

167












168

169

170

171

172










173

174

175

176

177

178

下面我们再看一个现实中的例子,连接池的问题。许多应用程序需要访问存储在数据库和其他数据源。要访问数据库中的数据,应用程序就需要建立到数据库的连接。然后,应用程序就可以使用连接进行数据访问。建立数据库连接会花相对较长的时间,因为在建立连接的过程中数据库服务器和应用程序之间必须进行协商。数据库连接也会消耗宝贵的系统资源,如CPU处理能力,内存,网络带宽。因此,很值得研究和应用技术来减少建立数据库连接的需要和活动连接数量。在这个前提下我们来看看程序代码:
1
using
System;
2
using
System.Drawing;
3
using
System.Collections;
4
using
System.ComponentModel;
5
using
System.Windows.Forms;
6
using
System.Data;
7
8
using
System.Data.SqlClient;
9
/**/
/// <summary>
10
///============== Program Description==============
11
///Name:PoolingTester.cs
12
///Objective:PoolingTester
13
///Date:2006-04-26
14
///Written By coffee.liu
15
///================================================
16
/// </summary>
17
namespace
ConnectionPooling
18
{
19
20
public class PoolingTester : System.Windows.Forms.Form
21
{
22
private System.Windows.Forms.Label label1;
23
private System.Windows.Forms.TextBox txtNumberOfConnections;
24
private System.Windows.Forms.Button btnConnect;
25
private System.Windows.Forms.Button btnDisconnect;
26
private System.Windows.Forms.CheckBox chkPooling;
27
Pooling PL;
28
/**//// <summary>
29
/// Required designer variable.
30
/// </summary>
31
private System.ComponentModel.Container components = null;
32
33
public PoolingTester()
34
{
35
//
36
// Required for Windows Form Designer support
37
//
38
InitializeComponent();
39
40
// Enable the Connect button and
41
// disable the Disconnect button
42
EnableButtons(false);
43
}
44
45
/**//// <summary>
46
/// Clean up any resources being used.
47
/// </summary>
48
protected override void Dispose( bool disposing )
49
{
50
if( disposing )
51
{
52
if (components != null)
53
{
54
components.Dispose();
55
}
56
}
57
base.Dispose( disposing );
58
}
59
60
Windows Form Designer generated code#region Windows Form Designer generated code
61
/**//// <summary>
62
/// Required method for Designer support - do not modify
63
/// the contents of this method with the code editor.
64
/// </summary>
65
private void InitializeComponent()
66
{
67
this.btnDisconnect = new System.Windows.Forms.Button();
68
this.btnConnect = new System.Windows.Forms.Button();
69
this.txtNumberOfConnections = new System.Windows.Forms.TextBox();
70
this.label1 = new System.Windows.Forms.Label();
71
this.chkPooling = new System.Windows.Forms.CheckBox();
72
this.SuspendLayout();
73
//
74
// btnDisconnect
75
//
76
this.btnDisconnect.Location = new System.Drawing.Point(280, 40);
77
this.btnDisconnect.Name = "btnDisconnect";
78
this.btnDisconnect.Size = new System.Drawing.Size(96, 23);
79
this.btnDisconnect.TabIndex = 2;
80
this.btnDisconnect.Text = "Disconnect";
81
this.btnDisconnect.Click += new System.EventHandler(this.btnDisconnect_Click);
82
//
83
// btnConnect
84
//
85
this.btnConnect.Location = new System.Drawing.Point(280, 8);
86
this.btnConnect.Name = "btnConnect";
87
this.btnConnect.Size = new System.Drawing.Size(96, 23);
88
this.btnConnect.TabIndex = 2;
89
this.btnConnect.Text = "Connect";
90
this.btnConnect.Click += new System.EventHandler(this.btnConnect_Click);
91
//
92
// txtNumberOfConnections
93
//
94
this.txtNumberOfConnections.Location = new System.Drawing.Point(176, 8);
95
this.txtNumberOfConnections.Name = "txtNumberOfConnections";
96
this.txtNumberOfConnections.TabIndex = 1;
97
this.txtNumberOfConnections.Text = "";
98
//
99
// label1
100
//
101
this.label1.Location = new System.Drawing.Point(8, 8);
102
this.label1.Name = "label1";
103
this.label1.Size = new System.Drawing.Size(168, 23);
104
this.label1.TabIndex = 0;
105
this.label1.Text = "Number of Connections:";
106
//
107
// chkPooling
108
//
109
this.chkPooling.Location = new System.Drawing.Point(8, 40);
110
this.chkPooling.Name = "chkPooling";
111
this.chkPooling.Size = new System.Drawing.Size(264, 24);
112
this.chkPooling.TabIndex = 3;
113
this.chkPooling.Text = "Pool Connections";
114
//
115
// PoolingTester
116
//
117
this.AutoScaleBaseSize = new System.Drawing.Size(6, 15);
118
this.ClientSize = new System.Drawing.Size(384, 72);
119
this.Controls.AddRange(new System.Windows.Forms.Control[]
{
120
this.chkPooling,
121
this.btnDisconnect,
122
this.btnConnect,
123
this.txtNumberOfConnections,
124
this.label1});
125
this.Name = "PoolingTester";
126
this.Text = "ADO.NET Connection Pooling Test Form";
127
this.ResumeLayout(false);
128
129
}
130
#endregion
131
132
/**//// <summary>
133
/// The main entry point for the application.
134
/// </summary>
135
[STAThread]
136
static void Main()
137
{
138
Application.Run(new PoolingTester());
139
}
140
141
// Connect to database
142
private void btnConnect_Click(object sender, System.EventArgs e)
143
{
144
//create Pooling object
145
this.PL=Pooling.Instance;
146
PL.Connect(Convert.ToInt32(txtNumberOfConnections.Text), chkPooling.Checked);
147
EnableButtons(true);
148
}
149
150
// Disconnect from the database
151
private void btnDisconnect_Click(object sender, System.EventArgs e)
152
{
153
//create Pooling object
154
this.PL=Pooling.Instance;
155
PL.Disconnect();
156
EnableButtons(false);
157
}
158
159
// Enable the Connect and Disconnect buttons depending on
160
// whether or not it is connected to the database
161
private void EnableButtons(bool Connected)
162
{
163
btnConnect.Enabled = !Connected;
164
btnDisconnect.Enabled = Connected;
165
}
166
167
168
}
169
sealed class Pooling
170
{
171
private static bool balancer=false;
172
private Pooling()
{}
173
public static readonly Pooling instance=new Pooling();
174
175
public static Pooling Instance
176
{
177
get
178
{
179
return instance;
180
}
181
}
182
private ArrayList mConnectionArray = null;
183
private const string mcConnString =
184
"Data Source=(local);" +
185
"Integrated Security=SSPI;" +
186
"Initial Catalog=Northwind";
187
public void Disconnect()
188
{
189
190
balancer=true;
191
foreach (SqlConnection cn in mConnectionArray)
192
{
193
cn.Close();
194
}
195
}
196
public void Connect(int NumberOfConnections, bool PoolConnections)
197
{
198
lock (typeof(Pooling))
199
{
200
balancer=false;
201
string ConnString = mcConnString + ";Pooling=" + PoolConnections;
202
203
mConnectionArray = new ArrayList(NumberOfConnections);
204
for (int Idx = 0; Idx < NumberOfConnections; ++Idx)
205
{
206
SqlConnection cn = new SqlConnection(ConnString);
207
cn.Open();
208
mConnectionArray.Add(cn);
209
}
210
}
211
}
212
}
213
}
214

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

162



163

164

165

166

167

168

169

170



171

172



173

174

175

176



177

178



179

180

181

182

183

184

185

186

187

188



189

190

191

192



193

194

195

196

197



198

199



200

201

202

203

204

205



206

207

208

209

210

211

212

213

214

看看Pascal的实现:
1
unit PoolingTester1;
2
3
interface
4
5
uses
6
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
7
Dialogs, StdCtrls,ADODB, DB;
8
9
type
10
TForm1
=
class(TForm)
11
Button1: TButton;
12
procedure
Button1Click(Sender: TObject);
13
private
14
{ Private declarations }
15
public
16
{
Public
declarations }
17
end
;
18
const mcConnString
=
'
Provider=SQLOLEDB.1;Integrated Security=SSPI;
'
19
+
'
Persist Security Info=False;User ID=sa;Initial Catalog=Northwind
'
;
20
21
type Pooling
=
class
22
private
23
mConnectionArray :TList;
24
public
25
procedure
Disconnect();
26
procedure
Connect(NumberOfConnections:
integer
;PoolConnections:boolean);
27
Class
Function
GetInstance():Pooling;
28
class
Function
NewInstance():Tobject;override;
29
procedure
FreeInstance;override;
30
31
end
;
32
var
33
Form1: TForm1;
34
CriticalSection:TRTLCriticalSection;
35
GlobalPooling:Pooling
=
nil;
36
implementation
37
38
{$R
*
.dfm}
39
40
{ Pooling }
41
procedure
Pooling.Connect(NumberOfConnections:
integer
;
42
PoolConnections: boolean);
43
var
44
i:
integer
;
45
ADOcnArr:array
of
TADOConnection;
46
ConnString:string;
47
begin
48
EnterCriticalSection(CriticalSection);
//
进入临界区
49
mConnectionArray:
=
TList.
Create
;
50
setlength(ADOcnArr,NumberOfConnections);
51
for
i:
=
0
to
NumberOfConnections
-
1
do
52
begin
53
ConnString:
=
mcConnString
+
'
;Pooling=
'
+
booltostr(PoolConnections,true);
54
ADOcnArr
[
i
]
:
=
TADOConnection.
Create
(Application);
55
ADOcnArr
[
i
]
.ConnectionString:
=
ConnString;
56
ADOcnArr
[
i
]
.LoginPrompt:
=
false;
57
ADOcnArr
[
i
]
.Connected:
=
true;
58
mConnectionArray.
Add
(ADOcnArr
[
i
]
);
59
end
;
60
LeaveCriticalSection(CriticalSection);
//
离开临界区
61
end
;
62
63
procedure
Pooling.Disconnect;
64
var
65
i:
integer
;
66
begin
67
for
i:
=
0
to
mConnectionArray.
Count
-
1
do
68
begin
69
TADOConnection(mConnectionArray.Items
[
i
]
).Connected:
=
false;
70
end
;
71
end
;
72
73
procedure
Pooling.FreeInstance;
74
begin
75
inherited;
76
GlobalPooling:
=
nil;
77
end
;
78
79
class
function
Pooling.GetInstance: Pooling;
80
begin
81
if
not
Assigned(GlobalPooling)
then
82
GlobalPooling:
=
Pooling.
Create
;
83
result:
=
GlobalPooling;
84
85
end
;
86
87
class
function
Pooling.NewInstance: Tobject;
88
begin
89
if
not
Assigned(GlobalPooling)
then
90
GlobalPooling:
=
Pooling(inherited NewInstance);
91
result:
=
GlobalPooling;
92
end
;
93
94
95
procedure
TForm1.Button1Click(Sender: TObject);
96
begin
97
try
98
InitializeCriticalSection(CriticalSection);
//
初始化临界区
99
GlobalPooling:
=
Pooling.
Create
;
100
GlobalPooling.Connect(
20
,true);
101
finally
102
GlobalPooling.Disconnect;
103
end
;
104
end
;
105
106
end
.

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

在这个例子中我们可以看到Pooling这个连接池类是个Singleton类。即无论PoolingTester被实例化多少次,我们Pooling只会被初始化一次。以上思路可以用在Web应用程序中,由于IIS本身是作为COM+应用程序安装的,就COM+而言,同一个Web应用程序中的所有网页都在一个进程中运行。因此,它们也可共享相同的连接库。