創建型設計模式總結
(轉載請注明來源 http://www.cnblogs.com/jerry19880126/)
創建型設計模式包括簡單工廠模式,普通工廠模式,抽象工廠模式,建造者模式,原型模式和最簡單的單例模式。
簡單工廠模式(Simple Factory)
從UML圖中可以看出,工廠是具體的,Product是抽象的,里面的方法Operation是virtual的,它的三個子類是具體的,子類中的Operation覆蓋父類的方法Operation,由多態知識可知,運行時將會調用子類的Operation,父類的Operation只是聲明的“接口”。
多態有三個條件:一是父類的virtual,二是子類的覆蓋(要求函數名、參數、返回值都要一模一樣),三是父類的指針或引用指向子類的對象。前兩個條件已經滿足,下面關鍵是討論第三個條件。
第三個條件的關鍵就是Factory了,Factory能根據客戶端的請求,生成不同的具體對象,這可以用flag來標識,假定flag = 1時,生成ConcreteProduct1對象;flag = 2時,生成ConcreteProduct2對象。因此,可以在Factory這樣寫CreateProduct()。
Product* CreateProduct(flag)
{
Switch(flag)
case 1: return new ConcreateProduct1(); break;
case 2: return new ConcreateProduct2(); break;
case 3:…
}
在客戶端只要用
Factory f; // 生成factory對象
f.getProduct(1).Operation();就可以調用ConcreteProduct1的方法了
f.getProduct(2).Operation();就可以調用ConcreteProudct2的方法了。
這樣說來,相信讀者已經明白,其實工廠方法就是根據客戶端不同的“要求”,產生不同的具體對象,再利用多態特性,調用相應具體對象的方法。
下面給出可執行的源代碼,與上述例子不盡相同,但思想是一樣的,運行結果如下圖所示。

1 #include <iostream> 2 using namespace std; 3 4 // 簡單工廠模式 5 6 // 抽象產品 7 class AbstractProduct 8 { 9 public: 10 virtual void getProductsName() = 0; 11 }; 12 13 // 具體產品1 14 class ConcreteProduct1: public AbstractProduct 15 { 16 public: 17 void getProductsName() 18 { 19 cout << "ConcreteProduct1" << endl; 20 } 21 }; 22 23 24 // 具體產品2 25 class ConcreteProduct2: public AbstractProduct 26 { 27 public: 28 void getProductsName() 29 { 30 cout << "ConcreteProduct2" << endl; 31 } 32 }; 33 34 35 // 具體的工廠(沒有抽象工廠) 36 class ConcreteFactory 37 { 38 public: 39 AbstractProduct* getProductObject(int flag) 40 { 41 if(flag == 1) 42 { 43 return new ConcreteProduct1(); 44 } 45 else if(flag == 2) 46 { 47 return new ConcreteProduct2(); 48 } 49 else 50 { 51 return 0; 52 } 53 } 54 }; 55 56 int main() 57 { 58 ConcreteFactory f; 59 AbstractProduct* p1 = f.getProductObject(1); 60 AbstractProduct* p2 = f.getProductObject(2); 61 p1->getProductsName(); 62 p2->getProductsName(); 63 delete p1; 64 delete p2; 65 return 0; 66 }
運行結果如下:
(普通)工廠模式(Factory)
與簡單工廠模式換湯不換葯,只是把工廠也“抽象”了,UML圖如下所示:
從上圖可以看出,只不過多出一塊抽象工廠而已。抽象工廠提供CreateProduct的虛接口,在ConcreteFactory中實現,為了便於說明問題,這里假定有兩個ConcreteFactory,一個是ConcreteFactory1,只生產ConcreteProduct1產品,另一個是ConcreteFactory2,只生產ConcreteProduct2產品。
以ConcreteFactory1為例,代碼框架如下:
class ConcreateFactory1: public Factory
{
Product* CreateProduct()
{
return new ConcreteProduct1();
}
};
在客戶端用 Factory *f = new ConcreteFactory1(); f->CreateProduct();就可以得到ConcreteProduct1的對象了。
下面給出可執行的源代碼,運行結果的截圖放在后面。

1 #include <iostream> 2 using namespace std; 3 4 //普通工廠模式 5 6 //抽象產品 7 class AbstractProduct 8 { 9 public: 10 virtual void getProductName() = 0; 11 }; 12 13 //具體產品1 14 class ConcreteProduct1: public AbstractProduct 15 { 16 public: 17 void getProductName() 18 { 19 cout << "ConcreteProduct1" << endl; 20 } 21 }; 22 23 //具體產品2 24 class ConcreteProduct2: public AbstractProduct 25 { 26 public: 27 void getProductName() 28 { 29 cout << "ConcreteProduct2" << endl; 30 } 31 }; 32 33 34 //抽象工廠 35 class AbstractFactory 36 { 37 public: 38 virtual AbstractProduct* getProduct() = 0; 39 }; 40 41 //具體工廠1 42 class ConcreteFactory1: public AbstractFactory 43 { 44 public: 45 AbstractProduct* getProduct() 46 { 47 return new ConcreteProduct1(); 48 } 49 }; 50 51 52 //具體工廠2 53 class ConcreteFactory2: public AbstractFactory 54 { 55 public: 56 AbstractProduct* getProduct() 57 { 58 return new ConcreteProduct2(); 59 } 60 }; 61 62 int main() 63 { 64 AbstractFactory* f1 = new ConcreteFactory1(); 65 AbstractFactory* f2 = new ConcreteFactory2(); 66 f1->getProduct()->getProductName(); 67 f2->getProduct()->getProductName(); 68 delete f1, f2; 69 return 0; 70 }
運行結果如下:
抽象工廠模式(Abstract Factory)
抽象工廠又是(普通)工廠模式的升級版,但本質是相同的。觀察UML圖,可以看到不同的地方在於多了一個抽象產品的類。
ConcreateFactory1只生產ProductA1和ProductB1,即下標帶“1”的產品。可以預見,在ConcreteFactory1中的兩個方法應該如下:
AbstractProductA* CreateProductA()
{
return new ProductA1();
}
AbstractProductB* CreateProductB()
{
return new ProductB1();
}
ConcreateFactory2中的兩個方法類似,只是將最后的下標換成2而已。
下面給出可以執行的源代碼,運行截圖放在代碼的后面。

1 #include <iostream> 2 3 using namespace std; 4 5 6 7 // 抽象工廠模式 8 9 10 11 // 抽象產品A 12 13 class AbstractProductA 14 15 { 16 17 public: 18 19 virtual void getProductName() = 0; 20 21 }; 22 23 24 25 // 具體產品A1 26 27 class ConcreteProductA1: public AbstractProductA 28 29 { 30 31 public: 32 33 void getProductName() 34 35 { 36 37 cout << "ConcreteProductA1" << endl; 38 39 } 40 41 }; 42 43 44 45 46 47 // 具體產品A2 48 49 class ConcreteProductA2: public AbstractProductA 50 51 { 52 53 public: 54 55 void getProductName() 56 57 { 58 59 cout << "ConcreteProductA2" << endl; 60 61 } 62 63 }; 64 65 66 67 68 69 // 抽象產品B 70 71 class AbstractProductB 72 73 { 74 75 public: 76 77 virtual void getProductName() = 0; 78 79 }; 80 81 82 83 // 具體產品B1 84 85 class ConcreteProductB1: public AbstractProductB 86 87 { 88 89 public: 90 91 void getProductName() 92 93 { 94 95 cout << "ConcreteProductB1" << endl; 96 97 } 98 99 }; 100 101 102 103 104 105 // 具體產品B2 106 107 class ConcreteProductB2: public AbstractProductB 108 109 { 110 111 public: 112 113 void getProductName() 114 115 { 116 117 cout << "ConcreteProductB2" << endl; 118 119 } 120 121 }; 122 123 124 125 126 127 // 抽象工廠 128 129 class AbstractFactory 130 131 { 132 133 public: 134 135 virtual AbstractProductA* getProductA() = 0; 136 137 virtual AbstractProductB* getProductB() = 0; 138 139 }; 140 141 142 143 144 145 // 具體工廠1 146 147 class ConcreteFactory1: public AbstractFactory 148 149 { 150 151 AbstractProductA* getProductA() 152 153 { 154 155 return new ConcreteProductA1; 156 157 } 158 159 AbstractProductB* getProductB() 160 161 { 162 163 return new ConcreteProductB1; 164 165 } 166 167 }; 168 169 170 171 // 具體工廠2 172 173 class ConcreteFactory2: public AbstractFactory 174 175 { 176 177 AbstractProductA* getProductA() 178 179 { 180 181 return new ConcreteProductA2; 182 183 } 184 185 186 187 AbstractProductB* getProductB() 188 189 { 190 191 return new ConcreteProductB2; 192 193 } 194 195 }; 196 197 198 199 200 201 int main() 202 203 { 204 205 AbstractFactory *f1 = new ConcreteFactory1(); 206 207 AbstractFactory *f2 = new ConcreteFactory2(); 208 209 f1->getProductA()->getProductName(); 210 211 f1->getProductB()->getProductName(); 212 213 f2->getProductA()->getProductName(); 214 215 f2->getProductB()->getProductName(); 216 217 delete f1, f2; 218 219 return 0; 220 221 }
運行結果如下:
“工廠系列”設計模式總結
下面總結一下“工廠系列”設計模式,簡單工廠模式只有一份抽象的產品,工廠是具體的;(普通)工廠模式的同樣也只有一份抽象的產品,但工廠有抽象的了;抽象工廠模式工廠當然是抽象的,但是它獨特的地方在於產品至少有兩份是抽象的。
建造者模式(Builder)
建造者模式包含一個抽象的Builder類,還有它的若干子類——ConcreteBuilder,不用理會UML圖上的Product,關鍵是看Director,Director里面的方法Construct()其實包含了Builder指針或引用的形參,由客戶端傳入某個ConcreateBuilder對象。Construct(Builder* builder)的方法大致如下:
Construct(Builder* builder)
{
Builder->BuildPartA();
Builder->BuildPartB();
…
}
由多態性可知,客戶端傳進來的ConcreteBuilder是誰,就調用誰的方法。
可執行的源代碼如下,運行結果在源代碼的后面。

1 #include <iostream> 2 3 using namespace std; 4 5 6 7 // 建造者模式 8 9 10 11 // 抽象建造者 12 13 class AbstractBuilder 14 15 { 16 17 public: 18 19 virtual void buildPart1() = 0; 20 21 virtual void buildPart2() = 0; 22 23 }; 24 25 26 27 28 29 //具體建造者1 30 31 class ConcreteBuilder1: public AbstractBuilder 32 33 { 34 35 public: 36 37 void buildPart1() 38 39 { 40 41 cout << "用A構造第一部分" << endl; 42 43 } 44 45 void buildPart2() 46 47 { 48 49 cout << "用B構造第二部分" << endl; 50 51 } 52 53 }; 54 55 56 57 58 59 //具體建造者2 60 61 class ConcreteBuilder2: public AbstractBuilder 62 63 { 64 65 public: 66 67 void buildPart1() 68 69 { 70 71 cout << "用X構造第一部分" << endl; 72 73 } 74 75 void buildPart2() 76 77 { 78 79 cout << "用Y構造第二部分" << endl; 80 81 } 82 83 }; 84 85 86 87 // 指揮者,注意其方法的參數是抽象建造者的指針 88 89 class Director 90 91 { 92 93 public: 94 95 void build(AbstractBuilder *builder) 96 97 { 98 99 builder->buildPart1(); 100 101 builder->buildPart2(); 102 103 } 104 105 }; 106 107 108 109 int main() 110 111 { 112 113 Director d; 114 115 d.build(new ConcreteBuilder1()); 116 117 d.build(new ConcreteBuilder2()); 118 119 return 0; 120 121 }
運行結果如下:
原型模式(Prototype)
聽起來挺玄乎,其實說穿了,就是深拷貝問題。當一個類中包含了指針,那么這個類一定要顯示規定三個東西:拷貝構造函數,重載”=”操作符以及析構函數。如果程序員使用編譯器默認產生的這三個函數,那么得到的對象是原來對象的“淺拷貝”,即只是簡單拷貝了指針的值(只復制了地址),沒有復制指針指向空間的內容。“淺拷貝”只會保留最近的修改結果,而且在析構時容易出現重復析構的錯誤。
“深拷貝”是相對淺拷貝的概念說的,深拷貝不是簡單的拷貝指針的值(地址),而是重新生成了一個新的空間,這個空間里存放的內容與傳入類內容是完全相同的。
請看下面“淺拷貝”的實例:

1 #include <iostream> 2 3 using namespace std; 4 5 6 7 // 原型模式,本質就是深拷貝 8 9 10 11 // 淺拷貝,這時可以觀察到p的地址是一樣的,析構的時候因為重復釋放同一地址空間,所以 12 13 // 會出錯。 14 15 class PrototypeWrong 16 17 { 18 19 private: 20 21 int a; 22 23 int *p; // 有一個指針 24 25 public: 26 27 PrototypeWrong() 28 29 { 30 31 a = 3; 32 33 p = new int(2); 34 35 } 36 37 void outputPointerAddress() 38 39 { 40 41 cout << p << endl; 42 43 } 44 45 46 47 ~PrototypeWrong() 48 49 { 50 51 delete p; 52 53 } 54 55 }; 56 57 58 59 int main() 60 61 { 62 63 // 這一部分是錯誤的原型模式的測試樣例 64 65 PrototypeWrong p1; 66 67 PrototypeWrong p2 = p1; 68 69 p1.outputPointerAddress(); 70 71 p2.outputPointerAddress(); 72 73 }
運行結果為:
可以看到指針值(地址)是相同的,所以是淺拷貝,程序沒有出現“請按任意鍵繼續”的提示,說明程序其實是卡死的,原因是重復析構指針p。
再看看“深拷貝”的實例:

1 #include <iostream> 2 3 using namespace std; 4 5 6 7 // 原型模式,本質就是深拷貝 8 9 10 11 // 深拷貝,正確的原型模式 12 13 class PrototypeRight 14 15 { 16 17 private: 18 19 int a; 20 21 int *p; // 有一個指針 22 23 public: 24 25 PrototypeRight() 26 27 { 28 29 a = 3; 30 31 p = new int(2); 32 33 } 34 35 36 37 // 不使用默認的拷貝構造函數! 38 39 PrototypeRight(const PrototypeRight& obj) 40 41 { 42 43 a = obj.a; 44 45 p = new int(*obj.p); 46 47 } 48 49 50 51 void outputPointerAddress() 52 53 { 54 55 cout << p << endl; 56 57 } 58 59 60 61 ~PrototypeRight() 62 63 { 64 65 delete p; 66 67 } 68 69 }; 70 71 72 73 int main() 74 75 { 76 77 // 這一部分是正確的原型模式的測試樣例 78 79 80 81 PrototypeRight p1; 82 83 PrototypeRight p2 = p1; 84 85 p1.outputPointerAddress(); 86 87 p2.outputPointerAddress(); 88 89 return 0; 90 91 92 93 }
運行結果如下:
可見指針值不同了,說明指向了不同的空間,而且成功顯示了“請按任意鍵繼續”的提示符,說明析構也是正常了。但這里的程序還不完整,按照C++的程序風格,還應該重載“=”運算符,這里的練習便留給讀者。
單例模式(Singleton)
顧名思義,就是保證某個類只有一個實例(對象),這個應用還是很廣的,假設騰迅想讓用戶每次只能用一個QQ登陸,就可以用到這個模式。
這個UML圖非常簡單,可以說也是所有設計模式中最簡單的了,所以在面試中,常常被面試官們問起。
要使一個類只能生成一個對象,就是要限制使用它的構造函數,即將構造函數定義為private或protected的,然后另辟一個公有方法Instance,在這個方法里檢查instance(instance是指向本類的一個指針或引用,這在C++語法中是可以的)是否為空指針,若為空指針,則說明是第一次生成對象,那么操作是允許的,instance = new Singleton(),若指針非空,說明在之前已經有一個對象(實例)了,單例模式不允許再次生成實例,因此直接返回之前生成的對象的地址。
將instance定義成static變量,就更符合“單例”模式了,因為static變量只有一份,它屬於這個類,不屬於某個對象。相應的Instance()方法應該也定義成static的,用Singleton::Instance()來調用。注意這里的static方法和變量是必須的,如果函數不是static,則要事先生成對象才能調用Instance,就破壞了“單例”的思想了,如果變量不是static,那么靜態函數Instance又不能引用非static變量。
下面給出可以執行的源程序:

1 #include <iostream> 2 3 using namespace std; 4 5 6 7 // 單例模式 8 9 class Singleton 10 11 { 12 13 private: 14 15 Singleton(){}; // 不允許直接構造其對象 16 17 static Singleton *instance; 18 19 20 21 public: 22 23 static Singleton* createInstance() 24 25 { 26 27 if(!instance) 28 29 { 30 31 // 對象第一次被創建,允許 32 33 cout << "創建新對象" << endl; 34 35 instance = new Singleton(); 36 37 } 38 39 else 40 41 { 42 43 // 請求再次創建對象,不允許 44 45 cout << "已經創建過對象了,返回原對象" << endl; 46 47 } 48 49 return instance; 50 51 } 52 53 void getAddress() 54 55 { 56 57 cout << "我的地址是 " << instance << endl; 58 59 } 60 61 }; 62 63 64 65 Singleton* Singleton::instance = 0; //在初始化的時候,不能在前面加static了 66 67 68 69 int main() 70 71 { 72 73 //Singleton s;//報錯:無法訪問 private 成員(在“Singleton”類中聲明) 74 75 Singleton *s1 = Singleton::createInstance(); 76 77 s1->getAddress(); 78 79 80 81 cout << endl << endl; 82 83 84 85 Singleton *s2 = Singleton::createInstance(); 86 87 s2->getAddress(); 88 89 return 0; 90 91 }
運行結果為: