軟件設(shè)計(jì)模式是前輩們代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié),可以反復(fù)使用。設(shè)計(jì)模式共分為3大類,創(chuàng)建者模式(6種)、結(jié)構(gòu)型模式(7種)、行為型模式(11種),一共24種設(shè)計(jì)模式,軟件設(shè)計(jì)一般需要滿足7大基本原則。下面通過5章的學(xué)習(xí)一起來看看設(shè)計(jì)模式的魅力吧。
創(chuàng)建型模式(6種):本質(zhì)上就是將對(duì)象的創(chuàng)建與使用分離,就是描述怎樣去創(chuàng)建對(duì)象。
包括:單例、簡單工廠、工廠方法、抽象工廠、原型、建造者 模式
創(chuàng)建型模式的對(duì)比:
工廠方法模式VS建造者模式
工廠方法模式更注重對(duì)象的創(chuàng)建方式,建造者模式更注重部件的創(chuàng)建過程。
抽象工廠模式VS建造者模式
抽象工廠模式是對(duì)產(chǎn)品家族的創(chuàng)建,建造者模式是指按照指定的藍(lán)圖建造產(chǎn)品。
目錄
1.1、單例模式
1.2、簡單工廠模式
1.3、工廠方法模式
1.4、抽象工廠模式
1.5、原型模式
1.6、建造者模式
1.1、單例模式
單例模式是最簡單的設(shè)計(jì)模式之一,屬于創(chuàng)建者模式,提供了一種創(chuàng)建對(duì)象的最佳方式,類中只創(chuàng)建一個(gè)實(shí)例對(duì)象,并提供訪問對(duì)象的方式。單例模式是指確保一個(gè)類在任何情況下都只有一個(gè)實(shí)例,并且提供一個(gè)訪問該單例的全局訪問點(diǎn)。
單例模式分為餓漢模式與懶漢模式兩種。餓漢式是線程安全的,但是類加載的時(shí)候就創(chuàng)建實(shí)例對(duì)象,如果后期不使用,會(huì)造成內(nèi)存的浪費(fèi)。
餓漢模式:類加載的時(shí)候,實(shí)例對(duì)象使用之前,就創(chuàng)建了實(shí)例對(duì)象
懶漢模式:類加載的時(shí)候沒創(chuàng)建,使用的時(shí)候,才創(chuàng)建實(shí)例對(duì)象
餓漢模式可以通過靜態(tài)成員變量的方式創(chuàng)建實(shí)例,也可以通過靜態(tài)代碼塊的方式創(chuàng)建實(shí)例。
/**
* @author nuist__NJUPT
* @ClassName Singleton
* @description: 餓漢式-靜態(tài)成員變量
* @date 2024年01月19日
*/
public class Singleton {
// 1.私有構(gòu)造方法,避免外界創(chuàng)建帶參數(shù)的實(shí)例化對(duì)象
private Singleton(){}
// 2.在本類中創(chuàng)建本類的實(shí)例化對(duì)象
private static Singleton instance = new Singleton() ;
// 3.提供一個(gè)訪問該實(shí)例對(duì)象的方式,讓外界訪問
public static Singleton getInstance(){
return instance ;
}
}
/**
* @author nuist__NJUPT
* @ClassName Singleton1
* @description: 餓漢模式-靜態(tài)代碼塊
* @date 2024年01月19日
*/
public class Singleton1 {
private Singleton1(){}
private static Singleton1 instance ;
{
instance = new Singleton1() ;
}
public static Singleton1 getInstance() {
return instance;
}
}
通過測試類可以發(fā)現(xiàn)創(chuàng)建的兩個(gè)實(shí)例對(duì)象是同一個(gè)對(duì)象,即單例。
/**
* @author nuist__NJUPT
* @ClassName Client
* @description: 測試類
* @date 2024年01月19日
*/
public class Client {
public static void main(String[] args) {
Singleton instance1 = Singleton.getInstance() ;
Singleton instance2 = Singleton.getInstance() ;
System.out.println(instance1 == instance2) ;
Singleton1 singleton1 = Singleton1.getInstance() ;
Singleton1 singleton11 = Singleton1.getInstance() ;
System.out.println(singleton1 == singleton11) ;
}
}
懶漢模式是類加載的時(shí)候不創(chuàng)建實(shí)例,在使用的時(shí)候才創(chuàng)建,需要使用雙重檢查鎖的機(jī)制保證線程安全,即保證只創(chuàng)建一個(gè)實(shí)例。
/**
* @author nuist__NJUPT
* @ClassName Singleton
* @description: 懶漢模式-雙重檢查鎖方式
* @date 2024年01月19日
*/
public class Singleton {
// 1.私有構(gòu)造方法
private Singleton(){
}
// 2.定義實(shí)例對(duì)象
// jvm在實(shí)例化對(duì)象會(huì)進(jìn)行優(yōu)化和指令重排序操作,故使用雙重檢測瑣在多線程可能會(huì)出現(xiàn)空指針問題
// 使用volatile關(guān)鍵字可以保證可見性和有序性
private static volatile Singleton instance ;
//3.對(duì)外提供訪問方式
/**
* 雙重檢查鎖瑣的方式保證線程安全
* @return 實(shí)例對(duì)象
*/
public static Singleton getInstance() {
// 如果instance不為空,不需要搶占瑣,直接返回對(duì)象
if(instance == null){
synchronized (Singleton.class) {
// 搶到瑣之后再次判斷是否為空
if(instance == null){
instance = new Singleton() ;
}
}
}
return instance;
}
}
序列化與反序列化、以及反射的方式會(huì)破壞單例模式,需要進(jìn)一步解決相應(yīng)的問題。
JDK源碼中Runtime類對(duì)象是單例模式,餓漢式的方式實(shí)現(xiàn)。
單例模式應(yīng)用的場景一般發(fā)現(xiàn)在以下條件下:
?。?)資源共享的情況下,避免由于資源操作時(shí)導(dǎo)致的性能或損耗等。如日志文件,應(yīng)用配置。
?。?)控制資源的情況下,方便資源之間的互相通信。如線程池等。
日志系統(tǒng):在應(yīng)用程序中,通常只需要一個(gè)日志系統(tǒng),以避免在多個(gè)地方創(chuàng)建多個(gè)日志對(duì)象,并降低資源消耗。
數(shù)據(jù)庫連接池:在應(yīng)用程序中,數(shù)據(jù)庫連接池是一個(gè)非常重要的資源,單例模式可以確保在應(yīng)用程序中只有一個(gè)數(shù)據(jù)庫連接池實(shí)例,避免資源浪費(fèi)。
配置文件管理器:在應(yīng)用程序中,通常只需要一個(gè)配置文件管理器來管理應(yīng)用程序的配置文件,單例模式可以確保在整個(gè)應(yīng)用程序中只有一個(gè)配置文件管理器實(shí)例。
緩存系統(tǒng):在應(yīng)用程序中,緩存系統(tǒng)是一個(gè)重要的組件,單例模式可以確保在整個(gè)應(yīng)用程序中只有一個(gè)緩存實(shí)例,以提高應(yīng)用程序的性能。
GUI組件:在圖形用戶界面(GUI)開發(fā)中,單例模式可以確保在整個(gè)應(yīng)用程序中只有一個(gè)GUI組件實(shí)例,以確保用戶界面的一致性和穩(wěn)定性。
?
1.2、簡單工廠模式
簡單工廠模式:簡單工廠模式可以理解為一種編程習(xí)慣,簡單工廠包含如下三個(gè)角色:
01.抽象產(chǎn)品:定義產(chǎn)品的規(guī)范,主要包括產(chǎn)品的功能與特性。
02.具體產(chǎn)品:實(shí)現(xiàn)或者繼承抽象產(chǎn)品。
03.具體工廠:提供創(chuàng)建產(chǎn)品的方法,調(diào)用者通過該方法來獲取產(chǎn)品。
優(yōu)點(diǎn):要實(shí)現(xiàn)新產(chǎn)品直接修改工廠類代碼即可,避免客戶端代碼修改,更容易擴(kuò)展。
缺點(diǎn):增加新產(chǎn)品還是需要修改工廠類,在一定程度上違反了開閉原則。
1.我們看個(gè)案例,定義一個(gè)抽象產(chǎn)品咖啡,定義兩個(gè)具體產(chǎn)品美式咖啡和拿鐵咖啡。
/**
* @author nuist__NJUPT
* @ClassName Coffee
* @description: 咖啡類-抽象產(chǎn)品
* @date 2024年01月19日
*/
public abstract class Coffee {
public abstract String getName() ;
public void addSugar(){
System.out.println("加糖");
}
public void addMilk(){
System.out.println("加奶");
}
}
/**
* @author nuist__NJUPT
* @ClassName LatteCoffee
* @description: 拿鐵咖啡-具體產(chǎn)品
* @date 2024年01月19日
*/
public class LatteCoffee extends Coffee{
@Override
public String getName() {
return "拿鐵咖啡";
}
}
/**
* @author nuist__NJUPT
* @ClassName AmericanCoffee
* @description: 美式咖啡-具體產(chǎn)品
* @date 2024年01月19日
*/
public class AmericanCoffee extends Coffee {
@Override
public String getName() {
return "美式咖啡";
}
}
2.定義一個(gè)coffee工廠進(jìn)行咖啡生產(chǎn),解耦咖啡店和具體咖啡的關(guān)系。
/**
* @author nuist__NJUPT
* @ClassName SimpleCoffeeFactory
* @description: 簡單coffee工廠
* @date 2024年01月19日
* @version: 1.0
*/
public class SimpleCoffeeFactory {
/**
* 生產(chǎn)coffee方法
* @param type 咖啡類型
* @return 咖啡
*/
public Coffee createCoffee(String type){
Coffee coffee = null ;
if(type.equals("american")){
coffee = new AmericanCoffee() ;
}else if(type.equals("latte")){
coffee = new AmericanCoffee() ;
}else {
throw new RuntimeException("沒有您要的coffee") ;
}
return coffee ;
}
}
3.咖啡店直接調(diào)用簡單咖啡工廠進(jìn)行咖啡生產(chǎn)。這樣咖啡店不需要直接與具體的咖啡交互,只需要和工廠交互,工廠完成具體的咖啡生產(chǎn)管理,可以實(shí)現(xiàn)模塊之間的解耦。
/**
* @author nuist__NJUPT
* @ClassName CoffeeStore
* @description: coffee店
* @date 2024年01月19日
*/
public class CoffeeStore {
/**
* 生產(chǎn)coffee方法
* @param type 咖啡類型
* @return 咖啡
*/
public Coffee createCoffee(String type){
SimpleCoffeeFactory simpleCoffeeFactory = new SimpleCoffeeFactory() ;
Coffee coffee = simpleCoffeeFactory.createCoffee(type);
coffee.addSugar();
coffee.addMilk();
return coffee ;
}
}
4.編寫測試類進(jìn)行測試。
/**
* @author nuist__NJUPT
* @ClassName Client
* @description: 測試類
* @date 2024年01月19日
*/
public class Client {
public static void main(String[] args) {
CoffeeStore coffeeStore = new CoffeeStore() ;
Coffee latte = coffeeStore.createCoffee("latte");
System.out.println(latte.getName());
}
}
1.3、工廠方法模式
工廠方法模式可以避免簡單工廠模式違反開閉原則的問題。
工廠方法模式:定義一個(gè)用于創(chuàng)建對(duì)象的接口(工廠),讓子類對(duì)象決定實(shí)例化哪個(gè)產(chǎn)品對(duì)象,使一個(gè)產(chǎn)品的實(shí)例化延遲到其工廠的子類。
工廠方法模式的主要角色如下:
01.抽象產(chǎn)品:定義產(chǎn)品的規(guī)范,主要包括產(chǎn)品的功能與特性。
02.具體產(chǎn)品:實(shí)現(xiàn)或者繼承抽象產(chǎn)品。
03.具體工廠:實(shí)現(xiàn)抽象工廠中的方法,完成具體產(chǎn)品的創(chuàng)建。
04.抽象工廠:提供創(chuàng)建產(chǎn)品的接口,調(diào)用者通過抽象工廠訪問具體工廠的工廠方法來創(chuàng)建產(chǎn)品。
優(yōu)點(diǎn):封裝性比較好,只需知道具體工廠名字就可以得到產(chǎn)品,無需知道產(chǎn)品的創(chuàng)建過程。另外新增加產(chǎn)品的時(shí)候只需要增加抽象工廠的實(shí)現(xiàn)類即可,原抽象工廠不需要改動(dòng),滿足開閉原則。
缺點(diǎn):只能生產(chǎn)統(tǒng)一等級(jí)的產(chǎn)品,不支持生產(chǎn)多等級(jí)的產(chǎn)品
1.我們使用工廠方法模式改進(jìn)上述的簡單工廠模式,首先定義一個(gè)抽象產(chǎn)品咖啡和兩個(gè)具體產(chǎn)品拿鐵咖啡和美式咖啡類,與上面的一樣。
2.然后我們定義抽象咖啡工廠接口與咖啡工廠接口的實(shí)現(xiàn)類(具體工廠)。
/**
* @author nuist__NJUPT
* @InterfaceName CoffeeFactory
* @description: 抽象工廠接口
* @date 2024年01月19日
*/
public interface CoffeeFactory {
// 創(chuàng)建咖啡對(duì)象
Coffee createCoffee() ;
}
/**
* @author nuist__NJUPT
* @ClassName AmericanCoffeeFactory
* @description: 抽象工廠實(shí)現(xiàn)類
* @date 2024年01月19日
*/
public class AmericanCoffeeFactory implements CoffeeFactory {
@Override
public Coffee createCoffee() {
return new AmericanCoffee();
}
}
/**
* @author nuist__NJUPT
* @ClassName LatteCoffeeFactory
* @description: 咖啡工廠實(shí)現(xiàn)類
* @date 2024年01月19日
*/
public class LatteCoffeeFactory implements CoffeeFactory{
@Override
public Coffee createCoffee() {
return new LatteCoffee();
}
}
3.接下來就可以在咖啡店中注入抽象工廠,而不是具體工廠。
/**
* @author nuist__NJUPT
* @ClassName CoffeeStore
* @description: coffee店
* @date 2024年01月19日
*/
public class CoffeeStore {
private CoffeeFactory coffeeFactory ;
public void setCoffeeFactory(CoffeeFactory coffeeFactory) {
this.coffeeFactory = coffeeFactory;
}
/**
* 點(diǎn)coffee方法
* @return 咖啡
*/
public Coffee orderCoffee(){
Coffee coffee = coffeeFactory.createCoffee();
coffee.addMilk();
coffee.addSugar();
return coffee ;
}
}
4.最后編寫測試類測試點(diǎn)咖啡功能。
/**
* @author nuist__NJUPT
* @ClassName Client
* @description: 測試類
* @date 2024年01月19日
*/
public class Client {
public static void main(String[] args) {
// 實(shí)例化咖啡店
CoffeeStore coffeeStore = new CoffeeStore() ;
// 面向抽象工廠接口實(shí)例化具體咖啡工廠
CoffeeFactory coffeeFactory = new AmericanCoffeeFactory() ;
// 在咖啡店注入抽象咖啡工廠
coffeeStore.setCoffeeFactory(coffeeFactory);
// 調(diào)用方法點(diǎn)咖啡
Coffee coffee = coffeeStore.orderCoffee();
System.out.println(coffee.getName());
}
}
1.4、抽象工廠模式
工廠方法模式是用于生產(chǎn)同一等級(jí)的多種產(chǎn)品,對(duì)于不同等級(jí)的產(chǎn)品,使用抽象工廠生產(chǎn)。
抽象工廠模式:生產(chǎn)多等級(jí)的產(chǎn)品,將一個(gè)具體工廠生產(chǎn)的不同等級(jí)的產(chǎn)品的一組產(chǎn)品成為產(chǎn)品族。
抽象工廠模式的主要角色如下:
01.抽象產(chǎn)品:定義產(chǎn)品的規(guī)范,主要包括產(chǎn)品的功能與特性。
02.具體產(chǎn)品:實(shí)現(xiàn)或者繼承抽象產(chǎn)品。
03.具體工廠:實(shí)現(xiàn)抽象工廠中的方法,完成具體產(chǎn)品的創(chuàng)建。
04.抽象工廠:提供創(chuàng)建產(chǎn)品的接口,包含多個(gè)創(chuàng)建產(chǎn)品的方法,可以創(chuàng)建多個(gè)不同等級(jí)的產(chǎn)品。
優(yōu)點(diǎn):當(dāng)一個(gè)產(chǎn)品族中多個(gè)對(duì)象被設(shè)計(jì)在一起工作的時(shí)候,能保證客戶端始終只使用同一個(gè)產(chǎn)品族的對(duì)象。
缺點(diǎn):當(dāng)產(chǎn)品族中需要增加一個(gè)產(chǎn)品的時(shí)候,所有的工廠類都需要修改。
Java的JDK源碼的迭代器遍歷集合用的就是抽象工廠方法。
我們下面看一個(gè)案例,還是咖啡的案例,不過這次加了甜品,屬于同族的產(chǎn)品。
1.首先定義抽象產(chǎn)品咖啡和甜品,然后定義具體的拿鐵咖啡和美式咖啡、以及具體的兩種甜品。
/**
* @author nuist__NJUPT
* @ClassName Coffee
* @description: 咖啡類-抽象產(chǎn)品
* @date 2024年01月19日
*/
public abstract class Coffee {
public abstract String getName() ;
public void addSugar(){
System.out.println("加糖");
}
public void addMilk(){
System.out.println("加奶");
}
}
/**
* @author nuist__NJUPT
* @ClassName AmericanCoffee
* @description: 美式咖啡-具體產(chǎn)品
* @date 2024年01月19日
*/
public class AmericanCoffee extends Coffee {
@Override
public String getName() {
return "美式咖啡";
}
}
/**
* @author nuist__NJUPT
* @ClassName LatteCoffee
* @description: 拿鐵咖啡-具體產(chǎn)品
* @date 2024年01月19日
*/
public class LatteCoffee extends Coffee {
@Override
public String getName() {
return "拿鐵咖啡";
}
}
/**
* @author nuist__NJUPT
* @ClassName Dessert
* @description: 甜品抽象類
* @date 2024年01月19日
*/
public abstract class Dessert {
public abstract void show() ;
}
/**
* @author nuist__NJUPT
* @ClassName MatchaMousse
* @description: 抹茶慕斯類
* @date 2024年01月19日
*/
public class MatchaMousse extends Dessert {
@Override
public void show() {
System.out.println("抹茶慕斯");
}
}
/**
* @author nuist__NJUPT
* @ClassName Trimisu
* @description: 提拉米蘇類
* @date 2024年01月19日
*/
public class Trimisu extends Dessert {
@Override
public void show() {
System.out.println("提拉米蘇類");
}
}
2.定義一個(gè)抽象工廠,用于生產(chǎn)同族產(chǎn)品。
/**
* @author nuist__NJUPT
* @InterfaceName DessertFactory
* @description: 甜品工廠
* @date 2024年01月19日
*/
public interface DessertFactory {
// 生產(chǎn)咖啡
Coffee createCoffee() ;
// 生產(chǎn)天甜品
Dessert createDessert() ;
}
3.定義抽象工廠具體的實(shí)現(xiàn)類,用于生產(chǎn)具體的同族產(chǎn)品。
/**
* @author nuist__NJUPT
* @ClassName AmericanDessertFactory
* @description: 美式甜品工廠:即能生產(chǎn)美式咖啡,也能生產(chǎn)抹茶慕斯
* @date 2024年01月19日
*/
public class AmericanDessertFactory implements DessertFactory{
@Override
public Coffee createCoffee() {
return new AmericanCoffee();
}
@Override
public Dessert createDessert() {
return new MatchaMousse();
}
}
/**
* @author nuist__NJUPT
* @ClassName ItalyDessertFactory
* @description: 意大利風(fēng)味的甜品工廠
* @date 2024年01月19日
*/
public class ItalyDessertFactory implements DessertFactory {
@Override
public Coffee createCoffee() {
return new LatteCoffee();
}
@Override
public Dessert createDessert() {
return new Trimisu();
}
}
4.定義測試類,實(shí)例化工廠實(shí)現(xiàn)類,并生產(chǎn)調(diào)用方法生產(chǎn)同族產(chǎn)品。
/**
* @author nuist__NJUPT
* @ClassName Client
* @description: 測試類
* @date 2024年01月19日
*/
public class Client {
public static void main(String[] args) {
// 創(chuàng)建抽象工廠對(duì)象
// AmericanDessertFactory factory = new AmericanDessertFactory() ;
ItalyDessertFactory factory = new ItalyDessertFactory() ;
//生產(chǎn)咖啡和甜品等產(chǎn)品
Coffee coffee = factory.createCoffee();
Dessert dessert = factory.createDessert();
System.out.println(coffee.getName());
dessert.show();
}
}
1.5、原型模式
原型模式:用一個(gè)已經(jīng)創(chuàng)建的實(shí)例作為原型,通過復(fù)制該原型對(duì)象創(chuàng)建一個(gè)和該原型對(duì)象相同的對(duì)象。
原型模式包含如下角色:
01.抽象原型類:規(guī)定了具體原型對(duì)象必須實(shí)現(xiàn)的clone()方法
02.具體原型類:實(shí)現(xiàn)抽象原型類的clone()方法,它是可被復(fù)制的對(duì)象
03.訪問類:使用具體原型類的clone()方法來復(fù)制新對(duì)象
深克?。嚎寺』绢愋蛿?shù)據(jù)與其地址
淺克?。翰豢寺〉刂罚豢寺』緮?shù)據(jù)
我們看一個(gè)原型模式的案例,同一個(gè)學(xué)校的三好學(xué)生獎(jiǎng)狀除了學(xué)生名字外其余都相同,我們可以使用原型模式復(fù)制多個(gè)三好學(xué)生的獎(jiǎng)狀,然后修改名字即可。
1.定義原型對(duì)象,實(shí)現(xiàn)Cloneable接口并重寫clone()方法。
/**
* @author nuist__NJUPT
* @ClassName Citation
* @description: 原型對(duì)象
* @date 2024年01月19日
*/
public class Citation implements Cloneable{
// 三好學(xué)生的姓名
private String name ;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println(name + "同學(xué)獲得三好學(xué)生獎(jiǎng)狀");
}
@Override
protected Citation clone() throws CloneNotSupportedException {
return (Citation) super.clone();
}
}
2.定義測試類,實(shí)例化原型對(duì)象,并克隆原型對(duì)象,并調(diào)用原型對(duì)象的方法。
/**
* @author nuist__NJUPT
* @ClassName Client
* @description: 測試類
* @date 2024年01月19日
*/
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
// 創(chuàng)建原型對(duì)象
Citation citation = new Citation() ;
// 克隆出來的原型對(duì)象
// 對(duì)象的創(chuàng)建比較復(fù)雜的情況下,可以使用原型對(duì)象克隆,而不是創(chuàng)建對(duì)象
Citation citation1 = citation.clone();
citation.setName("張三");
citation1.setName("李四");
citation.show();
citation1.show();
}
}
對(duì)于淺克隆克隆的對(duì)象還是同一個(gè)對(duì)象,對(duì)象的地址并沒有改變,會(huì)導(dǎo)致一些問題,java可以通過序列化和反序列化的方式實(shí)現(xiàn)深克隆。
如下定義一個(gè)Student實(shí)體,定義一個(gè)原型類進(jìn)行深克隆,定義測試類通過反序列化實(shí)現(xiàn)深克隆。
import java.io.Serializable;
/**
* @author nuist__NJUPT
* @ClassName Student
* @description: TODO
* @date 2024年01月19日
*/
public class Student implements Serializable {
private String name ;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
import java.io.Serializable;
/**
* @author nuist__NJUPT
* @ClassName Citation
* @description: 原型對(duì)象
* @date 2024年01月19日
*/
public class Citation implements Cloneable, Serializable {
Student student ;
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
public void show(){
System.out.println(student.getName() + "同學(xué)獲得三好學(xué)生獎(jiǎng)狀");
}
@Override
protected Citation clone() throws CloneNotSupportedException {
return (Citation) super.clone();
}
}
import java.io.*;
/**
* @author nuist__NJUPT
* @ClassName Client
* @description: 測試類
* @date 2024年01月19日
*/
public class Client {
public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
// 創(chuàng)建原型對(duì)象
Citation citation = new Citation() ;
Student student = new Student() ;
student.setName("張三");
citation.setStudent(student);
// 克隆出來的原型對(duì)象
// 對(duì)象的創(chuàng)建比較復(fù)雜的情況下,可以使用原型對(duì)象克隆,而不是創(chuàng)建對(duì)象
// Citation citation1 = citation.clone();
// 通過序列化與反序列化的方式進(jìn)行深克隆
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("file.txt")) ;
oos.writeObject(citation);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("file.txt")) ;
Citation citation1 = (Citation) ois.readObject();
ois.close();
citation1.getStudent().setName("李四");
// 同一個(gè)student對(duì)象,淺克隆導(dǎo)致修改之后會(huì)覆蓋之前的
citation.show();
citation1.show();
}
}
1.6、建造者模式
建造者模式:將復(fù)雜對(duì)象的構(gòu)造與裝配分離,就是將組件與整體進(jìn)行分離,使得同樣的裝配組件的過程可以生產(chǎn)出不同的整體。
建造者模式包含如下幾個(gè)角色:
01.抽象建造者類:該接口規(guī)定了實(shí)現(xiàn)復(fù)雜對(duì)象的那部分創(chuàng)建,并不涉及具體部件對(duì)象的創(chuàng)建。
02.具體建造者類:實(shí)現(xiàn)抽象類接口,完成復(fù)雜對(duì)象的具體創(chuàng)建,提供產(chǎn)品的實(shí)例。
03.產(chǎn)品類:要?jiǎng)?chuàng)建的復(fù)雜對(duì)象。
04.指揮者類:完成具體裝配的過程,不涉及具體的產(chǎn)品信息,只保證對(duì)象各部分的完整創(chuàng)建。
建造者模式的優(yōu)缺點(diǎn)如下:
優(yōu)點(diǎn):封裝性較好,建造過程可以封裝到具體建造者類中,產(chǎn)品與組裝過程解耦,復(fù)雜的步驟可以分解在不同的方法中,易擴(kuò)展,擴(kuò)展的話只需要增加一個(gè)具體建造者類即可,滿足開閉原則 。
缺點(diǎn):如果創(chuàng)建的產(chǎn)品差異性較大不適合使用
建造者模式適用場景:適合于產(chǎn)品組件復(fù)雜但是建造過程的算法相對(duì)穩(wěn)定。
1.下面我們通過共享單車構(gòu)建過程的案例去進(jìn)一步學(xué)習(xí)一下建造者模式。首先我們定義具體的產(chǎn)品類Bike,即需要構(gòu)建的對(duì)象。
/**
* @author nuist__NJUPT
* @ClassName Bike
* @description: 單車產(chǎn)品類
* @date 2024年01月20日
*/
public class Bike {
// 車架
private String frame ;
// 車座
private String seat ;
public String getFrame() {
return frame;
}
public void setFrame(String frame) {
this.frame = frame;
}
public String getSeat() {
return seat;
}
public void setSeat(String seat) {
this.seat = seat;
}
}
2.下面定義抽象建造者類,在抽象建造者類中定義建造規(guī)則,并定義具體建造者類實(shí)現(xiàn)具體的規(guī)則。
/**
* @author nuist__NJUPT
* @ClassName Builder
* @description: 抽象建造者類
* @date 2024年01月20日
*/
public abstract class Builder {
// 聲明Bike類型
protected Bike bike = new Bike() ;
// 構(gòu)建車架
public abstract void buildFrame() ;
// 構(gòu)建車座
public abstract void buildSeat() ;
// 構(gòu)建自行車
public abstract Bike createBike() ;
}
/**
* @author nuist__NJUPT
* @ClassName MobileBuilder
* @description: 具體的建造者-摩拜單車構(gòu)建者
* @date 2024年01月20日
*/
public class MobileBuilder extends Builder {
@Override
public void buildFrame() {
bike.setFrame("碳纖維車架");
}
@Override
public void buildSeat() {
bike.setSeat("假皮車座");
}
@Override
public Bike createBike() {
return bike ;
}
}
/**
* @author nuist__NJUPT
* @ClassName OfoBuilder
* @description: Ofo單車建造者
* @date 2024年01月20日
*/
public class OfoBuilder extends Builder{
@Override
public void buildFrame() {
bike.setFrame("鋁合金車架");
}
@Override
public void buildSeat() {
bike.setSeat("牛皮車座");
}
@Override
public Bike createBike() {
return bike;
}
}
3.定義指揮者類,負(fù)責(zé)指導(dǎo)對(duì)象的建造過程,確保產(chǎn)品的創(chuàng)建。
/**
* @author nuist__NJUPT
* @ClassName Director
* @description: 指揮者類
* 指揮者類在建造者模式中負(fù)責(zé)指導(dǎo)具體建造者如何建造產(chǎn)品,
* 控制好調(diào)用的先后順序,并向調(diào)用者返回完整的產(chǎn)品類
* 在某些場景中可以將指揮者類和抽象建造者類進(jìn)行融合,也就是把construct方法放到Builder類中
* @date 2024年01月20日
*/
public class Director {
// 注入建造者
private Builder builder ;
public Director(Builder builder) {
this.builder = builder;
}
/**
* 構(gòu)建單車方法
* @return 單車
*/
public Bike construct(){
builder.buildFrame();
builder.buildSeat();
Bike bike = builder.createBike();
return bike ;
}
}
4.最后定義測試類進(jìn)行測試是否建造完成,實(shí)例化指揮者類并注入具體的建造者,指揮完成具體產(chǎn)品的建造。
/**
* @author nuist__NJUPT
* @ClassName Client
* @description: 測試類
* @date 2024年01月20日
*/
public class Client {
public static void main(String[] args) {
// 創(chuàng)建指揮者對(duì)象
Director director = new Director(new MobileBuilder()) ;
// 指揮者指揮組裝自行車
Bike construct = director.construct();
// 打印
System.out.println(construct.getFrame() + construct.getSeat());
}
}
我們通過下面一個(gè)案例看一下傳統(tǒng)的構(gòu)造器傳參和利用建造者模式傳參,建造者模式代碼可讀性更好,參數(shù)位置可以在客戶端定義,不需要在構(gòu)造器中寫死,更靈活。
1.傳統(tǒng)的構(gòu)造器傳參,如下:
/**
* @author nuist__NJUPT
* @ClassName Phone1
* @description: 傳統(tǒng)的構(gòu)造傳參
* @date 2024年01月20日
*/
public class Phone1 {
private String cpu ;
private String screen ;
private String memory ;
private String mainBoard ;
public Phone1(String cpu, String screen, String memory, String mainBoard) {
this.cpu = cpu;
this.screen = screen;
this.memory = memory;
this.mainBoard = mainBoard;
}
@Override
public String toString() {
return "Phone1{" +
"cpu='" + cpu + '\'' +
", screen='" + screen + '\'' +
", memory='" + memory + '\'' +
", mainBoard='" + mainBoard + '\'' +
'}';
}
}
2.使用建造者模式,利用指揮者控制傳參順序。文章來源:http://www.zghlxwxcb.cn/news/detail-813749.html
/**
* @author nuist__NJUPT
* @ClassName Phone
* @description: 建造者模式改進(jìn)的構(gòu)造器
* @date 2024年01月20日
*/
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 ;
}
@Override
public String toString() {
return "Phone{" +
"cpu='" + cpu + '\'' +
", screen='" + screen + '\'' +
", memory='" + memory + '\'' +
", mainBoard='" + 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 memory(String memory){
this.memory = memory ;
return this ;
}
public Builder screen(String screen){
this.screen = screen ;
return this ;
}
public Builder mainBoard(String mainBoard){
this.mainBoard = mainBoard ;
return this ;
}
public Phone build(){
return new Phone(this) ;
}
}
}
3.最后定義測試類進(jìn)行測試,觀察兩種傳參方式,可以發(fā)現(xiàn)建造者模式可以改變傳參順序,靈活性強(qiáng),同時(shí)可讀性也更高。文章來源地址http://www.zghlxwxcb.cn/news/detail-813749.html
/**
* @author nuist__NJUPT
* @ClassName Client
* @description: 測試類
* @date 2024年01月20日
*/
public class Client {
public static void main(String[] args) {
// 創(chuàng)建指揮者對(duì)象
Phone phone = new Phone.Builder()
.cpu("英特爾CPU")
.memory("金士頓內(nèi)存")
.mainBoard("聯(lián)想主板")
.screen("液晶屏幕").build() ;
System.out.println(phone);
// 傳統(tǒng)方法
Phone1 phone1 = new Phone1("AMD的CPU", "三星顯示屏", "金士頓內(nèi)存", "華為主板") ;
System.out.println(phone1);
}
}
到了這里,關(guān)于24種設(shè)計(jì)模式之創(chuàng)建者模式-Java版的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!