设计模式:单例

设计模式:单例 概述

如果您只希望一个类的一个实例,请使用“单例设计模式”。 该单个实例必须具有单个全局访问点。 也就是说,无论对象隐藏在哪里,每个人都需要访问它。

全局访问点是对象的Instance()方法。

需要防止单个用户创建自己的Singleton实例。

Singleton代表全局变量的替换。 专业的C ++开发人员出于各种原因避免使用全局变量。

如果应用程序需要许多Singleton对象,则可能有必要构造一个称为注册表的Singletons容器。 在这里,单个Singleton对象可以与密钥一起存储以进行检索。

单例课程–第1部分

所示的SysParms类表示能够成为Singleton的类。 默认构造函数受保护,因此只有派生类可以执行它。 这样就可以创建派生类的实例,但不能创建SysParms类本身的实例。

同样,SysParms析构函数也受保护,因此只能由派生对象调用。 这样可以防止任何用户删除Singleton。 当然,析构函数是虚拟的,它允许SysParms类与多态一起使用。

Instance()方法是静态的,因此您不需要SysParms对象来调用它。 实际上,首先将使用Instance()方法创建SysParms对象。 这称为

创建对象的惰性方法。
 
class SysParms
{     
   private:
        SysParms(const SysParms&);  //Prevents making a copy 
   protected:
        SysParms();       //Only a SysParms member can call this
                     //Prevents a user from creating singleton objects
        virtual ~SysParms();  //Prevents just anyone from deleting the singleton 
   public:  
       //The "official" access point.
       static SysParms* Instance(); 
};  
SysParms类的实现本身将在源文件中。

在这里,您可以看到在匿名名称空间中实现为变量的单个实例的地址。 原因是匿名名称空间的成员可以在该名称空间所在的文件外部访问。 也就是说,它们具有内部链接。

实例地址的初始值设置为0。只有在需要时,才会实际创建SysParms单例。

静态SysParms :: Instance()方法仅检查匿名命名空间中实例的地址,如果地址为零,则该方法在堆上创建一个新的SysParms对象,并将堆地址存储在命名空间中。

如果匿名名称空间中的地址不为零,则表示单例已经创建。 在这种情况下,Instance()方法仅返回名称空间中的地址。

 
//Use anonymous namespace to force internal linkage for instance 
namespace
{
    SysParms* instance = 0;        //Address of the singleton 
} 
SysParms::SysParms()
{ 
}
SysParms::~SysParms()
{
    delete instance;
                instance = 0;
} 
//The "official" access point
SysParms* SysParms::Instance()
{
   //"Lazy" initialization. Singleton not created until it's needed
   if (!instance)
   {
      instance = new SysParms;
   }
   return instance;
} 
使用简单的单例

若要使用SysParms单例,只需调用Instance()方法,您将收到单例的地址。

从那里,您可以执行单例上的任何方法。


int main()
{
   SysParms* theSysParmsSingleton;
   cout << "Example A: Creating only one instance" << endl; 
   //
   //SysParms obj;  //ERROR: singleton objects cannot be created by the user
   //
   //Use Instance() method to locate singleton. 
   //Instance() is static as we have no object
   theSysParmsSingleton = SysParms::Instance();
   //
   cout << "theSysParmsSingleton is located at " << &theSysParmsSingleton 
          << endl;    
   return 0;
} 
单例课程–第2部分

在第二部分中,将扩展SysParms类并添加一个派生类。 均为单例的派生类的实例与检索键一起存储在称为注册表的容器中。

在此示例中,单例对象处理排序序列,因此每个排序序列都会有一个单例对象。

在这种情况下,排序顺序是升序或降序。

已添加Register()方法以将单例存储在注册表中。

已添加Lookup()方法以从注册表中检索单例。

最后,还有一些访问器方法可用于从单例中更改或检索参数。 为简单起见,参数只是一个简单的枚举。 在实际程序中,参数将作为Parameter类的对象返回。

请注意:不建议使用公共虚拟功能,因为公共虚拟功能违反了接口与实现分离的概念。 在此示例中,它们被公开显示为仅用于指导目的。


class SysParms
    private:
         SysParms(const SysParms&);  //Prevents making a copy 
    protected:
         SysParms();       //Only a SysParms member or subclass can call this
         virtual ~SysParms(); //Prevent just anyone from deleting the singleton 
    public:
       enum Parms {ASCENDING, DESCENDING};   
    //The "official" access point. 
    static SysParms* Instance(const std::string& name); 
    //Register the singleton object by a name
    //Returns false if registration failed
    bool Register(const std::string& name, SysParms* obj); 
    //Returns 0 if name not in registry
    SysParms* Lookup(const std::string& name); 
   //Parameter Interface:
   //These should really be private. 
   virtual Parms GetParms()= 0; 
   virtual void SetParms(Parms in) = 0; 
};  
SysParms实现文件现在扩展为:

extern SingletonRegistry theRegistry;  
SysParms::SysParms()
{ 
}
SysParms::~SysParms()
{ 
} 
//The "official" access point
//
//You look up the singleton in theRegistry and return its address
//
SysParms* SysParms::Instance(const string& name)
{ 
    return theRegistry.Lookup(name); 
} 
bool SysParms::Register(const string& name, SysParms* obj)
{
    return theRegistry.Add(name, obj);
} 
SysParms* SysParms::Lookup(const string& name)
{
    return theRegistry.Lookup(name);
} 
您可以看到Register()和Lookup()方法的实现。 注册表本身在其自己的实现文件中。

这里,STL映射用于注册表。


class SingletonRegistry
{
   private:
    std::map<std::string, SysParms*> theRegistry;         
   public:
    //Default ctor and dtor of std::map<> will be used 
    //Locate 
    SysParms*   Lookup(const std::string& name);         
    //Add
    bool Add(const std::string& name, SysParms* obj);
}; 
注册表实现如下所示。

只需通过使用提供的字符串作为检索键和指向单例对象的指针创建pair <>对象,即可将单例存储在注册表中。 该对将插入地图容器中。

通过使用指定的字符串访问地图容器来检索单例,该字符串用于从地图容器中检索对对象。 该对的第二个成员是与提供的字符串相对应的单例的地址。 如果单例不在注册表中,则该方法返回空指针。


extern SingletonRegistry theRegistry; 
//Registry of Singletons. 
SysParms* SingletonRegistry::Lookup(const string& name)
{
    pair<string, SysParms*> theEntry;
    map<string, SysParms*>::iterator itr = theRegistry.find(name);
    if (itr == theRegistry.end())
    {
        return 0;  //not found
    }
    return itr->second;    
} 
bool SingletonRegistry::Add(const string& name, SysParms* obj)
{
    pair<string, SysParms*> theEntry;
    theEntry.first = name;
    theEntry.second = obj; 
    theRegistry.insert(theEntry); 
    return true;   
} 
放置在注册表中的单例是:

class SortParms :public SysParms
{ 
   private:
     enum  Parms seq;  
   public:
    SortParms(const std::string& name);
    ~SortParms();
    virtual Parms GetParms(); 
    virtual void SetParms(Parms in);
}; 
并实现为:

extern SingletonRegistry theRegistry; 
SortParms::SortParms(const std::string& name)
{
    theRegistry.Add(name, this);
}
SortParms::~SortParms()
{ 
}   
enum SysParms::Parms SortParms::GetParms()
{
    return this->seq; 
}
void SortParms::SetParms(Parms in)
{
        this->seq = in;
} 
请注意,这些单例在创建时会通过将注册代码作为单例构造函数的一部分进行注册。

在单独的源文件中,创建注册表,然后创建单例。 同样,单例对象位于匿名名称空间中,这使它们无法从任何位置访问。 唯一的全局参考点不是注册表上的Lookup()方法。 这是实现:


//The Registry object 
    SingletonRegistry theRegistry;
//
//The SortParms objects
//
//The anonymous namespace makes them publicly inaccessible outside this file.
//
namespace
{
   SortParms obj(std::string("SortParms"));       //The singleton itself
   SortParms obj1(std::string("SortParmsForPerson"));    //The singleton itself
   SortParms obj2(std::string("SortParmsForDate"));    //The singleton itself
} 
上面的实现基于以下特征:按声明顺序创建全局变量,并且必须在main()启动之前创建所有全局变量。

基于此,您可以看到创建了注册表,之后是三个单例。 这些单例使用构造函数中的字符串注册自己。

用户现在必须使用这些字符串之一调用Instance()方法。 Instance()将使用该字符串在注册表中查找单例并返回指向它的指针。

 
int main()
{
   SysParms* theSysParmsSingleton;
   cout << "Example B: Creating a registered singleton" << endl; 
   //
   //SysParms obj;  //ERROR: singleton objects cannot be created by the user
   //
   //Use Instance() method to locate singleton. 
   theSysParmsSingleton = SysParms::Instance("SortParms"); 
   //
   cout << "the SortParms Singleton is located at " << theSysParmsSingleton 
          << endl; 
   theSysParmsSingleton = SysParms::Instance("SortParmsForPerson"); 
   //
   cout << "the SortParmsForPerson Singleton is located at "
           <<   theSysParmsSingleton << endl; 
   theSysParmsSingleton = SysParms::Instance("SortParmsForDate"); 
   //
   cout << "the SortParmsForDate Singleton is located at "
          << theSysParmsSingleton << endl; 
   //Set a sort sequence for Person
   SysParms::Instance("SortParmsForPerson")->SetParms(SysParms::ASCENDING); 
   //Set a sort sequence for Date
   SysParms::Instance("SortParmsForDate")->SetParms(SysParms::DESCENDING);  
   //Retrieve sort sequence
   enum SysParms::Parms rval = SysParms::Instance("SortParmsForPerson")->GetParms();
   if (rval == SysParms::ASCENDING)
   { 
      cout << "Sort Person Ascending" << endl;
   }
   else
   {
      cout << "Sort Person Descending" << endl;
   } 
   //Retrieve sort sequence
   rval = SysParms::Instance("SortParmsForDate")->GetParms();
   if (rval == SysParms::ASCENDING)
   {
      cout << "Sort Date Ascending" << endl;
   }
   else
   {
      cout << "Sort Date Descending" << endl;
   }
   return 0;
} 
使用手柄

本文中的示例使用指针,因为通常会理解指针语法。 但是,建议在实际应用中使用句柄。 您应该参考“ C / C ++文章”部分中有关“句柄”的文章。

更多的信息

请参阅Erich Fromm等人的《设计模式》一书,Addison-Wesley,1994年。

本文仅显示Singleton模式的概念基础,而没有显示使用此模式的动机和后果。

版权所有2007 Buchmiller Technical Associates美国北华盛顿本德

From: https://bytes.com/topic/c/insights/656124-design-patterns-singleton

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值