联系我们
简单又实用的WordPress网站制作教学
当前位置:网站首页 > 程序开发学习 > 正文

建造者模式

作者:访客发布时间:2023-10-24分类:程序开发学习浏览:118


导读:一、什么是建造者模式建造者模式(构建器模式)是一种创建型设计模式,它可以将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的展示。建造者模式可以让用户在不知道内部构...

一、什么是建造者模式

建造者模式(构建器模式)是一种创建型设计模式,它可以将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的展示。建造者模式可以让用户在不知道内部构建细节的情况下,更精细地控制对象的构造流程.

建造者模式的主要角色有:

  • 产品(产品):要创建的复杂对象,通常由多个部件组成。
  • 抽象建造者(构建商):定义了产品的创建过程和各个部件的构造方法,一般是一个接口或抽象类。
  • 具体建造者(混凝土建造商):实现了抽象建造者的接口,提供具体的构造方法和返回产品的方法。
  • 指挥者(导演):负责调用合适的建造者来组合产品,一般只有一个实例。

建造者模式

二、安卓源码中的实例

安卓开发中经常使用到建造者模式,例如警报对话框、通知、StringBuilder等类都是采用了建造者模式来创建对象。下面以警报对话框为例,分析其源码实现。

警报对话框是一个常用的对话框类,它可以显示标题、消息、图标、按钮等组件,也可以自定义布局。警报对话框的构造方法是私有的,不能直接创建对象,而是通过生成器类来构建。Builder类是警报对话框的一个静态内部类,它实现了抽象建造者的角色,提供了各种设置对话框属性的方法,例如设置标题、设置消息、设置图标、设置位置按钮等。这些方法都返回生成器对象本身,实现了链式调用。Builder类还提供了一个创建方法,用于返回一个警报对话框对象。这个方法会调用警报对话框的私有构造方法,并将生成器对象作为参数传入。在警报对话框的构造方法中,会根据生成器对象的属性来设置对话框的各个组件,并创建一个警报控制器对象来管理对话框的显示和交互。警报控制器类相当于指挥者的角色,它负责调用合适的生成器方法来组合对话框。

下面是警报对话框和生成器类的部分源码:

public class AlertDialog extends Dialog implements DialogInterface {
    // 省略其他代码

    // 私有构造方法
    protected AlertDialog(Context context, int themeResId) {
        super(context, resolveDialogTheme(context, themeResId));
        mWindow.alwaysReadCloseOnTouchAttr();
        mAlert = new AlertController(getContext(), this, getWindow());
    }

    // 静态内部类Builder
    public static class Builder {
        // 产品对象
        private final AlertController.AlertParams P;
        // 主题资源ID
        private int mTheme;

        // 构造方法
        public Builder(Context context) {
            this(context, resolveDialogTheme(context, 0));
        }

        // 构造方法
        public Builder(Context context, int themeResId) {
            P = new AlertController.AlertParams(new ContextThemeWrapper(
                    context, resolveDialogTheme(context, themeResId)));
            mTheme = themeResId;
        }

        // 设置标题
        public Builder setTitle(@StringRes int titleId) {
            P.mTitle = P.mContext.getText(titleId);
            return this;
        }

        // 设置标题
        public Builder setTitle(CharSequence title) {
            P.mTitle = title;
            return this;
        }

        // 省略其他设置方法

        // 创建对话框对象
        public AlertDialog create() {
            // Context has already been wrapped with the appropriate theme.
            final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
            P.apply(dialog.mAlert);
            dialog.setCancelable(P.mCancelable);
            if (P.mCancelable) {
                dialog.setCanceledOnTouchOutside(true);
            }
            dialog.setOnCancelListener(P.mOnCancelListener);
            dialog.setOnDismissListener(P.mOnDismissListener);
            if (P.mOnKeyListener != null) {
                dialog.setOnKeyListener(P.mOnKeyListener);
            }
            return dialog;
        }
    }
}

使用生成器类创建警报对话框对象的示例代码如下:

AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("提示")
       .setMessage("确定要退出吗?")
       .setIcon(R.drawable.ic_launcher)
       .setPositiveButton("确定", new DialogInterface.OnClickListener() {
           @Override
           public void onClick(DialogInterface dialog, int which) {
               finish();
           }
       })
       .setNegativeButton("取消", null)
       .create()
       .show();

从上面的代码可以看出,使用建造者模式可以让用户更灵活地构建对话框对象,而不需要关心对话框的内部实现细节.

三、Kotlin实现建造者模式

下面以一个电脑类为例,举例如何用kotlin实现建造者模式.

我们要根据不同的配置来构建一个电脑类的实例,希望可以自由配置电脑处理器、内存、显示器、键盘以及USB端口,从而组装出不同的Computer实例。如果使用JAVA来实现的话,最好的选择就是使用构建者模式。但是如果使用kotlin来实现的话,有一个更简单的方法,那就是使用命名参数和默认参数.

命名参数是指在调用函数时,可以通过参数名来指定参数值,而不需要按照参数顺序来传递.默认参数是指在定义函数时,可以给参数指定一个默认值,这样在调用函数时,如果没有传递该参数,就会使用默认值.利用这两个特性,我们可以直接在电脑类的主构造函数中定义所有的属性,并给它们赋予默认值.然后在创建电脑对象时,只需要传递需要修改的属性即可.这样就避免了定义一个额外的生成器类,并且代码更加简洁。

下面是电脑类的Kotlin实现:

class Computer(
    val cpu: String = "Intel Core i5",
    val ram: Int = 8,
    val monitor: String = "Dell 24寸",
    val keyboard: String = "罗技无线键盘",
    val usb: Int = 4
) {
    override fun toString(): String {
        return "Computer(cpu='$cpu', ram=$ram, monitor='$monitor', keyboard='$keyboard', usb=$usb)"
    }
}

创建电脑对象的示例代码如下:

val computer1 = Computer()
println(computer1)

val computer2 = Computer(cpu = "AMD Ryzen 7", ram = 16, monitor = "华硕 27```markdown
寸\") .build()
println(computer2)

val computer3 = Computer(keyboard = "雷柏机械键盘", usb = 6) .build()
println(computer3)

输出结果如下:

Computer(cpu='Intel Core i5', ram=8, monitor='Dell 24寸', keyboard='罗技无线键盘', usb=4)
Computer(cpu='AMD Ryzen 7', ram=16, monitor='华硕 27寸', keyboard='罗技无线键盘', usb=4)
Computer(cpu='Intel Core i5', ram=8, monitor='Dell 24寸', keyboard='雷柏机械键盘', usb=6)

从上面的代码可以看出,使用Kotlin的命名参数和默认参数,可以非常方便地实现建造者模式,而不需要定义额外的Builder类.当然,这种方法也有一些局限性,例如不能保证对象的不可变性,不能控制对象的创建顺序,不能实现复杂的构造逻辑等.因此,在实际开发中,需要根据具体的需求和场景来选择合适的方法.

四、JAVA实现建造者模式

下面以一个计算机类为例,举例如何用JAVA实现建造者模式。

我们要根据不同的配置来构建一个计算机类的实例,希望可以自由配置计算机、内存、硬盘、显卡、主板、电源等部件,从而组装出不同的Computer实例。如果使用JAVA的构造方法或者设置器方法来实现的话,会有以下几个问题:

  • 如果使用构造方法,参数过多时会导致代码可读性和可维护性降低,而且无法灵活地指定某些可选参数.
  • 如果使用Setter方法,对象会产生不一致的状态,而且对象是可变的,不利于保证线程安全性和封装性。

因此,使用建造者模式可以解决这些问题,让用户更方便地构建复杂对象.具体步骤如下:

  • 定义一个计算机类(产品),包含各种属性,并提供一个私有的构造方法和一个静态内部类生成器。
  • 定义一个建筑商类(混凝土建筑商),包含与产品类相同的属性,并提供一个公有的构造方法和各种设置属性的方法,这些方法都返回建筑商对象本身,实现链式调用。Builder类还提供了一个Build方法,用于返回一个产品对象。这个方法会调用产品类的私有构造方法,并将构建器对象作为参数传入。
  • 在产品类的私有构造方法中,根据构建器对象的属性来初始化自身的属性。

下面是计算机类和生成器类的部分源码:

public class Computer {
    // 必选属性
    private String cpu;
    private String ram;
    // 可选属性
    private String hardDisk;
    private String keyboard;
    private String mouse;

    // 私有构造方法
    private Computer(Builder builder) {
        this.cpu = builder.cpu;
        this.ram = builder.ram;
        this.hardDisk = builder.hardDisk;
        this.keyboard = builder.keyboard;
        this.mouse = builder.mouse;
    }

    // 静态内部类Builder
    public static class Builder {
        // 必选属性
        private String cpu;
        private String ram;
        // 可选属性
        private String hardDisk;
        private String keyboard;
        private String mouse;

        // 公有构造方法
        public Builder(String cpu, String ram) {
            this.cpu = cpu;
            this.ram = ram;
        }

        // 设置硬盘
        public Builder setHardDisk(String hardDisk) {
            this.hardDisk = hardDisk;
            return this;
        }

        // 设置键盘
        public Builder setKeyboard(String keyboard) {
            this.keyboard = keyboard;
            return this;
        }

        // 设置鼠标
        public Builder setMouse(String mouse) {
            this.mouse = mouse;
            return this;
        }

        // 返回产品对象
        public Computer build() {
            return new Computer(this);
        }
    }
}

创建计算机对象的示例代码如下:

Computer computer1 = new Computer.Builder("Intel Core i7", 16).build();
System.out.println(computer1);

Computer computer2 = new Computer.Builder("AMD Ryzen 9", 32)
                    .setHardDisk("Samsung SSD 1TB")
                    .setKeyboard("Logitech G915")
                    .setMouse("Logitech G502")
                    .build();
System.out.println(computer2);

输出结果如下:

Computer{cpu='Intel Core i7', ram=16, hardDisk='null', keyboard='null', mouse='null'}
Computer{cpu='AMD Ryzen 9', ram=32, hardDisk='Samsung SSD 1TB', keyboard='Logitech G915', mouse='Logitech G502'}

从上面的代码可以看出,使用建造者模式可以让用户更清晰地构建复杂对象,而不需要关心对象的内部实现细节.同时,使用生成器类可以保证对象的不可变性和线程安全性,以及对象创建过程的顺序性和逻辑性。

五、优缺点和使用场景

语言优点缺点使用场景
科特林代码简洁,调用方便,实现灵活对象可变,不能保证线程安全,不能控制创建顺序和逻辑需要创建简单、灵活、可变的对象
爪哇对象不可变,可以保证线程安全,可以控制创建顺序和逻辑代码冗长,调用繁琐,实现固定需要创建复杂、固定、不可变的对象

#kotlin的优点:

  • 代码更简洁,不需要定义额外的生成器类。
  • 调用更方便,可以通过命名参数来指定需要修改的属性,而不需要按照顺序来传递参数.
  • 实现更灵活,可以根据需要给属性赋予默认值,也可以在主构造函数中添加逻辑判断或者初始化代码.

#kotlin的缺点:

  • 对象是可变的,不能保证对象的不可变性和线程安全性.
  • 不能控制对象的创建顺序,如果对象的属性之间有依赖关系,可能会导致错误或异常.
  • 不能实现复杂的构造逻辑,如果对象的创建过程涉及到多个步骤或者条件判断,可能会导致代码冗长或者混乱.

#kotlin适用于以下场景:

  • 需要创建的对象比较简单,没有复杂的内部结构或者依赖关系.
  • 需要创建的对象的属性比较多,但是大部分都是可选的或者有默认值.
  • 需要创建的对象不需要保证不可变性或者线程安全性.

#JAVA的优点:

  • 对象是不可变的,可以保证对象的不可变性和线程安全性.
  • 可以控制对象的创建顺序,如果对象的属性之间有依赖关系,可以通过生成器类来保证正确的构造过程。
  • 可以实现复杂的构造逻辑,如果对象的创建过程涉及到多个步骤或者条件判断,可以通过生成器类来封装和隐藏这些细节。

#JAVA的缺点:

  • 代码更冗长,需要定义一个额外的生成器类来封装对象的构造过程。
  • 调用更繁琐,需要按照顺序来传递参数,而且不能通过参数名来指定参数值.
  • 实现更固定,不能根据需要给属性赋予默认值,也不能在主构造函数中添加逻辑判断或者初始化代码.

#JAVA适用于以下场景:

  • 需要创建的对象比较复杂,有复杂的内部结构或者依赖关系.
  • 需要创建的对象的属性比较少,但是大部分都是必选的或者没有默认值.
  • 需要创建的对象需要保证不可变性或者线程安全性.

##六、与其他相似设计模式对比

设计模式相似之处不同之处使用场景
建造者模式都是创建型模式,都可以将对象的创建过程封装起来,对客户端隐藏细节建造者模式有指导者这个角色,直接返回一个组装好的复杂产品,而其他创建型模式返回的是单一或相关的产品,建造者模式更关注产品的构造过程和表示,而其他创建型模式更关注产品的创建建造者模式适用于创建复杂对象的情况,特别是当对象的构造过程涉及到多个步骤或者条件判断时
工厂方法模式都是创建型模式,都可以将对象的创建过程封装起来,对客户端隐藏细节工厂方法模式只关注对象的创建,而建造者模式关注对象的构造过程和表示,工厂方法模式只有一个抽象产品类,而建造者模式有多个部件类工厂方法模式适用于创建单一产品的情况,建造者模式适用于创建复杂产品的情况
抽象工厂模式都是创建型模式,都可以将对象的创建过程封装起来,对客户端隐藏细节抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族,而建造者模式返回一个组装好的复杂产品,抽象工厂模式没有指导者这个角色抽象工厂模式适用于创建多个产品族的情况,建造者模式适用于创建复杂产品的情况
原型模式都是创建型模式,都可以将对象的创建过程封装起来,对客户端隐藏细节原型模式通过复制一个现有的对象生成新对象,而建造者模式通过调用不同的方法生成新对象,原型模式不需要指导者和抽象建造者这两个角色原型模式适用于创建重复或相似的对象的情况,建造者模式适用于创建复杂对象的情况

标签:模式


程序开发学习排行
最近发表
网站分类
标签列表