設計模式——創建型設計模式

来源:https://www.cnblogs.com/buchizicai/archive/2022/08/16/16580716.html
-Advertisement-
Play Games

創建型模式是處理對象創建的設計模式,試圖根據實際情況使用合適的方式創建對象。基本的對象創建方式可能會導致設計上的問題,或增加設計的複雜度。創建型模式通過以某種方式控制對象的創建來解決問題。創建型模式由兩個主導思想構成。一是將系統使用的具體類封裝起來,二是隱藏這些具體類的實例創建和結合的方式。 ...


創建型設計模式

爭對對象/類創建時的優化

工廠方法模式(瞭解)

通過定義頂層抽象工廠類,通過繼承的方式,針對於每一個產品都提供一個工廠類用於創建。

情況:只適用於簡單對象,當我們需要生產許多個產品族的時候,這種模式就有點乏力了

創建對象不再使用傳統的new,而是創建一個工廠類,作為all實體類創建對象的一個封裝類。(避免了更改類名、構造方法時,需要修改大量的代碼)

簡單工廠模式:(不靈活,不建議)

不符合開閉原則。如果輸入沒有提前寫好的水果則就需要再添加每個類里的代碼

//水果抽象類
public abstract class Fruit {   
    private final String name;
    
    public Fruit(String name){
        this.name = name;
    }

    @Override
    public String toString() {
        return name+"@"+hashCode();   //列印一下當前水果名稱,還有對象的hashCode
    }
}

//水果實體類
public class Apple extends Fruit{   //蘋果,繼承自水果

    public Apple() {
        super("蘋果");
    }
}
public class Orange extends Fruit{  //橘子,也是繼承自水果
    public Orange() {
        super("橘子");
    }
}

//水果工廠
public class FruitFactory {
    /**
     * 這裡就直接來一個靜態方法根據指定類型進行創建
     * @param type 水果類型
     * @return 對應的水果對象
     */
    public static Fruit getFruit(String type) {
        switch (type) {
            case "蘋果":
                return new Apple();
           	case "橘子":
                return new Orange();
            default:
                return null;
        }
    }
}

//主方法
public class Main {
    public static void main(String[] args) {
        Fruit fruit = FruitFactory.getFruit("橘子");   //直接問工廠要,而不是我們自己去創建
        System.out.println(fruit);
    }
}

工廠方法模式:通過範型靈活實現

如果新增了水果類型,直接創建一個新的工廠類就行,不需要修改之前已經編寫好的內容。

缺點:一種水果就有一種新的工廠類,太多工廠類了

//水果抽象類
public abstract class Fruit {   
    private final String name;
    
    public Fruit(String name){
        this.name = name;
    }

    @Override
    public String toString() {
        return name+"@"+hashCode();   //列印一下當前水果名稱,還有對象的hashCode
    }
}

//水果工廠
public abstract class FruitFactory<T extends Fruit> {   //將水果工廠抽象為抽象類,添加泛型T由子類指定水果類型
    public abstract T getFruit();  //不同的水果工廠,通過此方法生產不同的水果
}

//Apple工廠
public class AppleFactory extends FruitFactory<Apple> {  //蘋果工廠,直接返回Apple,一步到位
    @Override
    public Apple getFruit() {
        return new Apple();
    }
}

//主方法
public class Main {
    public static void main(String[] args) {
        test(new AppleFactory()::getFruit);   //比如我們現在要吃一個蘋果,那麼就直接通過蘋果工廠來獲取蘋果
    }

    //此方法模擬吃掉一個水果
    private static void test(Supplier<Fruit> supplier){
        System.out.println(supplier.get()+" 被吃掉了,真好吃。");
    }
}

抽象工廠模式

情況:適用於有一系列產品的公司。

缺點:容易違背開閉原則。一旦增加了一種產品,此時就必須去修改抽象工廠的介面,這樣就涉及到抽象工廠類的以及所有子類的改變

舉例:

image

實際上這些產品都是成族出現的,比如小米的產品線上有小米12,小米平板等,華為的產品線上也有華為手機、華為平板,但是如果按照我們之前工廠方法模式來進行設計,那就需要單獨設計9個工廠來生產上面這些產品,顯然這樣就比較浪費時間的。

我們就可以使用抽象工廠模式,我們可以將多個產品,都放在一個工廠中進行生成,按不同的產品族進行劃分,比如小米,那麼我就可以安排一個小米工廠,而這個工廠裡面就可以生產整條產品線上的內容,包括小米手機、小米平板、小米路由等。

//工廠抽象類
public abstract class AbstractFactory {
    public abstract Phone getPhone();
    public abstract Table getTable();
    public abstract Router getRouter();
}

//工廠實現類
public class AbstractFactoryImpl extends AbstractFactory{
    @Override
    public Phone getPhone() {
        return new ProductPhone();
    }

    @Override
    public Table getTable() {
        return new ProductTable();
    }

    @Override
    public Router getRouter() {
        return new ProductRouter();
    }
}

//產品抽象類
public abstract class AbRouter{
    public abstract Router getRouter();
}
...
    
//產品實體類
public class Router extends AbRouter{
    
    @Override
    public Router getRouter(){
        return new Router();
    }
}

建造者模式

當構造對象時參數較多,可以通過建造者模式使用鏈式方法創建對象,保證參數填寫正確。

可以去看看StringBuilder的源碼,有很多的框架都為我們提供了形如XXXBuilder的類型,我們一般也是使用這些類來創建我們需要的對象。

建造者模式創建對象其實和StringBuilder一樣:實際上我們是通過建造者來不斷配置參數或是內容,當我們配置完所有內容後,最後再進行對象的構建。

public static void main(String[] args) {
    StringBuilder builder = new StringBuilder();   //創建一個StringBuilder來逐步構建一個字元串
    builder.append(666);   //拼接一個數字
    builder.append("老鐵");   //拼接一個字元串
   	builder.insert(2, '?');  //在第三個位置插入一個字元
    System.out.println(builder.toString());   //差不多成形了,最後轉換為字元串
}

舉例:

//實體類的編寫
public class Student {
	int id;
    int age;
    int grade;
    String name;
    String college;
    String profession;
    List<String> awards;

    //一律使用建造者來創建,不對外直接開放
    private Student(int id, int age, int grade, String name, String college, String profession, List<String> awards) {
        this.id = id;
        this.age = age;
        this.grade = grade;
        this.name = name;
        this.college = college;
        this.profession = profession;
        this.awards = awards;
    }

    public static StudentBuilder builder(){   //通過builder方法直接獲取建造者
        return new StudentBuilder();
    }

    public static class StudentBuilder{   //這裡就直接創建一個內部類
        //Builder也需要將所有的參數都進行暫時保存,所以Student怎麼定義的這裡就怎麼定義
        int id;
        int age;
        int grade;
        String name;
        String college;
        String profession;
        List<String> awards;

        public StudentBuilder id(int id){    //直接調用建造者對應的方法,為對應的屬性賦值
            this.id = id;
            return this;   //為了支持鏈式調用,這裡直接返回建造者本身,下同
        }

        public StudentBuilder age(int age){
            this.age = age;
            return this;
        }
      
      	...

        public StudentBuilder awards(String... awards){
            this.awards = Arrays.asList(awards);
            return this;
        }
        
        public Student build(){    //最後我們只需要調用建造者提供的build方法即可根據我們的配置返回一個對象
            return new Student(id, age, grade, name, college, profession, awards);
        }
    }
}
//主方法
public static void main(String[] args) {
    Student student = Student.builder()   //獲取建造者
            .id(1)    //逐步配置各個參數
            .age(18)
            .grade(3)
            .name("小明")
            .awards("ICPC-ACM 區域賽 金牌", "LPL 2022春季賽 冠軍")
            .build();   //最後直接建造我們想要的對象
}

單例模式

單例模式:在電腦進程中,同一個類始終只會有一個對象來進行操作。

多例模式:在電腦進程中,對一個實體類創建一次對象就是對當個對象操作,若是創建多個對象則是分別對對應的對象操作。

單例模式的三種寫法:

  1. 餓漢式單例(不建議)

    在最開始就創建了對象(太饑渴了,一開始就需要對象)

    public class Singleton {
        private final static Singleton INSTANCE = new Singleton();   //用於引用全局唯一的單例對象,在一開始就創建好
        
        private Singleton() {}   //禁用了構造方法Singleton()來創建對象。不允許隨便new,需要對象直接找getInstance
        
        public static Singleton getInstance(){   //獲取全局唯一的單例對象
            return INSTANCE;
        }
    }
    
  2. 加鎖的懶漢式單例(不建議,沒有第三種方法好)

    懶漢:在要用的時候才創建對象。但又得防多線程就上了鎖

    public class Singleton {
        private static volatile Singleton INSTANCE;   //在一開始先不進行對象創建。volatile關鍵字是多線程的時候,這個變數更改了,別的線程可以立馬檢測到
    
        private Singleton() {}   //禁用了構造方法Singleton()來創建對象。不允許隨便new,需要對象直接找getInstance
    
        public static Singleton getInstance(){
        if(INSTANCE == null) {	//這層判斷是便於第一次外訪問時不用在走鎖
            synchronized (Singleton.class) {	//加鎖是為了防止多線程創建了多個對象
                if(INSTANCE == null) INSTANCE = new Singleton();  //由於加了鎖,所以當一個進程進來創建了對象,其他線程需要再判斷一次有沒有人已經創建了這個類對象,有就不創建了。內層還要進行一次檢查,雙重檢查鎖定
            }
        }
        return INSTANCE;
    }
    }
    
  3. 靜態內部類的半懶、半餓式單例(建議)

    靜態內部類該開始不會載入,需要的時候才會載入,由於這個類一載入就會創建對象。

    所以實現了懶漢的資源不濫用,餓漢的防止多線程

    public class Singleton {
        private Singleton() {}//禁用了構造方法Singleton()來創建對象。不允許隨便new,需要對象直接找getInstance
    
        private static class Holder {   //由靜態內部類持有單例對象,但是根據類載入特性,我們僅使用Singleton類時,不會對靜態內部類進行初始化。一旦類初始化之後值將不會改變,有點餓漢式的味道。
            private final static Singleton INSTANCE = new Singleton();
        }
    
        public static Singleton getInstance(){   //只有真正使用內部類時,才會進行類初始化
            return Holder.INSTANCE;   //直接獲取內部類中的
        }
    }
    

原型模式

定義:用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。(說白了就是複製)

  • 淺拷貝:①對於類中基本數據類型,會直接複製值給拷貝對象;②對於引用類型(對象類型),只會複製對象的地址,而實際上指向的還是原來的那個對象,拷貝個基莫。
public static void main(String[] args) {
    int a = 10;
    int b = a;  //基本類型淺拷貝
    System.out.println(a == b);	//true

    Object o = new Object();
    Object k = o;    //引用類型淺拷貝,拷貝的僅僅是對上面對象的引用
    System.out.println(o == k);	//true
}
  • 深拷貝:無論是基本類型還是引用類型,深拷貝會將引用類型的所有內容,全部拷貝為一個新的對象,包括對象內部的所有成員變數,也會進行拷貝。

使用Cloneable介面提供的拷貝機制,來實現原型模式:操作完會發現Object的clone預設還是淺複製

protected class Student implements Cloneable{   //註意需要實現Cloneable介面
    ...
    //Cloneable中的方法,下麵代碼複製Object的clone源碼    
    @Override
    public Object clone() throws CloneNotSupportedException {   //提升clone方法的訪問許可權
        return super.clone();
    }
}

//主方法
public static void main(String[] args) throws CloneNotSupportedException {
    Student student0 = new Student();
    Student student1 = (Student) student0.clone();
    System.out.println(student0);
    System.out.println(student1);
    //兩個結果不同,就是地址不同
    
    Student student0 = new Student("小明");
    Student student1 = (Student) student0.clone();
    System.out.println(student0.getName() == student1.getName());
    //true 
}

深拷貝:在student實現介面Cloneable後重寫clone方法

@Override
public Object clone() throws CloneNotSupportedException {   //這裡我們改進一下,針對成員變數也進行拷貝
    Student student = (Student) super.clone();
    student.name = new String(name);
    return student;   //成員拷貝完成後,再返回
}


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • display , visibility 僅會改變元素顯示,不會改變元素種類。可以配合 js 使用使元素可以動態的顯示隱藏。 可以使用 display 或 visibility 實現html元素的隱藏功能,但是這兩種方式有不一樣的地方。display 中的 none 值隱藏,元素後不會在占據頁面空間 ...
  • 高階組件(HOC) 高階組件(Heigher Order Component)也被稱之為高階函數,容器組件,高階組件是類組件編程中的一種重要代碼邏輯復用技巧。 高階組件的語法 接收一個React組件作為入參,經過修飾,最後返回一個新的React組件,所以這個入參的React組件被稱之為“UI組件”; ...
  • 1 HTML定義 HTML(英文Hyper Text Markup Language的縮寫)中文譯為“超文本標簽語言”,主要是通過HTML標簽對網頁中的文本、圖片、聲音等內容進行描述。 <strong> 加粗字體 </strong> 2 HTML的骨架格式 <HTML> <head> <title> ...
  • 在React中上下文是一種通信方案。 上下文的特點 在組件樹中,是一種自上而下的單向數據流通信方案,數據只能從父組件註入,在子組件中訪問。 組件關係只要滿足“父組件-後代組件”這種關係時,都可以使用上下文通信。 在父組件中provide提供數據,在後代組件中註入並使用,這種通信不具有響應式,有點像v ...
  • 使用three.js(webgl)搭建智慧樓宇、設備檢測、數字孿生、物聯網3D、物業3D監控、物業基礎設施可視化運維、3D定位、三維室內定位、3d建築,3d消防,消防演習模擬,3d庫房,webGL,threejs,3d機房,bim管理系統 ...
  • HTTP 起源 HTTP 是由蒂姆·伯納斯-李(TimBerners—Lee)於1989年在歐洲核子研究組織(CERN)所發起 其中最著名的是 1999 年 6 月公佈的 RFC 2616,定義了 HTTP 協議中現今廣泛使用的一個版本——HTTP 1.1 HTTP 是什麼 全稱:超文本傳輸協議(H ...
  • 本文將探討一下,在多行文本情形下的一些有意思的文字動效。 多行文本,相對於單行文本,場景會複雜一些,但是在實際業務中,多行文本也是非常之多的,但是其效果處理比起單行文本會更困難。 單行與多行文本的漸隱 首先,我們來看這樣一個例子,我們要實現這樣一個單行文本的漸隱: 使用 mask,可以輕鬆實現這樣的 ...
  • 結構型模式所描述的是如何將類和對象結合在一起來形成一個更大的結構,它描述兩種不同的事物:類和對象,根據這一點,可分為類結構型和對象結構型模式。類結構型模式關心類的組合,由多個類可以組合成一個更大的系統,在類結構型模式中一般只存在繼承關係和實現關係;對象結構型模式關心類與對象的組合,通過關聯關係使得在... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...