24种设计模式之创建者模式-Java版

软件设计模式是前辈们代码设计经验的总结,可以反复使用。设计模式共分为3大类,创建者模式(6种)、结构型模式(7种)、行为型模式(11种),一共24种设计模式,软件设计一般需要满足7大基本原则。下面通过5章的学习一起来看看设计模式的魅力吧。

创建型模式(6种):本质上就是将对象的创建与使用分离,就是描述怎样去创建对象。

包括:单例、简单工厂、工厂方法、抽象工厂、原型、建造者 模式

创建型模式的对比:

工厂方法模式VS建造者模式

工厂方法模式更注重对象的创建方式,建造者模式更注重部件的创建过程。

抽象工厂模式VS建造者模式

抽象工厂模式是对产品家族的创建,建造者模式是指按照指定的蓝图建造产品。

目录

1.1、单例模式

1.2、简单工厂模式

1.3、工厂方法模式

1.4、抽象工厂模式

1.5、原型模式

1.6、建造者模式


1.1、单例模式

单例模式是最简单的设计模式之一,属于创建者模式,提供了一种创建对象的最佳方式,类中只创建一个实例对象,并提供访问对象的方式。单例模式是指确保一个类在任何情况下都只有一个实例,并且提供一个访问该单例的全局访问点。

单例模式分为饿汉模式与懒汉模式两种。饿汉式是线程安全的,但是类加载的时候就创建实例对象,如果后期不使用,会造成内存的浪费。

饿汉模式:类加载的时候,实例对象使用之前,就创建了实例对象

懒汉模式:类加载的时候没创建,使用的时候,才创建实例对象

饿汉模式可以通过静态成员变量的方式创建实例,也可以通过静态代码块的方式创建实例。

/*** @author nuist__NJUPT* @ClassName Singleton* @description: 饿汉式-静态成员变量* @date 2024年01月19日*/
public class Singleton {// 1.私有构造方法,避免外界创建带参数的实例化对象private Singleton(){}// 2.在本类中创建本类的实例化对象private static Singleton instance  = new Singleton() ;// 3.提供一个访问该实例对象的方式,让外界访问public static Singleton getInstance(){return instance ;}}
/*** @author nuist__NJUPT* @ClassName Singleton1* @description: 饿汉模式-静态代码块* @date 2024年01月19日*/
public class Singleton1 {private Singleton1(){}private static Singleton1 instance ;{instance = new Singleton1() ;}public static Singleton1 getInstance() {return instance;}}

通过测试类可以发现创建的两个实例对象是同一个对象,即单例。

/*** @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) ;}
}

懒汉模式是类加载的时候不创建实例,在使用的时候才创建,需要使用双重检查锁的机制保证线程安全,即保证只创建一个实例。

/*** @author nuist__NJUPT* @ClassName Singleton* @description: 懒汉模式-双重检查锁方式* @date 2024年01月19日*/
public class Singleton {// 1.私有构造方法private Singleton(){}// 2.定义实例对象// jvm在实例化对象会进行优化和指令重排序操作,故使用双重检测琐在多线程可能会出现空指针问题// 使用volatile关键字可以保证可见性和有序性private static volatile Singleton instance ;//3.对外提供访问方式/*** 双重检查锁琐的方式保证线程安全* @return 实例对象*/public static Singleton getInstance() {// 如果instance不为空,不需要抢占琐,直接返回对象if(instance == null){synchronized (Singleton.class) {// 抢到琐之后再次判断是否为空if(instance == null){instance = new Singleton() ;}}}return instance;}
}

序列化与反序列化、以及反射的方式会破坏单例模式,需要进一步解决相应的问题。

JDK源码中Runtime类对象是单例模式,饿汉式的方式实现。

单例模式应用的场景一般发现在以下条件下:

  (1)资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如日志文件,应用配置。

  (2)控制资源的情况下,方便资源之间的互相通信。如线程池等。

日志系统:在应用程序中,通常只需要一个日志系统,以避免在多个地方创建多个日志对象,并降低资源消耗。
数据库连接池:在应用程序中,数据库连接池是一个非常重要的资源,单例模式可以确保在应用程序中只有一个数据库连接池实例,避免资源浪费。
配置文件管理器:在应用程序中,通常只需要一个配置文件管理器来管理应用程序的配置文件,单例模式可以确保在整个应用程序中只有一个配置文件管理器实例。
缓存系统:在应用程序中,缓存系统是一个重要的组件,单例模式可以确保在整个应用程序中只有一个缓存实例,以提高应用程序的性能。
GUI组件:在图形用户界面(GUI)开发中,单例模式可以确保在整个应用程序中只有一个GUI组件实例,以确保用户界面的一致性和稳定性。
 

1.2、简单工厂模式

简单工厂模式:简单工厂模式可以理解为一种编程习惯,简单工厂包含如下三个角色:

01.抽象产品:定义产品的规范,主要包括产品的功能与特性。

02.具体产品:实现或者继承抽象产品。

03.具体工厂:提供创建产品的方法,调用者通过该方法来获取产品。

优点:要实现新产品直接修改工厂类代码即可,避免客户端代码修改,更容易扩展。

缺点:增加新产品还是需要修改工厂类,在一定程度上违反了开闭原则。

1.我们看个案例,定义一个抽象产品咖啡,定义两个具体产品美式咖啡和拿铁咖啡。

/*** @author nuist__NJUPT* @ClassName Coffee* @description: 咖啡类-抽象产品* @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: 拿铁咖啡-具体产品* @date 2024年01月19日*/
public class LatteCoffee extends Coffee{@Overridepublic String getName() {return "拿铁咖啡";}
}
/*** @author nuist__NJUPT* @ClassName AmericanCoffee* @description: 美式咖啡-具体产品* @date 2024年01月19日*/
public class AmericanCoffee extends Coffee {@Overridepublic String getName() {return "美式咖啡";}}

2.定义一个coffee工厂进行咖啡生产,解耦咖啡店和具体咖啡的关系。

/*** @author nuist__NJUPT* @ClassName SimpleCoffeeFactory* @description: 简单coffee工厂* @date 2024年01月19日* @version: 1.0*/
public class SimpleCoffeeFactory {/*** 生产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.咖啡店直接调用简单咖啡工厂进行咖啡生产。这样咖啡店不需要直接与具体的咖啡交互,只需要和工厂交互,工厂完成具体的咖啡生产管理,可以实现模块之间的解耦。

/*** @author nuist__NJUPT* @ClassName CoffeeStore* @description: coffee店* @date 2024年01月19日*/
public class CoffeeStore {/*** 生产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.编写测试类进行测试。

/*** @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、工厂方法模式

工厂方法模式可以避免简单工厂模式违反开闭原则的问题。

工厂方法模式:定义一个用于创建对象的接口(工厂),让子类对象决定实例化哪个产品对象,使一个产品的实例化延迟到其工厂的子类。

工厂方法模式的主要角色如下:

01.抽象产品:定义产品的规范,主要包括产品的功能与特性。

02.具体产品:实现或者继承抽象产品。

03.具体工厂:实现抽象工厂中的方法,完成具体产品的创建。

04.抽象工厂:提供创建产品的接口,调用者通过抽象工厂访问具体工厂的工厂方法来创建产品。

优点:封装性比较好,只需知道具体工厂名字就可以得到产品,无需知道产品的创建过程。另外新增加产品的时候只需要增加抽象工厂的实现类即可,原抽象工厂不需要改动,满足开闭原则。

缺点:只能生产统一等级的产品,不支持生产多等级的产品

1.我们使用工厂方法模式改进上述的简单工厂模式,首先定义一个抽象产品咖啡和两个具体产品拿铁咖啡和美式咖啡类,与上面的一样。

2.然后我们定义抽象咖啡工厂接口与咖啡工厂接口的实现类(具体工厂)。

/*** @author nuist__NJUPT* @InterfaceName CoffeeFactory* @description: 抽象工厂接口* @date 2024年01月19日*/
public interface CoffeeFactory {// 创建咖啡对象Coffee createCoffee() ;
}
/*** @author nuist__NJUPT* @ClassName AmericanCoffeeFactory* @description: 抽象工厂实现类* @date 2024年01月19日*/
public class AmericanCoffeeFactory implements CoffeeFactory {@Overridepublic Coffee createCoffee() {return new AmericanCoffee();}
}
/*** @author nuist__NJUPT* @ClassName LatteCoffeeFactory* @description: 咖啡工厂实现类* @date 2024年01月19日*/
public class LatteCoffeeFactory implements CoffeeFactory{@Overridepublic 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;}/*** 点coffee方法* @return 咖啡*/public Coffee orderCoffee(){Coffee coffee = coffeeFactory.createCoffee();coffee.addMilk();coffee.addSugar();return coffee ;}
}

4.最后编写测试类测试点咖啡功能。


/*** @author nuist__NJUPT* @ClassName Client* @description: 测试类* @date 2024年01月19日*/
public class Client {public static void main(String[] args) {// 实例化咖啡店CoffeeStore coffeeStore = new CoffeeStore() ;// 面向抽象工厂接口实例化具体咖啡工厂CoffeeFactory coffeeFactory = new AmericanCoffeeFactory() ;// 在咖啡店注入抽象咖啡工厂coffeeStore.setCoffeeFactory(coffeeFactory);// 调用方法点咖啡Coffee coffee = coffeeStore.orderCoffee();System.out.println(coffee.getName());}
}
1.4、抽象工厂模式

工厂方法模式是用于生产同一等级的多种产品,对于不同等级的产品,使用抽象工厂生产。

抽象工厂模式:生产多等级的产品,将一个具体工厂生产的不同等级的产品的一组产品成为产品族。

抽象工厂模式的主要角色如下:

01.抽象产品:定义产品的规范,主要包括产品的功能与特性。

02.具体产品:实现或者继承抽象产品。

03.具体工厂:实现抽象工厂中的方法,完成具体产品的创建。

04.抽象工厂:提供创建产品的接口,包含多个创建产品的方法,可以创建多个不同等级的产品。

优点:当一个产品族中多个对象被设计在一起工作的时候,能保证客户端始终只使用同一个产品族的对象。

缺点:当产品族中需要增加一个产品的时候,所有的工厂类都需要修改。

Java的JDK源码的迭代器遍历集合用的就是抽象工厂方法。

我们下面看一个案例,还是咖啡的案例,不过这次加了甜品,属于同族的产品。

1.首先定义抽象产品咖啡和甜品,然后定义具体的拿铁咖啡和美式咖啡、以及具体的两种甜品。

/*** @author nuist__NJUPT* @ClassName Coffee* @description: 咖啡类-抽象产品* @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: 美式咖啡-具体产品* @date 2024年01月19日*/
public class AmericanCoffee extends Coffee {@Overridepublic String getName() {return "美式咖啡";}}
/*** @author nuist__NJUPT* @ClassName LatteCoffee* @description: 拿铁咖啡-具体产品* @date 2024年01月19日*/
public class LatteCoffee extends Coffee {@Overridepublic 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 {@Overridepublic void show() {System.out.println("抹茶慕斯");}
}
/*** @author nuist__NJUPT* @ClassName Trimisu* @description: 提拉米苏类* @date 2024年01月19日*/
public class Trimisu extends Dessert {@Overridepublic void show() {System.out.println("提拉米苏类");}
}

2.定义一个抽象工厂,用于生产同族产品。

/*** @author nuist__NJUPT* @InterfaceName DessertFactory* @description: 甜品工厂* @date 2024年01月19日*/
public interface DessertFactory {// 生产咖啡Coffee createCoffee() ;// 生产天甜品Dessert createDessert() ;}

3.定义抽象工厂具体的实现类,用于生产具体的同族产品。

/*** @author nuist__NJUPT* @ClassName AmericanDessertFactory* @description: 美式甜品工厂:即能生产美式咖啡,也能生产抹茶慕斯* @date 2024年01月19日*/
public class AmericanDessertFactory implements DessertFactory{@Overridepublic Coffee createCoffee() {return new AmericanCoffee();}@Overridepublic Dessert createDessert() {return new MatchaMousse();}
}
/*** @author nuist__NJUPT* @ClassName ItalyDessertFactory* @description: 意大利风味的甜品工厂* @date 2024年01月19日*/
public class ItalyDessertFactory implements DessertFactory {@Overridepublic Coffee createCoffee() {return new LatteCoffee();}@Overridepublic Dessert createDessert() {return new Trimisu();}
}

4.定义测试类,实例化工厂实现类,并生产调用方法生产同族产品。

/*** @author nuist__NJUPT* @ClassName Client* @description: 测试类* @date 2024年01月19日*/
public class Client {public static void main(String[] args) {// 创建抽象工厂对象// AmericanDessertFactory factory = new AmericanDessertFactory() ;ItalyDessertFactory factory = new ItalyDessertFactory() ;//生产咖啡和甜品等产品Coffee coffee = factory.createCoffee();Dessert dessert = factory.createDessert();System.out.println(coffee.getName());dessert.show();}
}
1.5、原型模式

原型模式:用一个已经创建的实例作为原型,通过复制该原型对象创建一个和该原型对象相同的对象。

原型模式包含如下角色:
01.抽象原型类:规定了具体原型对象必须实现的clone()方法

02.具体原型类:实现抽象原型类的clone()方法,它是可被复制的对象

03.访问类:使用具体原型类的clone()方法来复制新对象

深克隆:克隆基本类型数据与其地址

浅克隆:不克隆地址,只克隆基本数据

我们看一个原型模式的案例,同一个学校的三好学生奖状除了学生名字外其余都相同,我们可以使用原型模式复制多个三好学生的奖状,然后修改名字即可。

1.定义原型对象,实现Cloneable接口并重写clone()方法。

/*** @author nuist__NJUPT* @ClassName Citation* @description: 原型对象* @date 2024年01月19日*/
public class Citation implements Cloneable{// 三好学生的姓名private String name ;public String getName() {return name;}public void setName(String name) {this.name = name;}public void show(){System.out.println(name + "同学获得三好学生奖状");}@Overrideprotected Citation clone() throws CloneNotSupportedException {return (Citation) super.clone();}
}

2.定义测试类,实例化原型对象,并克隆原型对象,并调用原型对象的方法。

/*** @author nuist__NJUPT* @ClassName Client* @description: 测试类* @date 2024年01月19日*/
public class Client {public static void main(String[] args) throws CloneNotSupportedException {// 创建原型对象Citation citation = new Citation() ;// 克隆出来的原型对象// 对象的创建比较复杂的情况下,可以使用原型对象克隆,而不是创建对象Citation citation1 = citation.clone();citation.setName("张三");citation1.setName("李四");citation.show();citation1.show();}}

对于浅克隆克隆的对象还是同一个对象,对象的地址并没有改变,会导致一些问题,java可以通过序列化和反序列化的方式实现深克隆。

如下定义一个Student实体,定义一个原型类进行深克隆,定义测试类通过反序列化实现深克隆。

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;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +'}';}
}


import java.io.Serializable;/*** @author nuist__NJUPT* @ClassName Citation* @description: 原型对象* @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() + "同学获得三好学生奖状");}@Overrideprotected 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 {// 创建原型对象Citation citation = new Citation() ;Student student = new Student() ;student.setName("张三");citation.setStudent(student);// 克隆出来的原型对象// 对象的创建比较复杂的情况下,可以使用原型对象克隆,而不是创建对象// Citation citation1 = citation.clone();// 通过序列化与反序列化的方式进行深克隆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("李四");// 同一个student对象,浅克隆导致修改之后会覆盖之前的citation.show();citation1.show();}}
1.6、建造者模式

建造者模式:将复杂对象的构造与装配分离,就是将组件与整体进行分离,使得同样的装配组件的过程可以生产出不同的整体。

建造者模式包含如下几个角色:

01.抽象建造者类:该接口规定了实现复杂对象的那部分创建,并不涉及具体部件对象的创建。

02.具体建造者类:实现抽象类接口,完成复杂对象的具体创建,提供产品的实例。

03.产品类:要创建的复杂对象。

04.指挥者类:完成具体装配的过程,不涉及具体的产品信息,只保证对象各部分的完整创建。

建造者模式的优缺点如下:

优点:封装性较好,建造过程可以封装到具体建造者类中,产品与组装过程解耦,复杂的步骤可以分解在不同的方法中,易扩展,扩展的话只需要增加一个具体建造者类即可,满足开闭原则 。

缺点:如果创建的产品差异性较大不适合使用

建造者模式适用场景:适合于产品组件复杂但是建造过程的算法相对稳定。

1.下面我们通过共享单车构建过程的案例去进一步学习一下建造者模式。首先我们定义具体的产品类Bike,即需要构建的对象。

/*** @author nuist__NJUPT* @ClassName Bike* @description: 单车产品类* @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.下面定义抽象建造者类,在抽象建造者类中定义建造规则,并定义具体建造者类实现具体的规则。


/*** @author nuist__NJUPT* @ClassName Builder* @description: 抽象建造者类* @date 2024年01月20日*/
public abstract class Builder {// 声明Bike类型protected Bike bike = new Bike() ;// 构建车架public abstract void buildFrame() ;// 构建车座public abstract void buildSeat() ;// 构建自行车public abstract Bike createBike() ;}
/*** @author nuist__NJUPT* @ClassName MobileBuilder* @description: 具体的建造者-摩拜单车构建者* @date 2024年01月20日*/
public class MobileBuilder extends Builder {@Overridepublic void buildFrame() {bike.setFrame("碳纤维车架");}@Overridepublic void buildSeat() {bike.setSeat("假皮车座");}@Overridepublic Bike createBike() {return bike ;}
}
/*** @author nuist__NJUPT* @ClassName OfoBuilder* @description: Ofo单车建造者* @date 2024年01月20日*/
public class OfoBuilder extends Builder{@Overridepublic void buildFrame() {bike.setFrame("铝合金车架");}@Overridepublic void buildSeat() {bike.setSeat("牛皮车座");}@Overridepublic Bike createBike() {return bike;}
}

3.定义指挥者类,负责指导对象的建造过程,确保产品的创建。

/*** @author nuist__NJUPT* @ClassName Director* @description: 指挥者类* 指挥者类在建造者模式中负责指导具体建造者如何建造产品,* 控制好调用的先后顺序,并向调用者返回完整的产品类* 在某些场景中可以将指挥者类和抽象建造者类进行融合,也就是把construct方法放到Builder类中* @date 2024年01月20日*/
public class Director {// 注入建造者private Builder builder ;public Director(Builder builder) {this.builder = builder;}/***  构建单车方法* @return 单车*/public Bike construct(){builder.buildFrame();builder.buildSeat();Bike bike = builder.createBike();return bike ;}
}

4.最后定义测试类进行测试是否建造完成,实例化指挥者类并注入具体的建造者,指挥完成具体产品的建造。

/*** @author nuist__NJUPT* @ClassName Client* @description: 测试类* @date 2024年01月20日*/
public class Client {public static void main(String[] args) {// 创建指挥者对象Director director = new Director(new MobileBuilder()) ;// 指挥者指挥组装自行车Bike construct = director.construct();// 打印System.out.println(construct.getFrame() + construct.getSeat());}
}

我们通过下面一个案例看一下传统的构造器传参和利用建造者模式传参,建造者模式代码可读性更好,参数位置可以在客户端定义,不需要在构造器中写死,更灵活。

1.传统的构造器传参,如下:

/*** @author nuist__NJUPT* @ClassName Phone1* @description: 传统的构造传参* @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;}@Overridepublic String toString() {return "Phone1{" +"cpu='" + cpu + '\'' +", screen='" + screen + '\'' +", memory='" + memory + '\'' +", mainBoard='" + mainBoard + '\'' +'}';}}

2.使用建造者模式,利用指挥者控制传参顺序。

/*** @author nuist__NJUPT* @ClassName Phone* @description: 建造者模式改进的构造器* @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 ;}@Overridepublic 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.最后定义测试类进行测试,观察两种传参方式,可以发现建造者模式可以改变传参顺序,灵活性强,同时可读性也更高。

/*** @author nuist__NJUPT* @ClassName Client* @description: 测试类* @date 2024年01月20日*/
public class Client {public static void main(String[] args) {// 创建指挥者对象Phone phone = new Phone.Builder().cpu("英特尔CPU").memory("金士顿内存").mainBoard("联想主板").screen("液晶屏幕").build() ;System.out.println(phone);// 传统方法Phone1 phone1 = new Phone1("AMD的CPU", "三星显示屏", "金士顿内存", "华为主板") ;System.out.println(phone1);}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/417567.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HCIP-BGP实验3

实验步骤 配置IP地址 R1 [r1]int g0/0/0 [r1-GigabitEthernet0/0/0]ip add 12.1.1.1 24 [r1-GigabitEthernet0/0/0]int loopback0 [r1-LoopBack0]ip add 192.168.1.1 24 [r1-LoopBack0]int loopback1 [r1-LoopBack1]ip add 192.168.2.1 24 [r1-LoopBack1]int loopback3 [r1-…

一键操作,批量复制文件夹名称 - 提升效率,从这里开始!

在日常生活和工作中,我们经常需要处理大量的文件夹,有时候需要将它们的名称复制到其他地方。但是,手动一个个复制不仅效率低下,还容易出错。为了解决这个问题,我们推出了一款实用的软件,它能帮助您批量复制…

【Linux】grub命令行引导进入系统

文章目录 1.grub命令行界面2.设置启动目录3.chainloader加载windows启动文件4.启动5.grub命令行无响应办法 在卸载Linux系统后,有的小白可能会忘记删除Linux的EFI引导。这样的话,下次开机时就会自动进入grub的命令行,连windows系统都进不去了…

VS2019+QT5.12.12+opencv+pcl1.12.1 显示点云,运行程序报错-无法定位程序输入点于链接库 如何解决?

之前配置好VS2019QTopencv,做了简单视觉软件,实现了部分功能。昨天下载PCL1.12.1,并参考很多博友的文档配置pcl,编写点云显示代码,程序编译成功,但执行一直报错。 VC目录配置: 添加PCL各个目录下的Lib名…

全球 TOP 20 免费恢复删除的文件/照片的数据恢复软件

如今几乎一切都是数字化的。大多数人选择以数字方式存储所有重要文件、图片和其他数据,因为纯粹是为了方便。虽然数字存储使存储大量数据变得很方便,但它也面临着自己的挑战。 意外删除文件就像将它们存储在硬盘、SD 卡或 USB 驱动器上一样简单。这就是…

计算机导论10-软件与软件工程

文章目录 软件软件的概念软件的定义软件的特征 软件的保护与授权软件的法律保护软件许可 软件工程软件危机软件危机的概念产生软件危机的原因 软件工程的概念软件工程的定义软件工程基本原理软件工程框架软件工程三要素 软件工程方法学软件工程方法软件工程工具软件工程过程 软…

ASP.NET Core列表增删改查

前置要求&#xff1a; 1、 vueelement-plus实现前端静态页面 HelloWorld.vue(src->view) <template><h2>hello界面</h2><div class"tableList"><!-- 搜索框 --><el-row :gutter"20"><el-col :span"8"…

【Linux】磁盘结构 | 文件系统 | 软硬链接

文件的状态有被打开和没有被打开&#xff0c;之前谈到一个文件被进行读写&#xff0c;就要打开加载到内存中&#xff0c;通过对应的系统调用&#xff0c;fd文件描述符的管理&#xff0c;write和read等函数的增删查改。并且借助缓冲区对文件属性和内容的修改。 大部分文件是没有…

SpringAOP以及事务管理和优化处理

Spring的AOP 本篇章中所有的代码都将会放置到git仓库中去&#xff0c;并且会做一个简要的说明。 一、个人理解描述 Spring中所谓的AOP就是在不修改源码的情况下&#xff0c;来进行增强。所谓的增强其实就是在方法执行前后添加一些额外操作。 所谓的增强&#xff0c;就是我们…

Ubuntu使用docker-compose安装chatGPT

ubuntu环境搭建专栏&#x1f517;点击跳转 Ubuntu系统环境搭建&#xff08;十五&#xff09;——使用docker-compose安装chatGPT Welcome to the AI era! 使用docker compose安装 在/usr/local文件夹下创建chatgpt mkdir chatgpt创建docker-compose.yaml vim docker-compos…

什么是OSPF?为什么需要OSPF?OSPF基础概念

什么是OSPF&#xff1f; 开放式最短路径优先OSPF&#xff08;Open Shortest Path First&#xff09;是IETF组织开发的一个基于链路状态的内部网关协议&#xff08;Interior Gateway Protocol&#xff09;。 目前针对IPv4协议使用的是OSPF Version 2&#xff08;RFC2328&#x…

数据库(MySQL库表操作)

目录 1.1 SQL语句基础&#xff08;SQL命令&#xff09; 1.1.1 SQL的简介 1.1.2 SQL语句的分类 1.1.3 SQL语句的书写规范 1.2 数据库操作 1.2.1 查看 1.2.2 自建库 1.2.3 切换数据库 1.2.4 删库 1.3 MySQL字符集 1.3.1 MySQL字符集包括&#xff1a; 1.3.2 utf8 和 u…