【設計模式】- 創建者模式

單例模型

餓漢式

靜態方法創建對象

public class Singleton {// 私有構造方法private Singleton(){}private static Singleton instance = new Singleton();// 提供一個外界獲取的方法public static Singleton getInstance(){return instance;}
}

靜態代碼塊創建對象

public class Singleton {private Singleton(){}private static Singleton instance;static {instance = new Singleton();}public static Singleton getInstance(){return instance;}
}

懶漢式

synchronized關鍵字

public class Singleton {private Singleton(){}private static Singleton instance;public static Singleton getInstance(){if(instance == null){instance = new Singleton();}return instance;}
}

問題】上邊的代碼是存在線程不安全的情況的,當線程1進來,判讀instance==null成立,準備創建對象;但是線程1還沒創建對象完畢時,線程2來了,線程2也判斷成立,也去創建對象,此時就會創建兩個不同的對象。
解決】:給getInstance方法上添加synchronized關鍵字

public class Singleton {private Singleton(){}private static Singleton instance;public static synchronized Singleton getInstance(){if(instance == null){instance = new Singleton(); // 下邊簡稱:“寫操作”}return instance; // 下邊簡稱:“讀操作”}
}

雙重檢查鎖

問題】:對于getInstance()方法,其實大部分的操作都是讀操作,讀操作是線程安全的,如果直接給getInstance方法上加鎖,其實會造成大量的線程等待。
解決】:調整加鎖的時機,雙重檢查鎖

public class Singleton {private Singleton(){}private static volatile Singleton instance; // volatile:保證指令的有序性和可見性public static Singleton getInstance(){// 第一次判斷,如果instance的值不為null,不需要搶占鎖,直接返回對象if(instance == null){synchronized (Singleton.class){// 第二次判斷if(instance == null){instance = new Singleton();}}}return instance;}
}

靜態內部類

public class Singleton {private Singleton(){}// 定義一個靜態內部類private static class SingletonHolder{// 在內部類中聲明并初始化外部類的對象private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance(){return SingletonHolder.INSTANCE;}
}

JVM加載外部類的過程是不會加載靜態內部類的,只有內部類的屬性、方法被調用時才會加載并初始化靜態屬性。靜態屬性被static修飾,所以只會被實例化一次。

枚舉類

枚舉類也是線程安全的,只會被加載一次,也是所有單例實現中唯一一種不會被破壞的單例模式

public enum Singleton {INSTANCE
}

枚舉方式是屬于餓漢式的方式,在不考慮浪費內存空間的情況下,首選枚舉方式

存在的問題

問題】:破壞單例模式(讓單例模式可以創建多個對象,枚舉方式除外)

通過序列化和反序列化破壞單例模式

public class Client {public static void main(String[] args) throws Exception {writeObject2File();readObjectFromFile(); // Singleton@27bc2616readObjectFromFile(); // Singleton@3941a79c}// 從文件中讀取對象private static void readObjectFromFile() throws Exception {// 1. 創建輸入流對象ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\Desktop\\a.txt"));// 2. 讀取對象Singleton instance = (Singleton) ois.readObject();System.out.println(instance);// 3. 釋放資源ois.close();}// 從文件中寫對象public static void writeObject2File() throws Exception {// 1. 獲取Singleton對象Singleton instance = Singleton.getInstance();// 2. 將對象寫入文件ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\Desktop\\a.txt"));oos.writeObject(instance);// 3. 釋放資源oos.close();}
}

上面的代碼生成的兩個對象不是同一個對象,破壞了單例模式

通過反射破壞單例模式

public class Client {public static void main(String[] args) throws Exception {// 1. 獲取Singleton的字節碼對象Class clazz = Singleton.class;// 2. 獲取無參構造方法對象Constructor cons = clazz.getDeclaredConstructor();// 3. 取消訪問檢查(暴力反射)cons.setAccessible(true);// 4. 創建對象Singleton s1 = (Singleton) cons.newInstance();Singleton s2 = (Singleton) cons.newInstance();System.out.println(s1 == s2); // false - 破壞單例模式}
}

上邊代碼返回的是false,說明s1和s2不是同一個對象,破壞了單例模式

問題解決

序列化、反序列化破壞單例模式解決方法

在Singleton類種添加readResolve()方法,在反序列化時被反射調用。如果定義了這個方法,就返回這個方法的值,如果沒有定義,則返回new出來的對象。

public class Singleton implements Serializable {private Singleton(){}// 定義一個靜態內部類private static class SingletonHolder{// 在內部類中聲明并初始化外部類的對象private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance(){return SingletonHolder.INSTANCE;}// 當進行反序列化時,自動調用該方法,將該方法的返回值直接返回public Object readResolve(){return SingletonHolder.INSTANCE;}
}

反射方式破壞單例模式解決方法

其實反射破壞的原理是:通過反射獲取Singleton的私有構造方法,然后通過這個私有的構造方法去創建對象。
因此我們只需要在構造方法里添加一個判斷即可

public class Singleton implements Serializable {private static boolean flag = false;private Singleton(){synchronized (Singleton.class){if(flag) {throw new RuntimeException("不能創建多個對象");}flag = true;}}// 定義一個靜態內部類private static class SingletonHolder{// 在內部類中聲明并初始化外部類的對象private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance(){return SingletonHolder.INSTANCE;}
}

JDK源碼解析 - Runtime類

餓漢式:
在這里插入圖片描述

工廠模式

引例:點咖啡

現在有美式咖啡、拿鐵咖啡,顧客可以選擇咖啡的種類,咖啡都需要進行加糖加奶。

原本的寫法:

咖啡類:

public abstract class Coffee {public abstract String getName();// 加糖public void addsugar() {System.out.println("加糖");}// 加奶public void addmilk() {System.out.println("加奶");}
}

美式咖啡:

public class AmericanCoffee extends Coffee {@Overridepublic String getName() {return "美式咖啡";}
}

拿鐵咖啡:

public class LatteCoffee extends Coffee {@Overridepublic String getName() {return "拿鐵咖啡";}
}

咖啡店:

public class CoffeeStore {public Coffee orderCoffee(String coffeeType) {// 聲明Coffee類型的變量,根據不同的類型創建不同的子類對象Coffee coffee = null;if("american".equals(coffeeType)) {coffee = new AmericanCoffee();}else if("latte".equals(coffeeType)) {coffee = new LatteCoffee();}else {throw new RuntimeException("對不起的,您所點的咖啡沒有");}// 加配料coffee.addmilk();coffee.addsugar();return coffee;}
}

測試方法:

public class Client {public static void main(String[] args) {// 創建咖啡店類CoffeeStore store = new CoffeeStore();// 點咖啡Coffee coffee = store.orderCoffee("latte");System.out.println(coffee.getName());}
}

存在問題】:如果需要更換對象,那么所有new對象的地方都要修改一遍,這就違背了軟件設計的開閉原則。
解決】:工廠模式

簡單工廠模式

角色:

  • 抽象產品:定義了產品的規范(咖啡類)
  • 具體產品:是現貨集成抽象產品的子類(美式咖啡、拿鐵咖啡)
  • 具體工廠:提供了創建產品的方法,調用者通過該方法來獲取產品

簡單咖啡工廠類,用來生產咖啡:

public class SimpleCoffeeFactory {public Coffee createCoffee(String coffeeType) {// 聲明Coffee類型的變量,根據不同的類型創建不同的子類對象Coffee coffee = null;if("american".equals(coffeeType)) {coffee = new AmericanCoffee();}else if("latte".equals(coffeeType)) {coffee = new LatteCoffee();}else {throw new RuntimeException("對不起的,您所點的咖啡沒有");}return coffee;}
}

咖啡店:

public class CoffeeStore {public Coffee orderCoffee(String coffeeType) {SimpleCoffeeFactory factory = new SimpleCoffeeFactory();// 調用生產咖啡的方法Coffee coffee = factory.createCoffee(coffeeType);// 加配料coffee.addmilk();coffee.addsugar();return coffee;}
}

解除了CoffeeStore和具體的咖啡的耦合
優勢】:工廠類的客戶端可能有很多,這樣只需要去修改SimpleCoffeeFactory的代碼,可以省去其他的修改操作。
劣勢】:如果要再加新的品種的咖啡,就必須要修改SimpleCoffeeFactory的代碼,這違反了開閉原則。

工廠方法模式

角色:

  • 抽象產品:定義了產品的規范(咖啡類)
  • 具體產品:是現貨集成抽象產品的子類(美式咖啡、拿鐵咖啡)
  • 抽象工廠:提供創建產品的接口,調用者通過訪問它具體工廠的工廠方法來創建產品
  • 具體工廠:提供了創建產品的方法,調用者通過該方法來獲取產品

抽象工廠:

public interface CoffeeFactory {// 創建咖啡對象的方法Coffee createCoffee();
}

具體工廠:

  • 拿鐵咖啡工廠對象 - 用來生產拿鐵咖啡
public class LatteCoffeeFactory implements CoffeeFactory {@Overridepublic Coffee createCoffee() {return new LatteCoffee();}
}
  • 美式咖啡工廠對象 - 用來生產美式咖啡
public class AmericanCoffeeFactory implements CoffeeFactory {@Overridepublic Coffee createCoffee() {return new AmericanCoffee();}
}

咖啡店:

public class CoffeeStore {private CoffeeFactory factory;public void setFactory(CoffeeFactory factory) {this.factory = factory;}// 點咖啡public Coffee orderCoffee() {// 創建咖啡Coffee coffee = factory.createCoffee();// 加配料coffee.addmilk();coffee.addmilk();return coffee;}
}

測試方法:

public class Client {public static void main(String[] args) {// 創建咖啡店類CoffeeStore store = new CoffeeStore();store.setFactory(new AmericanCoffeeFactory()); // 生產美式咖啡// 點咖啡Coffee coffee = store.orderCoffee();System.out.println(coffee.getName());}
}

優勢】:用戶只要知道具體工程的類名就可以得到產品;系統增加新的產品只需要新增具體產品類和對應的具體工廠類即可。
劣勢】:每增加一個產品就需要增加一個具體產品類和具體工廠類, 增加了系統的復雜度

抽象工廠模式

抽象工廠模式和工廠方法模式的區別:

  • 工廠方法模式:只生產一個等級的產品
  • 抽象工廠模式:可以創建多個不同等級的產品

需求變更】:現在咖啡店不僅需要生產咖啡,還需要生產甜品

甜品抽象類:

public abstract class Dessert {abstract void show();
}

提拉米蘇類:

public class Trimisu extends Dessert{@Overridevoid show() {System.out.println("提拉米蘇");}
}

抹茶慕斯類:

public class MatchaMousse extends Dessert{@Overridevoid show() {System.out.println("抹茶慕斯");}
}

甜品抽象工廠:

public interface DessertFactory {// 生產咖啡的功能Coffee createCoffee();// 生產甜品的功能Dessert createDessert();
}

意大利風味甜品工廠(生產拿鐵咖啡和提拉米蘇甜品):

public class ItaltyDessertFactory implements DessertFactory{ // 意大利風味甜品工廠(生產拿鐵咖啡和提拉米蘇甜品)@Overridepublic Coffee createCoffee() {return new LatteCoffee(); // 拿鐵咖啡}@Overridepublic Dessert createDessert() {return new Trimisu(); // 提拉米蘇}
}

美式咖啡的甜品工廠(生產美式咖啡和抹茶慕斯):

public class AmericanDessertFactory implements DessertFactory{ // 美式咖啡的甜品工廠 - 生產美式咖啡和抹茶慕斯@Overridepublic Coffee createCoffee() {return new AmericanCoffee(); // 美式咖啡}@Overridepublic Dessert createDessert() {return new MatchaMousse(); // 抹茶慕斯}
}

測試類:

public class Client {public static void main(String[] args) {// 創建意大利風味的工廠ItaltyDessertFactory it = new ItaltyDessertFactory();Coffee coffee = it.createCoffee(); // 拿鐵咖啡Dessert dessert = it.createDessert(); // 提拉米蘇System.out.println(coffee.getName());dessert.show();}
}

如果要加一個產品族,只需要再加一個對應的工廠類,不需要修改其他類
優點】:客戶端只能使用同一個產品族中的對象
缺點】:產品族種需要新增一個新的產品,所有的工廠類都需要進行修改。

適用場景:

  • 需要創建的對象是一系列相互關聯或相互依賴的產品族(電器工廠中的電視機、洗衣機、空調)
  • 系統種有多個產品族每次只用其中一種產品(有人只喜歡穿一個品牌的衣服和褲子)
  • 系統提供了產品的類庫,且所有產品的接口相同,客戶端不依賴產品實例的創建細節和內部結構
  • 如:搜狗輸入法換皮膚(一套一起換)

模式擴展(Spring框架底層)

bean.properties文件:

american = com.itheima.pattern02factory.factory_04_config.AmericanCoffee
latte = com.itheima.pattern02factory.factory_04_config.LatteCoffee

工廠類:

public class CoffeeFactory {// 1. 定義容器對象存儲咖啡對象private static HashMap<String, Coffee> map = new HashMap<>();// 2. 加載配置文件,并創建該配置文件里類的對象并進行存儲(只需要加載一次)static {// 2.1 創建Properties對象Properties p = new Properties();// 2.2 調用p對象中的load方法進行配置文件的加載InputStream is = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");try {p.load(is);// 2.3 從p集合中獲取全類名并創建對象Set<Object> keys = p.keySet();for (Object key : keys) {String className = p.getProperty((String) key);// 2.4 通過反射技術創建對象Class clazz = Class.forName(className);Coffee coffee = (Coffee) clazz.newInstance();// 2.5 將名稱和對象存儲到容器中map.put((String) key, coffee);}} catch (Exception e) {throw new RuntimeException(e);}}// 根據名稱獲取對象public static Coffee createCoffee(String name) {return map.get(name);}
}

JDK源碼解析 - Collection.iterator()

在這里插入圖片描述
在這里插入圖片描述

Collection是抽象工廠;ArrayList是具體工廠
【補】:DateForamt類中的getInstance()、Calendar類中的getInstance()也是工廠模式

原型模式

用一個已經創建的對象作為原型,復制這個原型對象來創建一個和原型對象相同的新對象。

包含的角色:

  • 抽象原型類:規定了具體原型對象必須實現的clone()方法
  • 具體原型類:實現抽象原型類中的clone()方法,他是可以被復制的對象

淺克隆:創建一個新對象,新對象的屬性和原來對象完全相同。對于非基本類型屬性,克隆對象和源對象指向的是同一塊內存空間
深克隆:創建一個新對象,屬性中的引用的其他對象也會被克隆,不會指向原有對象地址。

引例1:克隆對象

具體原型對象

public class Relizetype implements Cloneable { // 必須實現Cloneable接口:否則調用clone()會拋出CloneNotSupportedExceptionpublic Relizetype() {System.out.println("具體的原型類創建成功");}@Overridepublic Relizetype clone() throws CloneNotSupportedException { // clone()方法是在Object類里的System.out.println("具體原型復制成功");return (Relizetype) super.clone();}
}

測試類

public class Client {public static void main(String[] args) throws CloneNotSupportedException {// 創建原型對象Relizetype relizetype = new Relizetype();// 調用原型類中的clone()方法進行對象的克隆Relizetype clone = relizetype.clone();System.out.println(relizetype == clone); // false}
}

必須實現Cloneable接口:否則調用clone()會拋出CloneNotSupportedException。
重寫clone()方法:通常需將其改為public訪問權限,并返回具體類型。
clone() 方法創建了一個新的對象,而不是返回原對象的引用,因為java的Object.clone()方法是一個native方法,不會調用構造方法,而是直接分配內存并復制數據

引例2.1:三好學生獎狀分發(淺克隆)

獎狀類:

@Data
public class Citation implements Cloneable {// 三好學生上的姓名private String name;@Overridepublic Citation clone() throws CloneNotSupportedException {return (Citation) super.clone();}public void show() {System.out.println(name + "同學被評為三好學生");}
}

測試類:

public class CitationTest {public static void main(String[] args) throws CloneNotSupportedException {// 1. 創建原型對象Citation citation = new Citation();// 2. 克隆獎狀對象Citation citation1 = citation.clone();citation.setName("張三");citation1.setName("李四");citation.show(); // 張三同學被評為三好學生citation1.show(); // 李四同學被評為三好學生}
}

使用場景

  1. 對象的創建非常復雜,可以使用原型模式快捷創建對象(原型對象的所屬類必須實現clone()方法)
  2. 性能和安全的要求比較高

引例2.2:三好學生獎狀分發(深克隆)

學生類:

@Data
@Accessors(chain = true)
public class Student {// 學生姓名private String name;
}

獎狀類:

@Data
public class Citation implements Cloneable {private Student student;@Overridepublic Citation clone() throws CloneNotSupportedException {return (Citation) super.clone();}public void show() {System.out.println(student.getName() + "同學被評為三好學生");}
}

測試類:

public class CitationTest {public static void main(String[] args) throws CloneNotSupportedException {// 1. 創建原型對象Citation citation = new Citation();Student stu = new Student().setName("張三");citation.setStudent(stu);// 2. 克隆獎狀對象Citation citation1 = citation.clone();Student stu1 = citation1.getStudent();stu1.setName("李四");citation.show(); // 李四同學被評為三好學生citation1.show(); // 李四同學被評為三好學生}
}

問題】由上邊測試結果可知,修改了citation1成員變量的值的同時,也修改了citation對象的值
產生原因】這是因為stu和stu1此時是一個對象(這就是淺克隆),此時將stu1的屬性改成“李四”,導致stu的屬性也變成“李四”

修改】:把淺克隆變成深克隆(使用序列化和反序列化實現)

public class CitationTest1 {public static void main(String[] args) throws Exception {// 1. 創建原型對象Citation citation = new Citation();Student stu = new Student().setName("張三");citation.setStudent(stu);// 把對象寫入文件中ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt")); // 對象輸出流對象oos.writeObject(citation); // 寫對象oos.close(); // 釋放資源// 從文件中讀取對象 (2. 克隆獎狀對象)ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));Citation citation1 = (Citation) ois.readObject();citation1.getStudent().setName("李四");ois.close();citation.show(); // 張三同學被評為三好學生citation1.show(); // 李四同學被評為三好學生}
}

Citation類和Student類都必須實現Serializable接口,否則會拋NotSerializableException異常

修改】:把淺克隆變成深克隆(在重寫clone()方法的時候調用屬性的clone()方法)

獎狀類:

@Data
public class Citation implements Cloneable, Serializable {private Student student;@Overridepublic Citation clone() throws CloneNotSupportedException {Citation clone = (Citation) super.clone();clone.setStudent(student.clone()); // 深拷貝return clone;}public void show() {System.out.println(student.getName() + "同學被評為三好學生");}
}

學生類:

@Data
@Accessors(chain = true)
public class Student implements Serializable, Cloneable {// 學生姓名private String name;@Overridepublic Student clone() throws CloneNotSupportedException {return (Student) super.clone();}
}

測試類:

public class CitationTest {public static void main(String[] args) throws CloneNotSupportedException {// 1. 創建原型對象Citation citation = new Citation();Student stu = new Student().setName("張三");citation.setStudent(stu);// 2. 克隆獎狀對象Citation citation1 = citation.clone();citation1.getStudent().setName("李四");citation.show(); // 張三同學被評為三好學生citation1.show(); // 李四同學被評為三好學生}
}

建造者模式

將復雜對象的構建和表示分離,使同樣的構建過程可以創建不同的表示。

建造者建造的產品一般需要有較多的共同點,組成部分需要相似(如果產品之間差異比較大,不適合使用建造者模式)

  • 產品類(Product):要創建的復雜對象
  • 抽象建造者類(Builder):這個接口規定要實現復雜對象那部分的創建,不涉及具體的對象創建
  • 具體建造者類(ConcreteBuilder):實現Builder接口,完成復雜產品的各個部件的具體創建方法(強調裝配的過程)
  • 指揮者類(Director):調用具體建造者來創建復雜對象的各個部分,不涉及具體產品信息,只保證對象各個部分完整創建或按照某種順序創建
    在這里插入圖片描述

引例:創建共享單車

需求】:自行車包含了車架、車座等組件的生產;車架又有碳纖維,鋁合金等材質;車座有橡膠、真皮的材質
在這里插入圖片描述

Bike類:產品類(車架、車座組件)
Builder:抽象建造者(MobikeBuilder、OfoBuilder是具體的建造者)
Director:指揮者

產品類:

@Data
public class Bike {/*車架*/private String frame;/*車座*/private String seat;
}

抽象構建者:

public abstract class Builder {/*Bike對象*/protected Bike bike = new Bike(); // 目前還沒有組裝組件(指揮者做)/*構建車架*/public abstract void buildFrame();/*構建車座*/public abstract void buildSeat();/*構建自行車*/public abstract Bike createBike();
}

具體構建者1(摩拜單車):

public class MobileBuilder extends Builder{@Overridepublic void buildFrame() {bike.setFrame("碳纖維車架");}@Overridepublic void buildSeat() {bike.setSeat("真皮車座");}@Overridepublic Bike createBike() {return bike;}
}

具體構建者2(ofo單車):

public class OfoBuilder extends Builder{@Overridepublic void buildFrame() {bike.setFrame("鋁合金車架");}@Overridepublic void buildSeat() {bike.setSeat("橡膠車座");}@Overridepublic Bike createBike() {return bike;}
}

指揮者類:

public class Director {private Builder builder;public Director(Builder builder) {this.builder = builder;}/*組裝自行車*/public Bike construct() {builder.buildFrame();builder.buildSeat();return builder.createBike();}
}

測試類:

public class Client {public static void main(String[] args) {// 1. 創建指揮者對象Director director = new Director(new MobileBuilder());// 2. 讓指揮者進行自行車的組裝Bike bike = director.construct();System.out.println(bike.getFrame());System.out.println(bike.getSeat());}
}

Director指揮者類在建造者模式中很重要,是由指揮者類來指導具體的建造者應該如何構建產品,控制調用的先后順序,向調用者返回完整的產品類。

改進】:指揮者類也可以和抽象建造者進行結合:

public abstract class Builder {/*Bike對象*/protected Bike bike = new Bike(); // 目前還沒有組裝組件(指揮者做)/*構建車架*/public abstract void buildFrame();/*構建車座*/public abstract void buildSeat();/*構建自行車*/public abstract Bike createBike();/*組裝自行車*/public Bike construct() {this.buildFrame();this.buildSeat();return this.createBike();}
}

這樣做雖然可以不用寫指揮者類,但是也加重了建造者類的職責,也不符合單一職責原則,如果construct()過于復雜,還是建議封裝到Director中。

模式擴展

當一個類的構造方法需要傳入很多參數,如果創建這個類的實例,代碼的可讀性就會很差,就可以使用建造者模式進行重構。
手機類:

@Data
public class Phone {private String cpu;private String screen;private String memory;private String mainboard;/*私有構造方法*/private Phone(Builder builder) {this.cpu = builder.cpu;this.screen = builder.screen;this.memory = builder.memory;this.mainboard = builder.mainboard;}public static final class Builder {private String cpu;private String screen;private String memory;private String mainboard;public Builder cpu(String cpu) {this.cpu = cpu;return this; // 為了鏈式編程}public Builder screen(String screen) {this.screen = screen;return this;}public Builder memory(String memory) {this.memory = memory;return this;}public Builder mainboard(String mainboard) {this.mainboard = mainboard;return this;}/*使用構建者創建Phone對象*/public Phone build() {return new Phone(this);}}
}

測試類:

public class Client {public static void main(String[] args) {/*創建手機對象 - 通過構建者對象獲取手機對象*/Phone phone = new Phone.Builder().cpu("intel").screen("三星").memory("金士頓內存條").mainboard("華碩").build();System.out.println(phone); // Phone(cpu=intel, screen=三星, memory=金士頓內存條, mainboard=華碩)}
}

將構建的順序交給客戶,這個相當于lombok里的@Builder注解

構建者模式對比

工廠方法模式 vs 建造者模式

  1. 工廠方法模式:整體對象的創建方式
  2. 建造者模式:部件構建的過程

抽象工廠模式 vs 建造者模式

  1. 抽象工廠模式:實現對產品家族的創建,不需要關心建造過程,只關心什么產品由什么工廠生產
  2. 建造者模式:按照指定的藍圖建造產品,通過組裝零件而產生一個新產品

抽象工廠模式:汽車配件生產工廠(生產一個產品族的產品)
建造者模式:騎車組裝工廠(通過對配件的組裝可以返回一個完整的騎車)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/81527.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/81527.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/81527.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

邏輯與非邏輯的彌聚

非邏輯彌聚與邏輯彌聚是復雜系統中兩種不同的信息整合方式。邏輯彌聚側重于通過明確的規則、規律和結構化方法&#xff0c;將分散的信息或功能進行有序的組織和集中處理&#xff0c;強調理性和確定性。而非邏輯彌聚則更多地涉及情感、直覺、經驗等非線性、非結構化的因素&#…

Linux進程信號(三)之信號產生2

文章目錄 4. 由軟件條件產生信號5. 硬件異常產生信號模擬一下除0錯誤和野指針異常除0錯誤野指針錯誤 總結思考一下 4. 由軟件條件產生信號 SIGPIPE是一種由軟件條件產生的信號,在“管道”中已經介紹過了。 軟件條件不就緒&#xff0c;很明顯這個軟件條件沒有直接報錯&#xff…

讀取18B20的問題,時鐘太慢了

使用MSP430&#xff0c;1M時鐘&#xff0c;在讀取18B20數據時&#xff0c;一直存在問題&#xff0c;使用邏輯分析儀讀取的數據也是莫名其妙&#xff0c;查看電路圖和器件也沒有發現問題&#xff0c;就這樣斷斷續續的卡了一周多。 今天忽然想把時鐘升一下試試&#xff0c;原來1…

第12章 Java多線程機制

12.1 進程與線程 4種狀態&#xff1a;新建、運行、中斷和死亡。 &#xff08;新建、運行、中斷和死亡&#xff09; 建立線程的兩種方法&#xff1a;用Thread類或其子類。 線程新建后&#xff0c;必須調用 start () 方法使其進入就緒隊列&#xff0c;才有機會獲得 CPU 資源&a…

利用 Amazon Bedrock Data Automation(BDA)對視頻數據進行自動化處理與檢索

當前點播視頻平臺搜索功能主要是基于視頻標題的關鍵字檢索。對于點播平臺而言&#xff0c;我們希望可以通過優化視頻搜索體驗滿足用戶通過模糊描述查找視頻的需求&#xff0c;從而提高用戶的搜索體驗。借助 Amazon Bedrock Data Automation&#xff08;BDA&#xff09;技術&…

React 19版本refs也支持清理函數了。

文章目錄 前言一、refs 支持清理函數二、案例演示1.useEffect寫法2.React 19改進 的ref寫法 總結 前言 React 19版本發布了ref支持清理函數了&#xff0c;這樣就可以達到useEffect一樣的效果了。為啥需要清理函數呢&#xff0c;這是因為節約內存。 清理事件監聽&#xff08;避…

城市內澇監測預警系統守護城市安全

一、系統背景 城市內澇是指由于強降水或連續性降水超過城市排水能力&#xff0c;導致城市內產生積水災害的現象。隨著氣候變化和城市化進程的加快&#xff0c;城市內澇現象愈發頻繁和嚴重。傳統的城市排水系統已難以滿足當前的城市排水需求&#xff0c;特別是在暴雨等極端天氣條…

Flink 作業提交流程

Apache Flink 的 作業提交流程&#xff08;Job Submission Process&#xff09; 是指從用戶編寫完 Flink 應用程序&#xff0c;到最終在 Flink 集群上運行并執行任務的整個過程。它涉及多個組件之間的交互&#xff0c;包括客戶端、JobManager、TaskManager 和 ResourceManager。…

ctr查看鏡像

# 拉取鏡像到 k8s.io 命名空間 sudo nerdctl --namespace k8s.io pull nginx:1.23.4 # 驗證鏡像是否已下載 sudo nerdctl --namespace k8s.io images 下載鏡像到k8s.io名稱空間下 nerdctl --namespace k8s.io pull zookeeper:3.6.2 sudo ctr image pull --namespace k8s.io …

中科院自動化研究所通用空中任務無人機!基于大模型的通用任務執行與自主飛行

作者&#xff1a; Ji Zhao and Xiao Lin 單位&#xff1a;中科院自動化研究所 論文標題&#xff1a;General-Purpose Aerial Intelligent Agents Empowered by Large Language Models 論文鏈接&#xff1a;https://arxiv.org/pdf/2503.08302 主要貢獻 硬件-軟件協同設計框…

數據結構 -- 樹形查找(三)紅黑樹

紅黑樹 為什么要發明紅黑樹 平衡二叉樹AVL&#xff1a;插入/刪除很容易破壞平衡性&#xff0c;需要頻繁調整樹的形態。如&#xff1a;插入操作導致不平衡&#xff0c;則需要先計算平衡因子&#xff0c;找到最小不平衡子樹&#xff08;時間開銷大&#xff09;&#xff0c;在進行…

容器化-k8s-使用和部署

一、K8s 使用 1、基本概念 集群: 由 master 節點和多個 slaver 節點組成,是 K8s 的運行基礎。節點: 可以是物理機或虛擬機,是 K8s 集群的工作單元,運行容器化應用。Pod: K8s 中最小的部署單元,一個 Pod 可以包含一個或多個緊密相關的容器,這些容器共享網絡和存儲資源。…

力扣-283-移動零

1.題目描述 2.題目鏈接 283. 移動零 - 力扣&#xff08;LeetCode&#xff09; 3.題目代碼 class Solution {public void moveZeroes(int[] nums) {int dest-1;int cur0;while(cur<nums.length){if(nums[cur]0){cur;}else if(nums[cur]!0){swap(nums,cur,dest1);cur;dest…

前端開發筆記與實踐

一、Vue 開發規范與響應式機制 1. 組件命名規范 自定義組件使用大駝峰命名法&#xff08;如 MyComponent&#xff09;&#xff0c;符合 Vue 官方推薦&#xff0c;便于與原生 HTML 元素區分。 2. Proxy vs defineProperty 特性Proxy&#xff08;Vue3&#xff09;Object.defi…

如何給PSCAD添加庫文件

1、點擊Options 2、選擇藍色的選項 3、查看Intel(R) Visual Fortran Compiler XE 的版本 4、打開原文件的Library 5、打開 6、點擊這個文件的右鍵 7、然后選擇第一項project setting 9、先把第8步中link里面原有的路徑刪除&#xff0c;再點browes[A1] &#xff0c;然后選擇 [A…

milvus+flask山寨《從零構建向量數據庫》第7章case2

繼續流水賬完這本書&#xff0c;這個案例是打造文字形式的個人知識庫雛形。 create_context_db: # Milvus Setup Arguments COLLECTION_NAME text_content_search DIMENSION 2048 MILVUS_HOST "localhost" MILVUS_PORT "19530"# Inference Arguments…

【第一篇】 創建SpringBoot工程的四種方式

簡介&#xff1a; 通過此篇博客你可以使用任何方式進行創建 SpringBoot 項目&#xff0c;并且在文章的最后附上答疑解惑一節&#xff0c;為你排除在使用過程中發生的常見問題。文章內容若存在錯誤或需改進的地方&#xff0c;歡迎大家指正&#xff01;若對操作有任何疑問歡迎留言…

GPT( Generative Pre-trained Transformer )模型:基于Transformer

GPT是由openAI開發的一款基于Transformer架構的預訓練語言模型&#xff0c;擁有強大的生成能力和多任務處理能力&#xff0c;推動了自然語言處理&#xff08;NLP&#xff09;的快速發展。 一 GPT發展歷程 1.1 GPT-1&#xff08;2018年&#xff09; 是首個基于Transformer架構…

網絡檢測工具InternetTest v8.9.1.2504 單文件版,支持一鍵查詢IP/DNS、WIFI密碼信息

—————【下 載 地 址】——————— 【?本章下載一】&#xff1a;https://drive.uc.cn/s/295e068b79314 【?本章下載二】&#xff1a;https://pan.xunlei.com/s/VOQDXguH0DYPxrql5y2zlkhTA1?pwdg2nx# 【百款黑科技】&#xff1a;https://ucnygalh6wle.feishu.cn/wiki/…

CSS- 4.1 浮動(Float)

本系列可作為前端學習系列的筆記&#xff0c;代碼的運行環境是在HBuilder中&#xff0c;小編會將代碼復制下來&#xff0c;大家復制下來就可以練習了&#xff0c;方便大家學習。 HTML系列文章 已經收錄在前端專欄&#xff0c;有需要的寶寶們可以點擊前端專欄查看&#xff01; 點…