【設計模式】學習筆記(二)——建立型設計模式
目錄
建立型設計模式
- 建立型設計模式介紹
- 建立型設計模式分類
簡單工廠設計模式
抽象工廠設計模式
建立者模式
原型模式
建立型設計模式介紹
這些設計模式提供了一種在建立物件的同時隱藏建立邏輯的方式,而不是使用 new 運算子直接例項化物件。這使得程式在判斷針對某個給定例項需要建立哪些物件時更加靈活。
建立型設計模式分類
- 簡單工廠模式(SimpleFactoryPattern)
- 工廠模式(Factory Pattern)
- 抽象工廠模式(Abstract Factory Pattern)
- 單例模式(Singleton Pattern)
- 建造者模式(Builder Pattern)
- 原型模式(Prototype Pattern)
簡單工廠設計模式
簡單工廠設計模式介紹
說的是定義一個工廠類,根據不同的引數,建立並返回不同的類。其中這些類具有一個公共的父類或是一個介面。簡單工廠模式不屬於GoF四人組提出的23種設計模式,它是最簡單的工廠模式。非常適合我們從此處學習設計模式。
優點:解耦,程式碼更容易維護
缺點:違反了開閉原則
適用場景:
- 客戶如果只知道傳入工廠類的引數,對於如何建立物件的邏輯不關心時;
- 當工廠類負責建立的物件(具體產品)比較少時。
簡單工廠設計模式實現
簡單工廠設計模式中包含:
-
actory:工廠類,內部有是個精通的方法,根據引數選擇建立的物件
-
Product:抽象產品類,其作為具體產品的父類,負責定義產品的公共介面
-
ConcreteProduct:具體產品類,有多個,都是繼承與抽象產品類,工廠就是建立該類物件
程式碼示例
假設現在我們是一家快餐工廠,專門生產快餐
/**
* 抽象產品類:快餐
*/
abstract class FastFood {
abstract void show();
}
我們的產品有KFC和MDL
/** * 具體產品類 */ class KFC extends FastFood { @Override void show() { System.out.println("生產了KFC"); } } class MDL extends FastFood { @Override void show() { System.out.println("生產了MDL"); } }
快餐工廠可以根據使用者的需求來建立對應的品牌
/**
* 快餐工廠
*/
public class FastFoodFactory {
/***
* 用於實現不同型別快餐品牌的建立
* @param brand 型別
* @return 快餐品牌
*/
public static FastFood GetFastFood(String brand) {
switch (brand) {
case "KFC":
return new KFC();
case "Mac":
return new Mac();
default:
throw new IllegalArgumentException("沒有該品牌");
}
}
}
於是我們迎來了我們的第一位客人
/**
* 學生
*/
public class Student {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
System.out.print("您想吃什麼:");
String brand=input.next();
FastFood kfc = FastFoodFactory.GetFastFood(brand);
kfc.show();
}
}
單例設計模式
單例設計模式介紹
單例設計模式說的是採取一定方法保證在整個軟體系統中,對某個類只能存在一個物件例項,並且該類只提供一個取得其物件例項的方法(靜態方法)。這是設計模式中最簡單的一種,沒準無意中就使用到過。
適用場景:
-
非常耗資源的物件建立時(比如讀取大檔案)
-
資源的建立比較昂貴(比如資料庫的連線)
-
邏輯上只應該有一個(比如winform中某些視窗只應開啟一次)
單例模式的實現方式
單例模式主要分為:
1)餓漢模式
2)懶漢模式
標紅為推薦使用的方式
餓漢模式
餓漢模式是指在類的載入的時候就建立一個例項。餓漢模式又分兩種實現方式:
1)通過靜態常量實現
優點:寫法比較簡單,就是在類轉載的時候就完成例項化。避免了執行緒同步問題。
缺點:是在類轉載的時候就完成例項化,如果從頭到尾未使用過這個例項,則會照成記憶體的浪費。
結論:這種單例模式可用,可能造成資源浪費,所以一般在確定一定會用到這個類時採用。
程式碼示例
步驟如下:
1)構造器私有化(防止 new)
2)類的內部建立物件
3)向外暴露一個靜態的公共方法。getInstance
class Singleton{
//1.構造器私有化,外部不能new
private Singleton(){
}
//2.本類內部建立物件例項
private final static Singleton instance = new Singleton();
//3.提供一個公有的靜態方法,放回例項物件
public static Singleton getInstance(){
return instance;
}
}
2)通過靜態程式碼塊實現。
這種方式和上面的方式其實類似,只不過將類例項化的過程放在了靜態程式碼塊中,也就是類轉載的時候,就執行靜態程式碼塊中的程式碼,初始化類的例項。優缺點和上面一樣
結論:這種單例模式可用,但是會造成資源浪費
程式碼示例
1)構造器私有化(防止 new)
2)類的內部聲名靜態物件
3)在靜態程式碼塊中,建立單例物件
4)提供一個公有的靜態方法,放回例項物件
class Singleton{
private static Singleton instance;
private Singleton(){
}
static {// 在靜態程式碼塊中,建立單例物件
instance = new Singleton();
}
public static Singleton getInstance(){
return instance;
}
}
懶漢模式
懶漢模式是指在呼叫方法的時候進行例項化的工作。實現方式有六種:
1)執行緒不安全
優點:起到了懶載入的效果,但是隻能在單執行緒下使用。
缺點:如果在多執行緒下,一個執行緒進入了if(singletion == null)判斷語句塊,還未來得及往下執行,另一個執行緒也通過了這個判斷語句,這時便會產生多個例項。所以在多執行緒環境下不可使用這種方式
結論:在實際開發中,不要使用這種方式(多執行緒環境下,執行緒不安全)
程式碼示例
實現步驟
1)構造器私有化(防止 new)
2)類的內部聲名靜態物件
3)提供一個靜態的公有方法,當使用到該方法時,才去建立 instance
class Singleton{
private static Singleton instance;
private Singleton(){
}
//提供一個靜態的公有方法,當使用到該方法時,才去建立 instance
//即懶漢式
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
2)執行緒安全,同步方法
優點:解決了執行緒不安全問題
缺點:效率太低了,每個執行緒在想獲得類的例項時候,執行getInstance()方法都要進行同步。而其實這個方法只執行一次例項化程式碼就夠了,後面的想要獲得該類例項,直接return就行了。
結論:在實際開發中,不推薦使用這種方法(方法進行同步效率太低)
程式碼示例
實現步驟
1)構造器私有化(防止 new)
2)類的內部聲名靜態物件
3)
class Singleton{
private static Singleton instance;
private Singleton(){
}
//提供一個靜態的公有方法,加入同步處理的程式碼,解決執行緒安全問題
//即懶漢式
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
3)執行緒安全,同步程式碼塊
這種方法,本意是對上一種實現方法的改進,因為前面方法效率太低,改為同步產生例項化的程式碼塊。但這種同步方法並不能起到執行緒同步的作用。跟第一種實現方法情形一致,假如一個執行緒進入了if(singletion == null)判斷語句塊,還未來得及往下執行,另一個執行緒也通過了這個判斷語句,這時便會產生多個例項。
結論:在實際開發中,不能使用這種方式
程式碼示例
實現步驟
1)構造器私有化(防止 new)
2)類的內部聲名靜態物件
3)提供一個靜態的公有方法,在程式碼塊中加入同步處理的程式碼(synchronized)
class Singleton{
private static Singleton singleton;
private Singleton(){
}
//提供一個靜態的公有方法,在程式碼塊中加入同步處理的程式碼(synchronized)
//即懶漢式
public static Singleton getInstance(){
if(singleton == null){
synchronized(Singleton.class){
singleton = new Singleton();
}
}
return singleton;
}
}
4)雙重檢查
優點:執行緒安全,延遲載入,效率較高
缺點:無!!!
結論:在實際開發中,推薦使用這種單例設計模式
程式碼示例
實現步驟
1)構造器私有化(防止 new)
2)類的內部聲名靜態物件並用volatile(用來確保將變數的更新操作通知到其他執行緒)關鍵字修飾
3)提供一個靜態的公有方法,加入雙重檢查程式碼,解決執行緒安全問題,同時解決懶載入問題
class Singleton{
private static volatile Singleton instance;
private Singleton(){
}
//提供一個靜態的公有方法,加入雙重檢查程式碼,解決執行緒安全問題,同時解決懶載入問題
public static synchronized Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){//當第一個執行緒執行完物件例項化操作後
if(instance == null) {//後面的執行緒來到這裡時判斷
instance = new Singleton();
}
//else 到下面返回 instance
}
}
//而當再後面的執行緒來時在最上方的判斷語句中就直接來到這裡返回instance
return instance;
}
}
5)靜態內部類
優點:
- 執行緒安全(在類進行初始化時,別的執行緒是無法進來的。)
- 延遲載入(外部類的裝載不會導致靜態內部類的裝載)
- 效率高
缺點:無 !!!
結論:實際開發中,推薦使用這種單例設計模式
程式碼示例
實現步驟
1)構造器私有化(防止 new)
2)類的內部聲名靜態物件
3)寫一個靜態內部類,該類中有一個靜態屬性外部類的靜態常量
4)提供一個靜態的公有方法,直接返回靜態內部類常量屬性
class Singleton{
private static Singleton instance;
private Singleton(){
}
//寫一個靜態內部類,該類中有一個靜態屬性 Singleton
private static class SingletonInstance{
private static final Singleton INSTANCE = new Singleton();
}
//提供一個靜態的公有方法,直接返回 SingletonInstance.INSTANCE
public static synchronized Singleton getInstance(){
return SingletonInstance.INSTANCE;
}
}
6)列舉
這是藉助JDK1.5中新增的列舉來實現單例模式。
優點:保證了執行緒安全、防止了反序列化重新建立新的物件
缺點:無 !!!
結論:推薦使用
程式碼示例
//使用列舉,可以實現單例,推薦
enum Singleton{
INSTANCE;//屬性
public void sayOK(){
System.out.println("ok~");
}
}
餓漢與懶漢的區別
區別 | 餓漢 | 懶漢 |
---|---|---|
執行緒 | 安全 | 不安全(不過有解決方案) |
資源載入 | 佔據一定的記憶體相應的在呼叫時速度也會更快 | 第一次掉用時要初始化如果要做的工作比較多效能上會有些延遲 |