設計模式之單例

  設計模式系列博客是學習《大話設計模式》的學習筆記,期間參考了一些網絡上的資源,設計模式之路,纔剛剛開始,如今掌握理論知識,寫一些簡單的demo,從此但願能靈活的應用在項目中,讓本身的代碼可以具備更高的可複用性和可擴展性,示例代碼中,方法、變量、類的命名可能並不符合規範,從此要多閱讀優秀的代碼,在編程中遵循《Goocle C++ 編程指南》,養成良好的代碼風格,爲本身也爲團隊留下優質的代碼。本系列博客中全部的demo都是在CentOS7.4 - 64 位下寫的,gcc版本爲4.8.5。有學習在linux下使用gcc編譯器寫C++代碼的能夠一塊兒交流一下,分享下學習資料和學習心得乃至經驗,本身一我的搗鼓真的太痛苦了。中間有什麼寫的很差的地方,請多多指教。謝謝。html

  編程是一門技術,是咱們賴以生活的職業技能,可是除此以外呢?《大話設計模式》這本書裏說編程是一門藝術。我相信它確實是一門藝術,計算的藝術,也是人類文明進化史上最具備奇思妙想的一門藝術之一。最近我在想,編程對我來講意味着什麼?是數之不盡的需求變動,仍是和這個不完善的體系或者沒有什麼解決問題思惟的人又或者對系統沒有宏觀認知的人鬥智鬥勇仍是不理世事,不辭辛苦埋頭苦幹,賺一口飯錢呢?我以爲都不是,有人說只有生活美滿的人才能寫出完美的代碼,那麼若是以爲心力交瘁多是一種不太正常的狀態。面對編程,說不上追求技術的極致那麼誇張(技術永無邊界),但至少要追求更高、更快、更強吧。編程到底意味着什麼呢?有些人爲錢而來,有些人不知何所來。總之不管如何,都不要磨滅對技術的熱情,永遠保持求知若渴的心態。linux

  最後感謝《大話設計模式》這本書,提供瞭如此之好的啓蒙方式,感謝網絡上的各路大神提供的優質資源。ios

知識補給站

  CentOS7.4支持中文顯示c++

  寫這個系列博客的時候還不知道這回事,因此輸出部分都是拿英文寫的,英文水平,實在是令我汗顏。上面引用的另一篇博客是講在系統(不帶圖形界面)已經安裝好的狀況下怎麼支持中文顯示的操做,通過我實際操做確實是有效的。算法

  

  我打算在這部分連接一些其它的基本知識,例如繼承的知識。可是如今尚未寫好,寫好以後會把連接放在這裏,還有一些我在學習怎麼在linux下用C++進行編程的一些心得也會放在這裏,以備往後查閱。最後學號英語真的很重要。數據庫

設計模式分類

  最先提出設計模式的時候總共有23種,能夠分爲3大類:(Gof)編程

設計模式分類
類型 描述
建立型模式(Creational Patterns) 用於構建對象,以便它們能夠從實現系統中分離出來。
結構型模式(Structural Patterns) 用於在許多不一樣的對象之間造成大型對象結構
行爲型模式(Behavioral Patterns) 用於管理對象之間的算法、關係和職責

建立型模式:

  單例模式(Singleton Pattern) 
  保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。設計模式

        簡單工廠模式安全

  簡單工廠模式是屬於建立型模式,又叫作靜態工廠方法(Static Factory Method)模式,但不屬於23種GOF設計模式之一。簡單工廠模式是由一個工廠對象決定建立出哪種產品類的實例。網絡

       工廠模式(Factory Method Pattern)

  定義一個用於建立對象的接口,讓子類決定將哪個類實例化。Factory Method 使一個類的實例化延遲到其子類。工廠模式是簡單工廠模式的升級版。

    抽象工廠模式(Abstract Factory Pattern)

       提供一個建立一系列相關或相互依賴對象的接口,而無需指定它們具體的類,抽象工廠模式是工廠模式的升級版。

  建造者模式(Builder Pattern)

  將一個複雜對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的表示。

  原型模式(Prototype Pattern)

  用原型實例指定建立對象的種類,而且經過拷貝這個原型來建立新的對象。

  

 

結構型模式:

  裝飾模式(Decorator)

  動態的給一個對象添加一些額外的職責,就添加功能來講,裝飾模式比生成子類更爲靈活。

        代理模式(Proxy Pattern)

  代理(Proxy)模式,爲其它對象提供一種代理以控制對這個對象的訪問。在某些狀況下一個對象不適合或者不能直接引用另外一個對象,而代理對象能夠在客戶端和目標對象之間起到中介做用。

  享元模式(Flyweight Pattern)

  享元模式(Flyweight),運用共享技術有效的支持大量細粒度的對象。

  組合模式(Composite Pattern)

  組合模式(Composite),將對象組合成樹形結構以表示‘部分-總體’的層次結構。組合模式使得用戶對單個對象(即葉子構件)和組合對象(即組合構件)的使用具備一致性。(例如,你能夠在word裏對單個字和一行字採用一樣的操做)注意,這裏說的樹就是一顆樹,沒有任何的限制,它能夠是任何形狀的。這棵樹它是靠對象之間的組合關係構建起來,而非數據結構意義上的樹。

  橋接模式(Bridge Pattern)

  橋接模式(Bridge),將抽象部分與它的實現部分分離,使它們均可以獨立地變化。這裏的抽象與它的實現分離,並非指讓抽象類和派生類分類,這沒有任何意義。實現指的是抽象類和它的派生類用來實現本身的對象。其實就是說,一個系統的實現,可能有多個角度的分類,每一種分類都有可能變化,那麼就把這種多角度分離出來讓它們獨自變化。舉例來講,手機便可以按照品牌分類(手機是抽象,功能是實現),也能夠按照功能分類(功能是實現,而手機變成了抽象)。

  外觀模式(Facade Pattern)

  外觀模式(Facade Pattern)又稱門面模式:爲子系統中的一組接口提供一個一致的界面,此模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。

  適配器模式(Adapter Pattern)

  適配器模式(Adapter):將一個類的接口轉換成客戶但願的另一個接口。Adapter模式使得本來因爲接口不兼容而不能一塊兒工做的那些類能夠一塊兒工做。

行爲型模式

  策略模式(Strategy Pattern)

  策略(Strategy):它定義了算法家族,分別封裝起來,讓它們之間能夠互相替換,此模式讓算法的變化,不會影響到使用算法的客戶。

  模板模式(Template Pattern)

  模板(Template)模式,定義一個操做中算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類能夠不改變一個算法的結構便可重定義該算法的某些特定步驟。

  觀察者模式(Observer Pattern)

  觀察者模式又叫作發佈-訂閱(Publish/Subscribe)模式。它定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態發生變化時,會通知全部的觀察者對象,使它們可以自動更新本身。

  狀態(State Pattern)模式

  狀態(State)模式,當一個對象的內在狀態改變時容許改變其行爲,這個對象看起來就像是改變了其類。狀態模式主要解決的是當控制一個對象狀態轉換條件表示式過於複雜時的狀況。把狀態的判斷邏輯轉移到表示不一樣狀態的一系列類當中,能夠把複雜的判斷邏輯簡化。固然若果這個判斷條件很簡單,那麼就沒有必要用狀態模式了。

  備忘錄(Memento Pattern)模式

  備忘錄(Memento):在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象以外保存這個狀態。這樣之後就可將該對象恢復到原來保存的狀態。

  迭代器(Iterator Pattern)模式

  遍歷:所謂遍歷,就是指把一個集合中的全部元素挨個訪問一遍(這裏的訪問,就是它的字面意思)。

  迭代器模式(Iterator),提供一種方法順序訪問一個聚合對象中的各個元素,而又不暴露該對象的內部表示。

  命令模式(Command Pattern)模式

  命令(Command)模式,將一個請求封裝爲一個對象,從而使你可用不一樣的請求對客戶進行參數化;對請求排隊或記錄請求日誌,以及支持可撤銷的操做。

  職責鏈(Chain Of Responsibility Pattern)模式

  職責鏈(Chain Of Responsibility)模式:使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係。將這個對象連成一條鏈,並沿着這條鏈傳遞該請求,知道有一個對象處理它爲止。

  這裏發出這個請求的客戶端並不知道這當中的哪個對象最終處理這個請求,這樣系統的更改能夠在不影響客戶端的狀況下動態的從新組織和分配責任。

  中介者(Mediator Pattern)模式

  中介者(Mediator)模式,用一箇中介對象來封裝一系列的對象交互。中介者使得各對象不須要顯式的互相引用,從而使得其耦合鬆散,並且能夠獨立的改變他們之間的交互。

  解釋器(Interpreter Pattern)模式

  解釋器(Interpreter)模式,給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。

  訪問者(Visitor Pattern)模式

  訪問者(Visitor)模式,表示一個做用於某對象結構中各個元素的操做。它使你能夠在不改變各元素的前提下定義做用於這些元素的新操做。

單例概述

       單例意即類在整個工程裏只能有一個實例。單例一般應用在以下場景中,類的構造是一個很是耗時的過程,而且,它沒有屢次構造的必要性。例如。你能夠打開一個數據庫鏈接,只在此鏈接上進行數據庫操做。

  那麼怎確保它在整個工程中只有一個實例呢?咱們能夠經過將構造函數的訪問權限設置爲private,並輔助其它手段來保證,同時將拷貝構造函數和賦值構造函數聲明爲delete的。

知識儲備

做用域

   在C++中,變量根據定義的位置不一樣具備不一樣的生命週期,具體分爲六種:語句做用域、類做用域、全局做用域、文件做用域、命名空間做用域、局部做用域(函數或者語句塊)。相應的變量也分爲局部變量、全局變量、局部靜態變量和全局靜態變量。

局部變量、全局變量、局部靜態變量、全局靜態變量

  生存週期和做用域:生存週期指的是變量從定義開始到銷燬經歷的時間範圍,而做用域指的是變量的可見代碼域。

       局部變量:局部變量具備局部做用域,例如函數的形參,定義在函數中的變量。從存儲空間上來看,局部變量是在棧上分配空間的。從生存週期來看,它僅存在與被定義時到離開局部做用域的那一刻。

       全局變量:全局變量具備全局做用域,意即,一個全局變量只能有一個定義,能夠有多個聲明,其它文件須要使用這個全局變量的話,須要使用extern進行聲明,它被定義於任何函數(包括main函數)以外。從存儲的角度來看,它被保存在了ELF的.data段或者.bss段(根據是否被初始化而定)。從生存週期來看,它存在於整個程序運行期間,直到程序退出。

        靜態局部變量:靜態局部變量就是在局部變量的前面加了static修飾符,它的做用域範圍和局部變量相同,生存週期從定義時起,到進程結束時由操做系統負責銷燬。從空間分配上來講它在ELF的.data段。

        靜態全局變量:靜態全局變量就是在全局變量的前面加了static修飾符,它具備文件做用域,所謂文件做用域即指這個變量僅在定義它的文件中生效,對其它文件不可見,就是說能夠在文件A和文件B中定義兩個同名的靜態全局變量。從空間分配的角度看,它在ELF文件的data段。

        那麼,類的數據成員怎麼分類呢?實際上,類的數據成員不適用於上述分類方式。普通數據成員就是類的實例的一部分,實例在,在數據成員在,實例不在,則數據成員亡。對靜態數據成員,它則是屬於類自己的,假設咱們有一個數據,須要多個對象共享,那麼可使用靜態數據成員。

單例-餓漢模式

  所謂餓漢模式即指不管該單例在工程中是否使用,都建立好這個單例。在C++11下餓漢模式的構建利用了靜態變量在main函數開始執行前即初始化的行爲。具體實現以下:

 

#ifndef SINGLETON_H_
#define SINGLETON_H_
#include <string>
#include <iostream>
class Singleton
{
private:
    static Singleton *m_SingleInstance; 
    std::string m_strInfo;
    Singleton(const Singleton &) = delete;    //copy Construct
    Singleton& operator=(const Singleton &)=delete;    //assign Construct 
    Singleton()
    {
        std::cout << "I am Constructed!"<< std::endl;
    }
    ~Singleton() = default;
public:
    void setInfo(const std::string strInfo)
    {
    m_strInfo = strInfo;
    }
    void getInfo(std::string &strInfo)
    {
    strInfo = m_strInfo;
    }
    static Singleton* getInstance();
};
#define SINGLETON Singleton::getInstance()
#endif

 

#include "Singleton.h"

Singleton* Singleton::m_SingleInstance = new Singleton();

Singleton* Singleton::getInstance()
{
    return m_SingleInstance;
}
#include "Singleton.h"

using namespace std;

int main(int argc,char *argv[])
{
    return(1);
}

 

       還有一個小小的MakeFile,第一次寫這個歡迎指正:

VPATH = ../
main:Singleton.o main.o
    g++ -g $^ -o main.out -std=c++11
main.o:main.cpp
    g++ -g -c $^ -o $@ -std=c++11
Singleton.o:Singleton.cpp
    g++ -g -c $^ -o $@ -std=c++11
.PHONY:clean
clean: 
    rm -r *.*

 

  能夠看到這個main函數裏沒有任何對單例的引用,使用GDB調試,在main函數入口處打上斷點,能夠看到在main函數尚未進入時就打印了Singleton的構造函數內的輸出信息,這意味着類的靜態數據成員是在main函數進入以前就被初始化了的,因此使用這種方式建立的單例沒有線程安全的隱患。

單例-----懶漢模式

  所謂懶漢模式,就是指咱們只有在須要的時候纔去實例化這個單例,這個叫作延後初始化,實現思路是採用局部靜態變量。採用局部靜態變量實現的靜態變量是線程安全的,這是C++11的規定。

  代碼示例:

#ifndef SINGLETONIDLER_H_
#define SINGLETONIDLER_H_
#include <iostream>
#include <string>

class SingletonIdler
{
public:
    std::string getInfo()
    {
    return m_strInfo;
    }
    void setInfo(const std::string &strInfo)
    {
    m_strInfo = strInfo;
    }
    static SingletonIdler& getInstance();
private:
    std::string m_strInfo;
    SingletonIdler() = default;
    ~SingletonIdler() = default;
    SingletonIdler(const SingletonIdler &) = delete;
    SingletonIdler & operator = (const SingletonIdler &) = delete;
};
#define SINGLETONIDLER SingletonIdler::getInstance()
#endif
SingletonIdler.h
#include "SingletonIdler.h"
SingletonIdler& SingletonIdler::getInstance()
{
    static SingletonIdler myInstance;
    return myInstance;
}
SingletonIdler.cpp
#include "SingletonIdler.h"

using namespace std;
int main(int argc,char *argv[])
{
    SINGLETONIDLER.setInfo("Hello World");
    cout << SINGLETONIDLER.getInfo() << endl;
    return (1);
}
main.cpp
相關文章
相關標籤/搜索