尽管在某种程度上,单件模式(Singleton Pattem)是限制而不是改进类的创建,但它仍和其他创建型模式分在一组。单件模式可以保证一个类有且只有一个实列。并提供一个访问它的全局访问点。在程序设计过程中,有很多情况需要确保一个类只有一个实列。例如,系统中只能有一个窗口管理器,一个打印假托机,或者一个数据引擎访问点。PC机中可能有几个串口,但只能有一个Com1实列。
 

单例模式的特点:

  • 单例类只能有一个实例。
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其它对象提供这一实例。

Singleton的设计方式大体上分为两种:
      1.使用静态方法创建单件,我们将用Pascal(定制)实现
      2.利用C#特有机制创建单件,我们将用C#来实现

下面我们看看利用C#特有机制创建单件的过程。为了方便起见我们在以前的SimFactory例子上作一些修改看看Singleton的实现过程。
UML图:

具体代码:

  1using System;
  2
  3namespace 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

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

下面我们再看一个现实中的例子,连接池的问题。许多应用程序需要访问存储在数据库和其他数据源。要访问数据库中的数据,应用程序就需要建立到数据库的连接。然后,应用程序就可以使用连接进行数据访问。建立数据库连接会花相对较长的时间,因为在建立连接的过程中数据库服务器和应用程序之间必须进行协商。数据库连接也会消耗宝贵的系统资源,如CPU处理能力,内存,网络带宽。因此,很值得研究和应用技术来减少建立数据库连接的需要和活动连接数量。在这个前提下我们来看看程序代码:
  1using System;
  2using System.Drawing;
  3using System.Collections;
  4using System.ComponentModel;
  5using System.Windows.Forms;
  6using System.Data;
  7
  8using 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>

 17namespace 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
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

看看Pascal的实现:
  1unit PoolingTester1;
  2
  3interface
  4
  5uses
  6  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  7  Dialogs, StdCtrls,ADODB, DB;
  8
  9type
 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;
 32var
 33  Form1: TForm1;
 34  CriticalSection:TRTLCriticalSection;
 35 GlobalPooling:Pooling=nil;
 36implementation
 37
 38{$R *.dfm}
 39
 40{ Pooling }
 41procedure Pooling.Connect(NumberOfConnections: integer;
 42  PoolConnections: boolean);
 43var
 44i:integer;
 45ADOcnArr:array of TADOConnection;
 46ConnString: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);    //离开临界区
 61end;
 62
 63procedure Pooling.Disconnect;
 64var
 65i:integer;
 66begin
 67     for i:=0 to mConnectionArray.Count-1 do
 68       begin
 69          TADOConnection(mConnectionArray.Items[i]).Connected:=false;
 70       end;
 71end;
 72
 73procedure Pooling.FreeInstance;
 74begin
 75  inherited;
 76    GlobalPooling:=nil;
 77end;
 78
 79class function Pooling.GetInstance: Pooling;
 80begin
 81     if not Assigned(GlobalPooling)then
 82  GlobalPooling:=Pooling.Create;
 83  result:= GlobalPooling;
 84
 85end;
 86
 87class function Pooling.NewInstance: Tobject;
 88begin
 89   if not Assigned(GlobalPooling)then
 90      GlobalPooling:=Pooling(inherited NewInstance);
 91      result:=GlobalPooling;
 92end;
 93
 94
 95procedure TForm1.Button1Click(Sender: TObject);
 96begin
 97 try
 98   InitializeCriticalSection(CriticalSection); //初始化临界区
 99   GlobalPooling:=Pooling.Create;
100   GlobalPooling.Connect(20,true);
101 finally
102    GlobalPooling.Disconnect;
103 end;
104end;
105
106end.

在这个例子中我们可以看到Pooling这个连接池类是个Singleton类。即无论PoolingTester被实例化多少次,我们Pooling只会被初始化一次。以上思路可以用在Web应用程序中,由于IIS本身是作为COM+应用程序安装的,就COM+而言,同一个Web应用程序中的所有网页都在一个进程中运行。因此,它们也可共享相同的连接库。
posted on 2006-04-26 12:40  coffeeliu  阅读(926)  评论(0编辑  收藏  举报