前言
昨天講了六大原則,今天就進入設計模式的正題了。
思維導圖
建立型設計模式,顧名思義就是與物件的建立相關。
單例模式
定義:保證一個類僅有一個例項,並提供用於一個訪問它的全域性訪問點。
5種寫法及其優缺點
(1) 餓漢模式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
複製程式碼
在類載入時就已經完成了初始化。
優點:1. 保障了執行緒同步的問題;2. 獲取物件的效率高。
缺點:1. 降低了類載入時速度;2. 如果一直不使用,會記憶體的浪費。
(2) 懶漢模式
- 執行緒不安全
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null) instance = new Singleton();
return instance;
}
}
複製程式碼
缺點:存線上程同步問題 2. 執行緒安全
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance == null) instance = new Singleton();
return instance;
}
}
複製程式碼
缺點:每一次都需要同步,存在一定的開銷問題。
懶漢模式相較於餓漢模式,不會存在不使用的問題。雖然不再在載入時消耗資源,但是例項化時同樣會有一定的時間開銷。
(3) 雙重檢查模式/DCL
public class Singleton {
private volatile static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null) {
synchronized (Singleton.class){
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
複製程式碼
使用volatile
關鍵詞是對正確性的一種保障。
相較於懶漢模式而言,這又是一種升級。因為不在將synchronized
套在了函式上,也就不會每次呼叫都對整個函式同步了,提高了資源的利用率。但是同樣存在失效的情況。
存在失效的原因(對使用volatile的解釋)
因為JVM的載入順序是一個無序狀態,他可能進行過指令優化的重排操作,那這種情況就是我們不可控制的,而volatile起著不被忽略的作用,保證了我們的instance不被指令重排。這也就是他的優化方法。
(4) 靜態內部類單例模式
public class Singleton {
private Singleton(){}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
private static class SingletonHolder {
private volatile static Singleton instance = new Singleton();
}
}
複製程式碼
這是最常用的方法,也是對DCL
的一種升級。
優缺點和前面都差不多,就不再複述了。
(5) 列舉單例模式
public enum Singleton {
INSTANCE;
private Singleton(){}
}
複製程式碼
這是一種一般不常用的方法,但是能夠保障執行緒同步。
簡單工廠模式
定義:又稱靜態工廠模式,是由一個工廠物件決定建立出那一種產品類的例項。
既然是一個工廠就舉一個工廠的例子。
一般來說的工廠模式都是這樣的: 客戶 --> 工廠 --> 產品
所以產生了三個類
/**
* 工廠類
*/
public class Factory {
public static Product product(String type){
Product product = null;
switch (type){
case "雞翅":
product = new ChickenWing();
break;
case "漢堡":
product = new Hamburger();
break;
}
return product;
}
}
/**
* 抽象產品類
*/
public abstract class Product {
public abstract void finish();
}
/**
* 具體產品類
*/
public class Hamburger extends Product {
@Override
public void finish() {
System.out.println("漢堡製作完成");
}
}
public class ChickenWing extends Product {
@Override
public void finish() {
System.out.println("雞翅製作完成");
}
}
複製程式碼
三個類已經完成了對應,就跟你在肯德基吃飯一樣,告訴他一個名字,他就開始生產。
優點:根據引數獲得例項,降低了耦合度。
缺點:不利於擴充套件功能,因為工廠從一開始就知道能載入的有什麼。
使用場景:(1) 工廠負責建立的物件較少;(2)客戶只需要知道想要什麼,不用關心怎麼生成的時候。
工廠方法模式
工廠模式中的四個角色:定義:定義一個用於建立物件的介面,讓子類決定例項化哪個類。工廠方法將類的例項化放到子類中實現。
- Product:產品抽象類
- Factory:工廠抽象類,返回Product物件
- ConcreteProduct:具體產品類
- ConcreteFactory:具體工廠類 其實就是對簡單工廠方法的一種升級,因為在簡單工廠方法中,只有產品這一層被抽象出來了。而工廠方法是把工廠也抽象出來。 所以以下程式碼是對簡單工廠方法的一種改進。
/**
* 抽象工廠類
*/
public abstract class Factory {
public abstract <T extends Product> T product(Class<T> clazz);
}
/**
* 具體工廠類
*/
public class KFC extends Factory {
@Override
public <T extends Product> T product(Class<T> clazz) {
Product product = null;
try{
product = (Product) Class.forName(clazz.getName()).newInstance();
}catch (Exception e){
e.printStackTrace();
}
return (T) product;
}
}
複製程式碼
使用反射機制,也就解決了簡單工廠中分支可能存在增加的問題了。
建造者模式
講一個複雜物件的構建與它的表示分離,使同樣的構建過程可以建立不同的表示。
四個角色:
- Director:導演類,將工作按順序完成。
- Builder:生成抽象類,生產產品的模版,具體操作由子類完成。
- ConcreteBuilder:生成具體類。
- Product:產品類。
跟簡單工廠不一樣,這一次的主角變成了肯德基裡服務的店員們了,他們負責完成漢堡製作的各項流程,而
Builder
只是說明要幹什麼事情。
/**
* 導演類
*/
public class Director {
Builder builder;
Director(Builder builder){
this.builder = builder;
}
public Hamburger create(String meat, String vegetable, String bread){
builder.cookBread(bread);
builder.cookMeat(meat);
builder.cookVegetable(vegetable);
return builder.finish();
}
}
/**
* Builder抽象類
*/
public abstract class Builder {
public abstract void cookMeat(String meat);
public abstract void cookBread(String bread);
public abstract void cookVegetable(String vegetable);
public abstract Hamburger finish();
}
/**
* Builder類
*/
public class ChickenHamburgerBuilder extends Builder{
private Hamburger hamburger = new Hamburger();
@Override
public void cookMeat(String meat) {
hamburger.setMeat(meat);
}
@Override
public void cookBread(String bread) {
hamburger.setBread(bread);
}
@Override
public void cookVegetable(String vegetable) {
hamburger.setVegetable(vegetable);
}
@Override
public Hamburger finish() {
return hamburger;
}
}
/**
* 產品類
*/
public class Hamburger {
private String vegetable;
private String meat;
private String bread;
// 以下省略Getter 和 Setter方法
}
複製程式碼
以上就是我的學習成果,如果有什麼我沒有思考到的地方或是文章記憶體在錯誤,歡迎與我分享。
相關文章推薦: