软件设计模式是前辈们代码设计经验的总结,可以反复使用。设计模式共分为3大类,创建者模式(6种)、结构型模式(7种)、行为型模式(11种),一共24种设计模式,软件设计一般需要满足7大基本原则。下面通过5章的学习一起来看看设计模式的魅力吧。
创建型模式(6种):本质上就是将对象的创建与使用分离,就是描述怎样去创建对象。
包括:单例、简单工厂、工厂方法、抽象工厂、原型、建造者 模式
创建型模式的对比:
工厂方法模式VS建造者模式
工厂方法模式更注重对象的创建方式,建造者模式更注重部件的创建过程。
抽象工厂模式VS建造者模式
抽象工厂模式是对产品家族的创建,建造者模式是指按照指定的蓝图建造产品。
目录
单例模式是最简单的设计模式之一,属于创建者模式,提供了一种创建对象的最佳方式,类中只创建一个实例对象,并提供访问对象的方式。单例模式是指确保一个类在任何情况下都只有一个实例,并且提供一个访问该单例的全局访问点。
单例模式分为饿汉模式与懒汉模式两种。饿汉式是线程安全的,但是类加载的时候就创建实例对象,如果后期不使用,会造成内存的浪费。
饿汉模式:类加载的时候,实例对象使用之前,就创建了实例对象
懒汉模式:类加载的时候没创建,使用的时候,才创建实例对象
饿汉模式可以通过静态成员变量的方式创建实例,也可以通过静态代码块的方式创建实例。
/**
* @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组件实例,以确保用户界面的一致性和稳定性。
?
简单工厂模式:简单工厂模式可以理解为一种编程习惯,简单工厂包含如下三个角色:
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{
@Override
public String getName() {
return "拿铁咖啡";
}
}
/**
* @author nuist__NJUPT
* @ClassName AmericanCoffee
* @description: 美式咖啡-具体产品
* @date 2024年01月19日
*/
public class AmericanCoffee extends Coffee {
@Override
public 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());
}
}
工厂方法模式可以避免简单工厂模式违反开闭原则的问题。
工厂方法模式:定义一个用于创建对象的接口(工厂),让子类对象决定实例化哪个产品对象,使一个产品的实例化延迟到其工厂的子类。
工厂方法模式的主要角色如下:
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 {
@Override
public Coffee createCoffee() {
return new AmericanCoffee();
}
}
/**
* @author nuist__NJUPT
* @ClassName LatteCoffeeFactory
* @description: 咖啡工厂实现类
* @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;
}
/**
* 点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());
}
}
工厂方法模式是用于生产同一等级的多种产品,对于不同等级的产品,使用抽象工厂生产。
抽象工厂模式:生产多等级的产品,将一个具体工厂生产的不同等级的产品的一组产品成为产品族。
抽象工厂模式的主要角色如下:
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 {
@Override
public String getName() {
return "美式咖啡";
}
}
/**
* @author nuist__NJUPT
* @ClassName LatteCoffee
* @description: 拿铁咖啡-具体产品
* @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.定义一个抽象工厂,用于生产同族产品。
/**
* @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{
@Override
public Coffee createCoffee() {
return new AmericanCoffee();
}
@Override
public Dessert createDessert() {
return new MatchaMousse();
}
}
/**
* @author nuist__NJUPT
* @ClassName ItalyDessertFactory
* @description: 意大利风味的甜品工厂
* @date 2024年01月19日
*/
public class ItalyDessertFactory implements DessertFactory {
@Override
public Coffee createCoffee() {
return new LatteCoffee();
}
@Override
public 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();
}
}
原型模式:用一个已经创建的实例作为原型,通过复制该原型对象创建一个和该原型对象相同的对象。
原型模式包含如下角色:
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 + "同学获得三好学生奖状");
}
@Override
protected 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;
}
@Override
public 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() + "同学获得三好学生奖状");
}
@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 {
// 创建原型对象
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();
}
}
建造者模式:将复杂对象的构造与装配分离,就是将组件与整体进行分离,使得同样的装配组件的过程可以生产出不同的整体。
建造者模式包含如下几个角色:
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 {
@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.定义指挥者类,负责指导对象的建造过程,确保产品的创建。
/**
* @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;
}
@Override
public 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 ;
}
@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.最后定义测试类进行测试,观察两种传参方式,可以发现建造者模式可以改变传参顺序,灵活性强,同时可读性也更高。
/**
* @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);
}
}