【Java】設計模式--建立型模式
建立型模式
目錄
☆多個方法工廠(沒有字串引數,呼叫不同方法作為傳遞引數方式 --> 工廠多個方法)
ConcurrentBuilder:每個套餐應該說明(實現)套餐的每個內容是什麼
測試:拿到選單A(mealA)後點餐,得到meal,不用知道是怎麼組成這個物件meal的
工廠模式
*組成:
- 產品規範:一個介面(抽象類)
- 產品:實現此介面(繼承抽象類)的類
- 工廠:根據“引數”返回不同實現(繼承)類的類
*流程:
使用者需要某個產品(實現類),使用對應“引數” --> 工廠 --> 工廠根據“引數”產生不同的物件
*好處:
只需要傳遞“引數”給工廠,就可以得到某種類,不需要自己去實現
*缺點:
如果要增加產品(實現類),也就是工廠要返回的物件種類變多,就要修改工廠,違反了開閉原則(對擴充套件開放/修改關閉)
普通的工廠(引數是字串)
//規範實現類的介面
interface Car{
public abstract void run();
public abstract void stop();
}
//實現類A(使用者得到的產品)
class Benz implements Car{
@Override
public void run() {
System.out.println("Benz的run()方法");
}
@Override
public void stop() {
System.out.println("Benz的stop()方法");
}
}
//實現類B(使用者得到的產品)
class BMW implements Car{
@Override
public void run() {
System.out.println("BMW的run()方法");
}
@Override
public void stop() {
System.out.println("BMW的stop()方法");
}
}
//工廠類
class Factory{
public Car getCarInstance(String car) {
Car c = null;
if("Benz".equals(car)) {
c = new Benz();
}else if("BMW".equals(car)) {
c = new BMW();
}
return c;
}
}
//測試 是否工廠能夠正確的生產產品(實現類)
public class FactoryPattern {
public static void main(String[] args) {
Factory f = new Factory();
Car c = f.getCarInstance("Benz");
c.run();
c.stop();
}
}
☆多個方法工廠(沒有字串引數,呼叫不同方法作為傳遞引數方式 --> 工廠多個方法)
*好處:
不用再傻乎乎的傳引數給工廠了,因為引數可能傳錯,呼叫方法的話編譯階段就可以知道錯誤了。
對於上面的普通工廠,改動工廠類即可
class FactoryB{
//多個方法工廠
public Car getBenz() {
return new Benz();
}
public Car getBMW() {
return new BMW();
}
}
public class FactoryPattern {
public static void main(String[] args) {
FactoryB factory = new FactoryB();
Car c = factory.getBMW();
c.run();
c.stop();
}
}
☆☆靜態工廠方法模式(最優解BEST)
將多個方法工廠裡面方法設定成靜態
*好處:
想了想,工廠好像除了呼叫方法返回物件之後就沒什麼用了,為什麼不用靜態方法呢,這樣就少建立了一個工廠例項咯(#^.^#)
class FactoryC{
//靜態工廠
public static Car getBenz() {
return new Benz();
}
public static Car getBMW() {
return new BMW();
}
}
public class FactoryPattern {
public static void main(String[] args) {
Car c = FactoryC.getBenz();
c.run();
c.stop();
}
}
*總而言之,靜態工廠方法模式是最優最優的,因為沒有工廠例項(節省空間了),而且不用傳字串引數,減少出錯
抽象工廠模式
為了解決工廠模式擴充業務(工廠產生新種類物件)時要修改工廠的問題,
使用抽象工廠模式,一旦需要增加新的種類的實現類,直接增加新的工廠,不需要修改之前的程式碼。
*組成:
- 產品規範:一個介面(抽象類)
- 產品:實現此介面(繼承抽象類)的類
- 工廠:一個工廠對應一個物件,直接返回這個工廠唯一的產品(物件)
- 抽象工廠:實現此介面的工廠才可以返回其工廠的產品給使用者,管理所有的工廠
*流程:
使用者傳遞工廠名 --> 抽象工廠根據工廠名找到工廠 --> 不同的工廠直接返回不同的物件(產品)
*好處:
一旦需要增加新的種類(新的產品),直接增加新的工廠就好,不用修改工廠程式碼。
//產品規範
interface FillColor{
public void Fill();
}
//具體產品A(實現類)
class Pink implements FillColor{
@Override
public void Fill() {
// TODO Auto-generated method stub
System.out.println("Filling Pink");
}
}
//具體產品B(實現類)
class Blue implements FillColor{
@Override
public void Fill() {
// TODO Auto-generated method stub
System.out.println("Filling Blue");
}
}
//工廠的規範,工廠呼叫這個方法就產出產品
abstract class AbstractFactory{
public abstract FillColor ProvideProduct();
}
//工廠A以及產出A產品
class FactoryPink extends AbstractFactory{
@Override
public FillColor ProvideProduct() {
// TODO Auto-generated method stub
return new Pink();
}
}
//工廠B以及產出B產品
class FactoryBlue extends AbstractFactory{
@Override
public FillColor ProvideProduct() {
// TODO Auto-generated method stub
return new Blue();
}
}
public class AbstractFactoryPattern {
public static void main(String[] args) {
AbstractFactory factory = new FactoryPink();
FillColor pink = factory.ProvideProduct();
pink.Fill();
}
}
單例模式
*好處:
- 某些類的建立比較頻繁(各種成員屬性),並且對於大型物件建立很麻煩,就可以使用單例
- 作為通訊的媒介,資料共享,可以在不建立直接關聯的條件下,讓多個不相關的執行緒或者多個程式之間進行通訊
*缺點:
- 沒有抽象層,擴充套件困難,需要擴充套件的話要修改原始碼
- 職責過重,既要負責工廠方法又要提供業務方法,違背單一職責原則
- 例項化的物件長時間不被利用可能被GC
static Instance原因
- 要被靜態方法getInstance()呼叫
- static變數儲存在方法區,每次都指向同一個變數
一般單例模式
懶漢式
public class Singleton {
//賦值為null,實現延遲載入
private static Singleton instance = null;
//私有構造方法 防止被例項化(直接new)
private Singleton() {
}
private static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
不用想,肯定有多執行緒問題,所以一般想到的就是改進①:synchronized關鍵字
private static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
這樣每次呼叫此方法時鎖住的是整個物件,效能下降,但是我們只需要在第一次建立物件時才鎖住物件。
改進②雙檢鎖:
private static Singleton getInstance() {
if(instance == null) {
synchronized(instance) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
好像解決了問題,但是instance = new Singleton()這一句不是原子操作,分為三步
- 1:給instance分配記憶體
- 2:呼叫Singleton建構函式初始化成員變數
- 3:將instance物件指向分配的記憶體空間(此時instance ≠ null)
由於JVM的指令重排序優化機制,所以最終執行時是1-3-2。
如果A程式執行完3就切換程式了,程式B執行時發現instance不為空,呼叫時發現還未初始化就報錯了
所以要禁止重排序,很容易就想到可以用volatile修飾instance達到效果(也就是寫操作A優先於讀操作B)。
但是特別注意在 Java 5 以前的版本使用了 volatile 的雙檢鎖還是有問題的。其原因是 Java 5 以前的 JMM (Java 記憶體模型)是存在缺陷的,即時將變數宣告成 volatile 也不能完全避免重排序,主要是 volatile 變數前後的程式碼仍然存在重排序問題。這個 volatile 遮蔽重排序的問題在 Java 5 中才得以修復,所以在這之後才可以放心使用 volatile。
---------------------
作者:一往無前-千夜
來源:CSDN
原文:https://blog.csdn.net/wolfking0608/article/details/69066773
版權宣告:本文為博主原創文章,轉載請附上博文連結!
改進③final:餓漢式,第一次載入類到記憶體就會被初始化
*優點:執行緒安全,呼叫效率高(沒有鎖)
*缺點:不能延時載入,空間換時間,浪費記憶體
public class Singleton{
//類載入時就初始化
private static final Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
沒有延遲載入的結果 --> 某些情況使用不了:譬如 Singleton 例項的建立是依賴引數或者配置檔案的,在 getInstance() 之前必須呼叫某個方法設定引數給它,那樣這種單例寫法就無法使用了
☆☆改進④靜態內部類實現:延遲載入
*靜態內部類:呼叫時(執行getInstance())才會載入,外部類載入了也不會載入
public class Singleton {
private Singleton() {
}
/* 此處使用一個內部類來維護單例 */
private static class SingletonFactory {
private static final Singleton instance = new Singleton();
}
/* 獲取例項 */
public static Singleton getInstance() {
return SingletonFactory.instance;
}
}
改進④:建立與獲取分離
靜態內部類的另外一種實現,將獲取單例與建立單例分成兩個函式
public class SingletonTest {
private static SingletonTest instance = null;
private SingletonTest() {
}
private static synchronized void syncInit() {
if (instance == null) {
instance = new SingletonTest();
}
}
public static SingletonTest getInstance() {
if (instance == null) {
syncInit();
}
return instance;
}
}
☆☆列舉型別(最佳實現單例)
*優點:簡單,不用像上面考慮過多多執行緒問題
*缺點:沒有使用延遲載入,在靜態程式碼塊初始化了
實現單例的核心是構造方法私有化,而在列舉型別中構造方法本身就是私有的,且列舉型別是執行緒安全的
public enum EnumSingleton{
INSTANCE;
}
直接通過EnumSingleton.INSTANCE就可以訪問單例,VERY EASY.
**列舉型別的實現:反編譯class檔案
P.S. jd-gui會直接反編譯成enum
package com.create;
public final class Color extends Enum
{
public static Color[] values()
{
return (Color[])$VALUES.clone();
}
public static Color valueOf(String s)
{
return (Color)Enum.valueOf(com/create/Color, s);
}
private Color(String s, int i)
{
super(s, i);
}
public static final Color INSTANCE;
private static final Color $VALUES[];
static
{
INSTANCE = new Color("INSTANCE", 0);
$VALUES = (new Color[] {
INSTANCE
});
}
}
可以看到,類似於餓漢式使用static 和 final修飾唯一物件INSTANCE
以及在靜態程式碼塊中初始化(Java類的載入和初始化過程都是執行緒安全的,所以INSTANCE也是確保了執行緒安全)
所以使用enum列舉實現單例模式不能延遲載入
建造者模式
相比於工廠模式,工廠模式是提供單個類的不同實現方式(飲料的實現:可樂/雪碧),而建造者模式是將這些不同的類再組合在一個類(飲料+食物)類似於套餐
*流程:
根據已有的套餐規範(Builder)已建立套餐選單A/B -->使用者拿著套餐選單A(ConcreteBuilder)
---> 找服務員Waiter(Director)根據此創造對應套餐Meal(Product)
*好處:
只需要拿到套餐選單(ConcurrentBuilder)給Waiter(Director),就可以得到組合的物件
Builder:套餐規範
//抽象建造者Builder
abstract class MealBuilder{
Meal meal = new Meal();
public abstract void buildFood();
public abstract void buildDrink();
public Meal getMeal() {
return meal;
}
}
ConcurrentBuilder:每個套餐應該說明(實現)套餐的每個內容是什麼
//具體建造者A ConcreteBuilder
class MealA extends MealBuilder{
@Override
public void buildFood() {
// TODO Auto-generated method stub
meal.setFood("漢堡");
}
@Override
public void buildDrink() {
// TODO Auto-generated method stub
meal.setDrink("可樂");
}
}
//具體建造者B ConcreteBuilder
class MealB extends MealBuilder{
@Override
public void buildFood() {
// TODO Auto-generated method stub
meal.setFood("蘭州拉麵");
}
@Override
public void buildDrink() {
// TODO Auto-generated method stub
}
}
Director:根據套餐選單,返回出對應套餐的食物
//Director
class Waiter {
private MealBuilder mealBuilder;
public Waiter(MealBuilder mealBuilder) {
this.mealBuilder = mealBuilder;
}
public Meal construct() {
mealBuilder.buildFood();
mealBuilder.buildDrink();
return mealBuilder.getMeal();
}
}
Product:最後得到的食物
//Product
class Meal{
private String food;
private String drink;
public String getFood() {
return food;
}
public void setFood(String food) {
this.food = food;
}
public String getDrink() {
return drink;
}
public void setDrink(String drink) {
this.drink = drink;
}
}
測試:拿到選單A(mealA)後點餐,得到meal,不用知道是怎麼組成這個物件meal的
public class Builder {
public static void main(String[] args) {
MealA mealA = new MealA();
Waiter w = new Waiter(mealA);
Meal m = w.construct();
System.out.println("食物:" + m.getFood() + " 飲料:" + m.getDrink());
}
}
原型模式
首先建立一個原型物件,通過對原型物件的複製,產生出更多同型別的物件(淺拷貝/深拷貝)
https://blog.csdn.net/TypantK/article/details/88708343 -- 3種淺拷貝/2種深拷貝
相關文章
- (Java)設計模式:建立型Java設計模式
- JAVA設計模式 4【建立型】理解建造者模式Java設計模式
- JavaScript設計模式之建立型設計模式JavaScript設計模式
- 設計模式-建立型-單例模式設計模式單例
- 建立型設計模式——抽象工廠模式設計模式抽象
- Java設計模式——單例模式(建立型模式)Java設計模式單例
- JAVA設計模式 3【建立型】理解工廠模式與抽象工廠模式Java設計模式抽象
- 聊一聊設計模式(二)-- 建立型設計模式設計模式
- 設計模式(一)建立型之單例模式設計模式單例
- 建立型設計模式對比總結 設計模式(八)設計模式
- Java內功心法,建立型設計模式包括哪些Java設計模式
- 物件導向-設計模式-建立型物件設計模式
- 大話 PHP 設計模式--建立型PHP設計模式
- 設計模式-建立型-工廠方法設計模式
- (Java)設計模式:結構型Java設計模式
- (Java)設計模式:行為型Java設計模式
- python--設計模式--4--建立型--工廠方法模式Python設計模式
- Java設計模式——模板設計模式Java設計模式
- Golang 常用的五種建立型設計模式Golang設計模式
- 【設計模式(四)】建立型模式--原型模式設計模式原型
- 設計模式-建立型模式學習設計模式
- Java設計模式-代理模式Java設計模式
- Java設計模式——命令模式Java設計模式
- Java設計模式—代理模式Java設計模式
- JAVA設計模式 2【建立型】原型模式的理解與使用、理解淺克隆和深克隆Java設計模式原型
- 初探Java設計模式1:建立型模式(工廠,單例等)Java設計模式單例
- 《設計模式》 - 3. 建立者模式( Builder )設計模式UI
- Java設計模式-工廠模式Java設計模式
- Java設計模式-單例模式Java設計模式單例
- Java設計模式【單例模式】Java設計模式單例
- Java設計模式-原型模式Java設計模式原型
- java設計模式-建造者模式Java設計模式
- Java設計模式---原型模式Java設計模式原型
- Java設計模式-橋接模式Java設計模式橋接
- Java設計模式之代理模式Java設計模式
- java設計模式 – 工廠模式Java設計模式
- Java設計模式之外觀模式Java設計模式
- 設計模式之----Java模板模式設計模式Java