【設計模式】建造者模式 Builder Pattern

来源:https://www.cnblogs.com/vaiyanzi/archive/2018/08/04/9417860.html
-Advertisement-
Play Games

前面學習了簡單工廠模式,工廠方法模式以及抽象工廠模式,這些都是創建類的對象所使用的一些常用的方法和套路, 那麼如果我們創建一個很複雜的對象可上面的三種方法都不太適合,那麼“專業的事交給專業人去做”,23設計模式總有一個模式是適合這種複雜對象的創建。比如現在的智能手機組成, 它包括一個屏幕,攝像頭,耳 ...


前面學習了簡單工廠模式工廠方法模式以及抽象工廠模式,這些都是創建類的對象所使用的一些常用的方法和套路, 那麼如果我們創建一個很複雜的對象可上面的三種方法都不太適合,那麼“專業的事交給專業人去做”,23設計模式總有一個模式是適合這種複雜對象的創建。比如現在的智能手機組成, 它包括一個屏幕,攝像頭,耳機介面,USB介面,CPU, RAM,主板等等, 但是每一個型號的手機的屏幕又不一樣,有的是劉海的,有的是全屏的,有的是全面屏的,CUP 也不一樣,有驍龍820 的,有 660的還有麒麟920 的等等,手機的組成圖如下:

image_thumb4

image_thumb6

 

那麼要創建一個這樣的複雜對象, 該怎麼創建呢? 那麼該建造者模式閃亮登場了。

一、建造者模式定義

建造者模式(Builder Pattern):將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。建造者模式是一種對象創建型模式。

二、建造者模式結構圖

 image_thumb18

1、AbstractBuilder(抽象建造者):

它為創建一個產品Product對象的各個部件指定抽象介面,在該介面中一般聲明兩類方法,一類方法是buildPartX(),它們用於創建複雜對象的各個部件;另一類方法是GetResult(),它們用於返回覆雜對象。AbstractBuilder既可以是抽象類,也可以是介面。

2、ConcreteBuilder(具體建造者):

它實現了AbstractBuilder介面,實現各個部件的具體構造和裝配方法,定義並明確它所創建的複雜對象,也可以提供一個方法返回創建好的複雜產品對象。

3、Product(產品角色):

它是被構建的複雜對象,包含多個組成部件,具體建造者創建該產品的內部表示並定義它的裝配過程。

4、Director(導演):

負責安排複雜對象的建造次序,導演與抽象建造者之間存在關聯關係,可以在其Construct()建造方法中調用建造者對象的部件構造與裝配方法,完成複雜對象的建造。客戶端一般只需要與指揮者進行交互,在客戶端確定具體建造者的類型,並實例化具體建造者對象(也可以通過配置文件和反射機制),然後通過指揮者類的構造函數將該對象傳入指揮者類中。

三、建造者模式典型代碼結構

public class Product
{
    public string PartA { get; set; }
    public string PartB { get; set; }
    public string PartC { get; set; }
}

public abstract class AbstractBuilder
{
    protected Product product = new Product();
    public abstract void BuildPartA();
    public abstract void BuildPartB();
    public abstract void BuildPartC();
    public  Product GetResult()
    {          
        return product;
    }
}

public class ConreteBuilder : AbstractBuilder
{
    public override void BuildPartA()
    {
        this.product.PartA = "PartA";
    }

    public override void BuildPartB()
    {
        this.product.PartB = "PartB";
    }

    public override void BuildPartC()
    {
        this.product.PartC = "PartC";
    }   
}

public class Director
{
    private AbstractBuilder builder;
    public Director(AbstractBuilder builder)
    {
        this.builder = builder;
    }

    public Product Construct()
    {
        builder.BuildPartA();
        builder.BuildPartB();
        builder.BuildPartC();
        return builder.GetResult();
    }
}

客戶端調用:

static void Main(string[] args)
{
    AbstractBuilder builder = new ConreteBuilder();
    Director director = new Director(builder);
    Product product = director.Construct();

    Console.WriteLine(product.PartA);
    Console.WriteLine(product.PartB);
    Console.WriteLine(product.PartC);

    Console.ReadKey();
}

輸出結果:

image_thumb12[4]

四、建造者模式實例

我們用本文開頭提出的手組成的例子來挑選幾個核心部件來構造幾個型號的手機,使用建造者模式。

1、X1型手機:CUP 驍龍 835, RAM 6GB ,屏幕 劉海全屏, 硬碟 64GB。

2、X2 型手機: CUP 麒麟 930, RAM 8G, 屏幕 全面屏, 硬碟 128GB。

3、X3型手機: CPU  驍龍 960 RAM 10G,屏幕 超清全面屏 256GB。

UML 圖如下:

image_thumb34

代碼:

public class Mobile
{
    public string CPU { get; set; }
    public string Screen { get; set; }
    public string RAM { get; set; }
    public string Disk { get; set; }
}
public abstract class MobileBuilder
{
    protected Mobile mobile = new Mobile();
    public abstract void BuildCPU();
    public abstract void BuildScreen();
    public abstract void BuildRAM();
    public abstract void BuildDisk();
    public Mobile GetMobile()
    {
        return this.mobile;
    }
}

public class X1Builder:MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X1]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X1]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X1]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X1]:Disk had been built.";
    }
}

public class X2Builder:MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X2]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X2]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X2]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X2]:Disk had been built.";
    }
}

public class X3Builder:MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X3]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X3]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X3]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X3]:Disk had been built.";
    }
}

public class MobileDirector
{
    private MobileBuilder builder;
    public MobileDirector(MobileBuilder builder)
    {
      this.builder=builder;
    }

    public Mobile GetMobile(){
     this.builder.BuildCPU();
     this.builder.BuildRAM();
     this.builder.BuildScreen();
     this.builder.BuildDisk();
     return this.builder.GetMobile();
    }
}

調用代碼如下:

static void Main(string[] args)
{
    MobileBuilder builder=new X1Builder();
    MobileDirector mobileDrector = new MobileDirector(builder);
    Mobile mobile = mobileDrector.GetMobile();
    Console.WriteLine(mobile.CPU);
    Console.WriteLine(mobile.Screen);
    Console.WriteLine(mobile.RAM);
    Console.WriteLine(mobile.Disk);
    Console.ReadKey();
}

輸出結果:

image_thumb20 

如果想生產X2型號的手機,只需要將具體建造者代碼修改一下就可以了,即將下麵的一行代碼:

MobileBuilder builder=new X1Builder();

改成:

MobileBuilder builder=new X2Builder();

輸出結果:

image_thumb22

也可以將具體建造者類配置在配置文件中,通過反射來創建建造者對象進而創建出新的型號的手機。

在配置文件中加入如下配置:

<appSettings>
    <add key="Builder" value="DesignPattern.Builder.MobileInstance.X3Builder"/>
</appSettings>

客戶端調用代碼如下:

static void Main(string[] args)
{
    MobileBuilder builder;
    var setting = ConfigurationSettings.AppSettings["Builder"];
    var obj = Type.GetType(setting);
    if (obj == null) return;
    builder = Activator.CreateInstance(obj) as MobileBuilder;

    if (builder == null) return;
    
    MobileDirector mobileDrector = new MobileDirector(builder);
    Mobile mobile = mobileDrector.GetMobile();
    Console.WriteLine(mobile.CPU);
    Console.WriteLine(mobile.Screen);
    Console.WriteLine(mobile.RAM);
    Console.WriteLine(mobile.Disk);
    Console.ReadKey();
}

輸出結果:

image_thumb24

 

五、建造者模式的優點

  1. 在建造者模式中,客戶端不必知道產品內部組成的細節,將產品本身與產品的創建過程解耦,使得相同的創建過程可以創建不同的產品對象。
  2. 每一個具體建造者都相對獨立,而與其他的具體建造者無關,因此可以很方便地替換具體建造者或增加新的具體建造者,用戶使用不同的具體建造者即可得到不同的產品對象。由於指揮者類針對抽象建造者編程,增加新的具體建造者無須修改原有類庫的代碼,系統擴展方便,符合“開閉原則(OCP)
  3. 可以更加精細地控制產品的創建過程。將複雜產品的創建步驟分解在不同的方法中,使得創建過程更加清晰,也更方便使用程式來控制創建過程

六、建造者模式的缺點

  1. 建造者模式所創建的產品一般具有較多的共同點,其組成部分相似,如果產品之間的差異性很大,例如很多組成部分都不相同,不適合使用建造者模式,因此其使用範圍受到一定的限制。
  2. 如果產品的內部變化複雜,可能會導致需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大,增加系統的理解難度和運行成本。

七、建造者模式的使用場景

  1. 需要生成的產品對象有複雜的內部結構,這些產品對象通常包含多個成員屬性。
  2. 需要生成的產品對象的屬性相互依賴,需要指定其生成順序。
  3. 對象的創建過程獨立於創建該對象的類。在建造者模式中通過引入了指揮者類,將創建過程封裝在指揮者類中,而不在建造者類和客戶類中。
  4. 隔離複雜對象的創建和使用,並使得相同的創建過程可以創建不同的產品。

八、擴展 

Director在建造者模式中扮演著重要的角色,Director類看似簡單但是作用卻非常大,它決定複雜對象各個部分的創建順序並且將構建對象的過程和具體建造者隔離,比如創建一個房子,首先肯定要做的事情是打地基,然後是磊牆,然後是封頂,最後是裝修,這個過程是有順序的且必須是這個順序,其它過程不無法完成房子的建造。

Director'就好像是拍電影導演一樣,導演要拍一部電影,需要拍攝若幹影像片段,最後由剪輯師做剪接拼接,導演最後會進行最終的剪輯排版合成一個長片-- 電影。那麼電影就充當了建造者模式中的複雜產品,拍攝的影像片段就是產片的各個部分,剪輯就是建造者模式的具體具體建造者,劇本是建造者模式的抽象建造者,導演就是Director。那麼在在這個過程中,編劇是不可以兼任導演? 剪輯師是不是也可以兼任導演呢?答案是肯定的。

1.抽象建造者幹了Director的活

那上面手機建造的實例,刪除掉Diretor, 將創建複雜對象的邏輯放到抽象建造者中,並且使子類方法不能重寫父類中的建造產品的方法,引用之前的配置代碼演變成如下:

public class Mobile
{
    public string CPU { get; set; }
    public string Screen { get; set; }
    public string RAM { get; set; }
    public string Disk { get; set; }
}
public abstract class MobileBuilder
{
    protected Mobile mobile = new Mobile();
    public abstract void BuildCPU();
    public abstract void BuildScreen();
    public abstract void BuildRAM();
    public abstract void BuildDisk();
    public static Mobile GetMobile(MobileBuilder builder)
    {
        builder.BuildCPU();
        builder.BuildScreen();
        builder.BuildRAM();
        builder.BuildDisk();
        return builder.mobile;
    }
}

public class X1Builder : MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X1]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X1]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X1]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X1]:Disk had been built.";
    }
}

public class X2Builder : MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X2]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X2]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X2]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X2]:Disk had been built.";
    }
}

public class X3Builder : MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X3]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X3]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X3]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X3]:Disk had been built.";
    }
}

調用代碼:

static void Main(string[] args)
{
    MobileBuilder builder;
    var setting = ConfigurationSettings.AppSettings["Builder"];
    var obj = Type.GetType(setting);
    if (obj == null) return;
    builder = Activator.CreateInstance(obj) as MobileBuilder;

    if (builder == null) return;

    Mobile mobile = builder.GetMobile();
    Console.WriteLine(mobile.CPU);
    Console.WriteLine(mobile.Screen);
    Console.WriteLine(mobile.RAM);
    Console.WriteLine(mobile.Disk);
    Console.ReadKey();
}

輸出結果:

image_thumb26

這種在抽象建造者中使用了一個靜態方法來創建產品的做法的好處是產品創建出來的一致性很好,創建產品流程被統一封裝,一般不會有差異,這種方式抽象類控制了產品建造的順序,並且所有的產品的創建順序都不能改變了(如造房子的流程),對於要求創建順序一致,並且產品部件的創建都路程一致的產品來說這是一個優點。

但是如果想創建出來的產品有差異,每一個產品的順序都不一樣那該怎麼辦呢?比如現在這種方法創建出來的順序是: CPU=》Screen=》RAM=》Disk, 那麼我要想X3型號的手機創建順序變成:CPU=》RAM=》Disk=》Screen。 這種方法就顯得不靈活了,沒有辦法做到個性化了。處理典型的將建造過程的控制權交給Director外,還可以不用Director來完成嗎?

2、具體建造者和抽象建造者都可以乾Director的活

這裡我們依然刪掉Director, 將抽象方法中的創建方法變成子類可以重寫的虛方法就可以了,然後在具體建造者中重寫抽象建造者的創建方法就可以了。

現在我們創建X1,X2,X3型號的手機的順序分別是這樣的:

X1: CPU=》Screen=》RAM=》Disk

X2:CPU=》RAM=》Disk=》Screen

X3:CPU=》Disk=》RAM=》Screen

代碼如下:

public class Mobile
{
    public string CPU { get; set; }
    public string Screen { get; set; }
    public string RAM { get; set; }
    public string Disk { get; set; }
}
public abstract class MobileBuilder
{
    protected Mobile mobile = new Mobile();
    public abstract void BuildCPU();
    public abstract void BuildScreen();
    public abstract void BuildRAM();
    public abstract void BuildDisk();
    public virtual Mobile GetMobile()
    {
        this.BuildCPU();
        this.BuildScreen();
        this.BuildRAM();
        this.BuildDisk();
        Console.WriteLine(mobile.CPU);
        Console.WriteLine(mobile.Screen);
        Console.WriteLine(mobile.RAM);
        Console.WriteLine(mobile.Disk);
       
        return this.mobile;
    }
}

public class X1Builder : MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X1]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X1]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X1]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X1]:Disk had been built.";
    }
}

public class X2Builder : MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X2]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X2]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X2]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X2]:Disk had been built.";
    }

    public override Mobile GetMobile()
    {
        BuildCPU();
        BuildRAM();
        BuildDisk();
        BuildScreen();
        Console.WriteLine(mobile.CPU);       
        Console.WriteLine(mobile.RAM);
        Console.WriteLine(mobile.Disk);
        Console.WriteLine(mobile.Screen);
        return this.mobile;
    }
}

public class X3Builder : MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X3]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X3]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X3]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X3]:Disk had been built.";
    }
    public override Mobile GetMobile()
    {
        BuildCPU();        
        BuildDisk();
        BuildRAM();
        BuildScreen();
        Console.WriteLine(mobile.CPU);
        Console.WriteLine(mobile.Disk);
        Console.WriteLine(mobile.RAM);       
        Console.WriteLine(mobile.Screen);
        return this.mobile;
    }
}

調用代碼:

static void Main(string[] args)
{
    MobileBuilder builder;
    var setting = ConfigurationSettings.AppSettings["Builder"];
    var obj = Type.GetType(setting);
    if (obj == null) return;
    builder = Activator.CreateInstance(obj) as MobileBuilder;

    if (builder == null) return;

    Mobile mobile = builder.GetMobile();

    Console.ReadKey();
}

輸出結果:

image_thumb28

3.  控制某些部件不用被創建

假如要造一個X4型號的手機,這個手機支持NFC,我們知道X1,X2,X3中都不支持NFC,那怎麼辦呢?,我們可以給抽象建造者類加一個方法,叫 HasNFC()並且返回bool值,並將其設置成預設值為false。 修改抽象建造者的GetMobile() 方法,只有當HasNFC()返回 true是才創建NFC模塊,並且在X4Builder的具體建造者類中重寫HasNFC()方法使其返回true就可以了。

代碼如下:

public class Mobile
{
    public string CPU { get; set; }
    public string Screen { get; set; }
    public string RAM { get; set; }
    public string Disk { get; set; }
}
public abstract class MobileBuilder
{
    protected Mobile mobile = new Mobile();
    public abstract void BuildCPU();
    public abstract void BuildScreen();
    public abstract void BuildRAM();
    public abstract void BuildDisk();
    public virtual void BuildNFC()
    {
        Console.WriteLine("NFC had been built.");
    }
    protected virtual bool HasNFC()
    {
        return false;
    }
    public virtual Mobile GetMobile()
    {
        this.BuildCPU();
        this.BuildScreen();
        this.BuildRAM();
        this.BuildDisk();
        if(HasNFC())
        {
            this.BuildNFC();
        }
        Console.WriteLine(mobile.CPU);
        Console.WriteLine(mobile.Screen);
        Console.WriteLine(mobile.RAM);
        Console.WriteLine(mobile.Disk);
       
        return this.mobile;
    }
}

public class X1Builder : MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X1]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X1]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X1]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X1]:Disk had been built.";
    }
}

public class X2Builder : MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X2]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X2]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X2]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X2]:Disk had been built.";
    }

    public override Mobile GetMobile()
    {
        BuildCPU();
        BuildRAM();
        BuildDisk();
        BuildScreen();
        Console.WriteLine(mobile.CPU);       
        Console.WriteLine(mobile.RAM);
        Console.WriteLine(mobile.Disk);
        Console.WriteLine(mobile.Screen);
        return this.mobile;
    }
}

public class X3Builder : MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X3]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X3]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X3]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X3]:Disk had been built.";
    }
    public override Mobile GetMobile()
    {
        BuildCPU();        
        BuildDisk();
        BuildRAM();
        BuildScreen();
        Console.WriteLine(mobile.CPU);
        Console.WriteLine(mobile.Disk);
        Console.WriteLine(mobile.RAM);       
        Console.WriteLine(mobile.Screen);
        return this.mobile;
    }
}
public class X4Builder : MobileBuilder
{
    public override void BuildCPU()
    {
        mobile.CPU = "[X4]:CPU had been built.";
    }

    public override void BuildScreen()
    {
        mobile.Screen = "[X4]:Screen had been built.";
    }

    public override void BuildRAM()
    {
        mobile.RAM = "[X4]:RAM had been built.";
    }

    public override void BuildDisk()
    {
        mobile.Disk = "[X4]:Disk had been built.";
    }
    
    protected override bool HasNFC()
    {
 	     return true;
    }
}

App.Config 配置:

<appSettings>
    <add key="Builder" value="DesignPattern.Builder.MobileInstance.X3Builder"/>
</appSettings>

客戶端代碼:

static void Main(string[] args)
{
    MobileBuilder builder;
    
    var setting = ConfigurationSettings.AppSettings["Builder"];
    var obj = Type.GetType(setting);
    if (obj == null) return;
    builder = Activator.CreateInstance(obj) as MobileBuilder;

    if (builder == null) return;

    Mobile mobile = builder.GetMobile();

    Console.ReadKey();
}

輸出結果:

image_thumb30

修改配置文件,使其造一臺X4 如下,調用代碼不變:

<appSettings>
    <add key="Builder" value="DesignPattern.Builder.MobileInstance.X4Builder"/>
</appSettings>

輸出結果:

image_thumb32

好了建造者模式就探討到這裡。


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

-Advertisement-
Play Games
更多相關文章
  • Require.js與Sea.js的區別 相同之處 和 都是模塊載入器,倡導模塊化開發理念,核心價值是讓 JavaScript 的模塊化開發變得簡單自然。 不同之處 兩者的主要區別如下: RequireJS 想成為瀏覽器端的模塊載入器,同時也想成為 Rhino / Node 等環境的模塊載入器。Se ...
  • 場景: 假如有一天,你的在寫一個前端項目,是關於一份點餐商家電話信息表,你啪塔啪塔地寫完了,突然間項目經理跑過來找你,要求你在每一個商家的電話號碼前都添加一個電話符號,來使得電話號碼更直觀和頁面更美觀。這個時候你就糾結了,這不是折磨人嗎?這不是要我在每個電話號碼前都添加一個<img>標簽?這要整到猴 ...
  • [TOC] 前後端如何通信 前段:客戶端 後端: 伺服器端 所謂的全棧,其實是你可以實現客戶端和伺服器端程式的編寫,而且可以實現倆端之間的通信 客戶端和伺服器端是如何通信的? 本地開發(當前項目可以在本地預覽) 部署到伺服器上,讓別人可以通過功能變數名稱或者外網訪問 購買一臺伺服器(阿裡雲獨立主機,虛擬服務 ...
  • 不知不覺接觸前端的時間已經過去半年了,越來越發覺對知識的學習不應該只停留在會用的層面,這在我學jQuery的一段時間後便有這樣的體會。 雖然jQuery只是一個JS的代碼庫,只要會一些JS的基本操作學習一兩天就能很快掌握jQuery的基本語法並熟練使用,但是如果不瞭解jQUery庫背後的實現原理,相 ...
  • 開發者的javascript造詣取決於對【動態】和【非同步】這兩個詞的理解水平。 這一期主要分析各種實際開發中各種複雜的 指向問題。 一. 嚴格模式 嚴格模式是 ES5 中添加的 的另一種運行模式,它可以禁止使用一些語法上不合理的部分,提高編譯和運行速度,但語法要求也更為嚴格,使用 標記開啟。 嚴格模 ...
  • 這幾天項目著急,同時也學到好多以前沒有接觸過的知識。oninput、onchange與onpropertychange事件的區別, 與input輸入框實時檢測 onchange事件只在鍵盤或者滑鼠操作改變對象屬性,value的值發生變化且失去焦點時觸發,用戶js改變value時無法觸發; onkey ...
  • 通常我們在寫程式的時候會碰到一個類只允許在整個系統中只存在一個實例(Instance) 的情況, 比如說我們想做一計數器,統計某些介面調用的次數,通常我們的資料庫連接也是只期望有一個實例。Windows系統的系統任務管理器也是始終只有一個,如果你打開了windows管理器,你再想打開一個那麼他還是同 ...
  • 大家好,這裡是「聊聊系統優化 」,併在下列地址同步更新 博客園:http://www.cnblogs.com/changsong/ 知乎專欄:https://zhuanlan.zhihu.com/youhua 全網私活,免費訂閱: http://www.zsihuo.com 在這裡我會從基於J2EE ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...