【再探】设计模式—抽象工厂及建造者模式

news2025/1/8 4:53:23

 抽象工厂模式和建造者模式都属于创建型模式。两者都能创建对应的对象,而创建者模式更侧重于创建复杂对象,将对象的创建过程封装起来,让客户端不需要知道对象的内部细节。

1 抽象工厂模式

需求:

  1. 在使用工厂方法模式时,当增加新的产品类型时,需要创建对应的Factory类,这导致类的数量增加,让系统变得臃肿。
  2. 需要保证同风格下生成一致的组件。比如在写页面时,有dark及light模式,我们希望button、text等组件能在不同模式下保持一致的风格。抽象工厂模式介绍

1.1 抽象工厂模式介绍

为创建一组对象提供一组解决方案。与工厂模式相比,抽象工厂模式中的具体工厂不只是创建一种产品,而是创建一族产品。

图 抽象工厂模式UML

public class Button {

    private final String color;

    public Button(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return getClass().getSimpleName() + "{" +
                "color='" + color + '\'' +
                '}';
    }
}

public class Text {

    private final String color;

    public Text(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return getClass().getSimpleName() + "{" +
                "color='" + color + '\'' +
                '}';
    }
}

public class DarkButton extends Button{
    public DarkButton(String color) {
        super(color);
    }
}

public class DarkText extends Text{
    public DarkText(String color) {
        super(color);
    }
}

public class LightButton extends Button{
    public LightButton(String color) {
        super(color);
    }
}

public class LightText extends Text{
    public LightText(String color) {
        super(color);
    }
}
public interface ComponentsFactory {

    Button buildButton();

    Text buildText();

}

public class DarkComponentFactory implements ComponentsFactory{

    private final String color = "dark";

    @Override
    public Button buildButton() {
        return new DarkButton(color);
    }

    @Override
    public Text buildText() {
        return new DarkText(color);
    }

}

public class LightComponentFactory implements ComponentsFactory{

    private final String color = "light";

    @Override
    public Button buildButton() {
        return new LightButton(color);
    }

    @Override
    public Text buildText() {
        return new LightText(color);
    }
}

 

public class HtmlWeb {
    public static void main(String[] args) {
        ComponentsFactory componentsFactory = new DarkComponentFactory();
        System.out.println("-----------dark模式-----------");
        System.out.println(componentsFactory.buildButton());
        System.out.println(componentsFactory.buildText());
        System.out.println("-----------light模式-----------");
        componentsFactory = new LightComponentFactory();
        System.out.println(componentsFactory.buildButton());
        System.out.println(componentsFactory.buildText());
    }
}

1.1.1 优缺点

优点:

  1. 隔离类的实例化过程,使得客户端并不需要知道如何创建对象。
  2. 当不同产品族多个对象被设计成一起工作时,能保证客户端始终只使用同一个产品族的对象。
  3. 减少了工厂类的数量。
  4. 增加新的产品族方便,符合开闭原则。

缺点:

  1. 不符合单一职责原则,一个类创建了多个对象。
  2. 族群增加新的种类时,不符合开闭原则,需要修改全部的工厂类。

2 建造者模式

需求:

  1. 需要创建一个复杂的对象,隔离类的实例化过程,使得客户端不需要知道对象的内部细节。
  2. 创建对象时,对执行顺序有要求。

2.1 建造者模式介绍

将一个复杂对象的构建与它的表示分离,使得同样的构建过程,不同的构建顺序可以构建不同的表示。

图 构建模式UML

Director 为指挥者,用来控制Buider 的执行顺序,在实际开发中,这个角色经常会被省略,它的功能集成到Builder上;而Builder中的builderPart方法的返回值为它本身,这样就可以链式调用了。

public class JdbcConnector {

    private JdbcConnection connection;

    public void initConnection(URI uri) {
         connection = new JdbcConnection(uri);
    }

    public JdbcPreparedStatement getPreparedStatement(String query) {
        if (connection == null) throw new RuntimeException("连接器还未初始化");
        return connection.getPreparedStatement(query);
    }

    public static class JdbcConnection {
        public JdbcConnection(URI uri){
            System.out.println(uri);
        }

        public JdbcPreparedStatement getPreparedStatement(String query) {
            return new JdbcPreparedStatement(query);
        }
    }

    public static class JdbcPreparedStatement {
        String query;

        public JdbcPreparedStatement(String query) {
            this.query = query;
        }

        public String getResult() {
            return this.query;
        }
    }

}

public class JdbcConnectorBuilder {

    private String scheme;
    private String host;
    private String port;
    private String database;
    private String username;
    private String password;

    public JdbcConnectorBuilder setScheme(String scheme) {
        this.scheme = scheme;
        return this;
    }

    public JdbcConnectorBuilder setHost(String host) {
        this.host = host;
        return this;
    }

    public JdbcConnectorBuilder setPort(String port) {
        this.port = port;
        return this;
    }

    public JdbcConnectorBuilder setDatabase(String database) {
        this.database = database;
        return this;
    }

    public JdbcConnectorBuilder setUsername(String username) {
        this.username = username;
        return this;
    }

    public JdbcConnectorBuilder setPassword(String password) {
        this.password = password;
        return this;
    }

    /**
     * 产品
     */
    private JdbcConnector jdbcConnector = null;

    public JdbcConnectorBuilder() {
        jdbcConnector = new JdbcConnector();
    }

    public JdbcConnector build() {
        StringBuilder sb = new StringBuilder("jdbc:");
        sb.append(scheme == null ? "mysql" : scheme).append("://");
        if (host == null) throw new RuntimeException("host不能为空");
        sb.append(host);
        sb.append(":").append(port == null ? "3306" : port);
        if (database == null) throw new RuntimeException("数据库不能为空");
        sb.append("/").append(database);
        try {
            URI uri = new URI(sb.toString());
            if (username == null || password == null) throw new RuntimeException("账号或密码错误");
            jdbcConnector.initConnection(uri);
        } catch (URISyntaxException e) {
            throw new RuntimeException("连接失败,连接信息有误");
        }
        return jdbcConnector;
    }

}

public class JdbcConnectionTest {
    public static void main(String[] args) {
        JdbcConnectorBuilder builder = new JdbcConnectorBuilder();
        builder.setScheme("mysql")
                .setHost("localhost")
                .setPort("3307")
                .setDatabase("my-database")
                .setUsername("root")
                .setPassword("123456");
        JdbcConnector jdbcConnector = builder.build();
        JdbcConnector.JdbcPreparedStatement statement = jdbcConnector.getPreparedStatement("select * from student");
        System.out.println(statement.getResult());
    }
}

JDK 中的StringBuilder及Apache httpclient 的 URIBuilder 应用了建造者模式。

自定义StringBuilder

public class CustomStringBuilder {

    public static void main(String[] args) {
        CustomStringBuilder customStringBuilder = new CustomStringBuilder("hello customStringBuilder");
        for (int i =0; i < 100; i++) {
            customStringBuilder.append("---建造者模式---").append(i + "");
        }
        System.out.println(customStringBuilder);
    }

    char[] value;

    int count = 0;

    public CustomStringBuilder(int capacity) {
        value = new char[capacity];
    }

    public CustomStringBuilder(String str) {
        this(str.length() + 16);
        append(str);
    }

    CustomStringBuilder append(String str) {
        if (str == null) {
            throw new RuntimeException("字符串不能为空");
        }
        ensureCapacity(count + str.length());
        str.getChars(0,str.length(),value,count);
        count += str.length();
        return this;
    }

    private void ensureCapacity(int minCapacity) {
        if (minCapacity >= value.length) {
            value = Arrays.copyOf(value,value.length << 1);
        }
    }

    @Override
    public String toString() {
        return new String(value,0,count);
    }
}

自定义URIBuilder

public class CustomURIBuilder {

    public static void main(String[] args) throws URISyntaxException {
        CustomURIBuilder customURIBuilder = new CustomURIBuilder();
        customURIBuilder.setHost("localhost").setPort("8080").setEncodedPath("/userinfo");
        customURIBuilder.appendQueryParam("username","hmf");
        customURIBuilder.appendQueryParam("status","1");
        customURIBuilder.appendQueryParam("createDate","2024/04/20");
        URI uri = customURIBuilder.build();
        System.out.println(uri);
    }

    private String scheme;

    private String host;

    private String port;

    private String encodedPath;

    private List<NameValuePair> queryParams;

    public CustomURIBuilder setScheme(String scheme) {
        this.scheme = scheme;
        return this;
    }

    public CustomURIBuilder setHost(String host) {
        this.host = host;
        return this;
    }

    public CustomURIBuilder setPort(String port) {
        this.port = port;
        return this;
    }

    public CustomURIBuilder setEncodedPath(String encodedPath) {
        this.encodedPath = encodedPath;
        return this;
    }

    public CustomURIBuilder appendQueryParam(String name,String value) {
        if (queryParams == null) queryParams = new ArrayList<>();
        queryParams.add(new NameValuePair(name,value));
        return this;
    }

    public URI build() throws URISyntaxException {
        return new URI(buildStr());
    }

    public String buildStr() {
        StringBuilder sb = new StringBuilder();
        if (this.scheme != null) sb.append(this.scheme).append("://");
        if (this.host == null) throw new RuntimeException("host不能为空");
        sb.append(this.host);
        if (this.port != null) sb.append(":").append(this.port);
        if (this.encodedPath != null) sb.append(this.encodedPath);
        if (this.queryParams != null) {
            for (int i = 0; i < this.queryParams.size(); i++) {
                sb.append(i == 0 ? "?" : "&").append(this.queryParams.get(i));
            }
        }
        return sb.toString();
    }

    private static class NameValuePair {
        String name;
        String value;

        public NameValuePair(String name, String value) {
            this.name = name;
            this.value = value;
        }

        @Override
        public String toString() {
            if (this.value == null) return name;
            return name + "=" + value;
        }
    }

}

2.1.1 优缺点

优点:

  1. 封装性好,将产品的内部表示与产品的生成过程分割开。客户端只需知道所需的产品类型,而不需要知道产品内部的具体结构和实现细节。
  2. 扩展性好,各个具体建造者相互独立,如果需要添加新的产品,只需创建对应的建造者即可,符合开闭原则。
  3. 便于构建发展对象,可以简化对象的创建过程,提高代码的可读性和可维护性。

缺点:

  1. 可维护性差,如果产品的内部发生变化,则对应的创建者可能需要修改的地方会比较多。
  2. 类的数量增加。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Gateway Predicate断言(谓词)

是什么 Spring Cloud Gateway匹配路由作为Spring WebFlux HandlerMapping基础设施的一部分。 Spring Cloud Gateway包含许多内置的路由谓词工厂。 所有这些谓词都匹配HTTP请求的不同属性。 您可以使用逻辑 and 语句来联合收割机组合多个路由谓词工厂。 Predicate就是为了实现一…

git 配置相关

问题一&#xff1a;ssh-keygen -t ed25519 -C "Gitee SSH Key" 这个命令中的 ed25519 字符是什么意思&#xff1f; ssh-keygen 是一个用于生成SSH密钥的工具&#xff0c;SSH&#xff08;Secure Shell&#xff09;是一种网络协议&#xff0c;用于加密方式远程登录和其…

【JAVA进阶篇教学】第九篇:MyBatis-Plus用法介绍

博主打算从0-1讲解下java进阶篇教学&#xff0c;今天教学第九篇&#xff1a;MyBatis-Plus用法介绍。 在 MyBatis-Plus 3.5.0 中&#xff0c;LambdaQueryWrapper支持多种条件构造方式&#xff0c;除了等于&#xff08;eq&#xff09;、不等于&#xff08;ne&#xff09;、大于&a…

SQL dialect is not configured. Apache Cassandra matches best.没有配置SQL方言 如何处理

我这里是MySQL语言,所以我设置MySQL dialect 写个记录,之后更换全局SQL语言再换 下图是设置

Vue通过下拉框选择字典值,并将对应的label以及value值提交到后端

产品品种从字典中获取 产品性质也是从字典中获取 字典当中的保存 dict_type表 dict_data表 在表单提交的方法中 1.因为做的产品性质是多选&#xff0c;它会以数组的方式提交&#xff0c;所以需要先将Json格式转变为String JSON.stringify(this.form.nature) 2.提交表单&…

汽车制造业安全事故频发,如何才能安全进行设计图纸文件外发?

汽车制造业产业链长&#xff0c;关联度高&#xff0c;汽车制造上游行业主要为钢铁、化工等行业&#xff0c;下游主要为个人消 费、基建、客运和军事等。在汽车制造的整个生命周期中&#xff0c;企业与上下游供应商、合作商之间有频繁、密切的数据交换&#xff0c;企业需要将设计…

大模型公开课-大模型的语言解码游戏学习总结

在当今快速发展的人工智能领域&#xff0c;深度学习作为其中的一项关键技术&#xff0c;正引领着科技的新潮流。而对于初学者来说&#xff0c;了解大型语言模型的解码游戏&#xff0c;对于理解深度学习的基本概念至关重要。本篇博客将对一次关于大型语言模型解码游戏的视频教学…

面试经典算法题之双指针专题

力扣经典面试题之双指针 ( 每天更新, 每天一题 ) 文章目录 力扣经典面试题之双指针 ( 每天更新, 每天一题 )验证回文串收获 392. 判断子序列 验证回文串 思路 一: 筛选 双指针验证 class Solution { public:bool isPalindrome(string s) {// 所有大写字母 > 小写 去除非字母…

nginx下载安装配置(含ssl)

下载安装环节 wget https://nginx.org/download/nginx-1.24.0.tar.gz tar -zxvf xxx.tar.gz yum -y install pcre-devel openssl openssl-devel ./configure --prefix/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-stream make & make i…

Linux的vim下制作进度条

目录 前言&#xff1a; 回车和换行有区别吗&#xff1f; 回车和换行的区别展示&#xff08;这个我在Linux下演示&#xff09; 为什么会消失呢? 回车和换行的区别 为什么\r和\n产生的效果不同&#xff1f; 打印进度条&#xff1a; &#xff08;1&#xff09;打印字符串 …

第十四届蓝桥杯国赛:2023次方的思考(指数塔,数论)

首先我们要知道&#xff0c;正常计算的话&#xff0c;指数优先级最高&#xff0c;因此得先计算指数&#xff0c;比如&#xff1a; 2 3 2 512 2^{3^2}512 232512 欧拉定理的关键在于&#xff0c;它允许我们通过减少计算的指数大小来简化模运算。 经过仔细研究&#xff08;看题…

设计模式之代理模式ProxyPattern(六)

一、代理模式介绍 1、什么是代理模式&#xff1f; 代理模式是一种结构型设计模式&#xff0c;它允许为其他对象提供一个替代品或占位符&#xff0c;以控制对这个对象的访问。 2、代理模式的角色构成 抽象主题&#xff08;Subject&#xff09;&#xff1a;定义了真实主题和代…

【算法】【单调栈】【leetcode】1019. 链表中的下一个更大节点

刷这题之前先看&#xff1a; 【算法】【OD算法】【单调栈】找朋友-CSDN博客 【算法】【单调栈】【leetcode】1475. 商品折扣后的最终价格-CSDN博客 【算法】【单调栈】【leetcode】901. 股票价格跨度-CSDN博客 【算法】【单调栈】每日温度-CSDN博客 题目地址&#xff1…

机器人系统ros2-开发实践04-ROS2 中 tf2的定义及示例说明

1. what ros2 tf2 &#xff1f; tf2的全称是transform2&#xff0c;在ROS&#xff08;Robot Operating System&#xff09;中&#xff0c;它是专门用于处理和变换不同坐标系间位置和方向的库。这个名字来源于“transform”这个词&#xff0c;表示坐标变换&#xff0c;而“2”则…

【介绍下Unity编辑器扩展】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

学习 Rust 第 23 天:闭包

Rust 闭包提供了简洁、富有表现力的匿名函数来捕获周围的变量。它们简化了代码&#xff0c;提供了存储、参数传递和函数重构方面的灵活性。它们与泛型的交互增强了灵活性&#xff0c;而捕获模式则促进了有效的所有权和可变性管理。从本质上讲&#xff0c;闭包是 Rust 的基础&am…

笔记-用Python脚本启停JAR程序

用Python脚本启停JAR程序&#xff0c;需要用到python中的以下内置模块 subprocess 是 Python 的一个标准库模块&#xff0c;用于在新进程中执行子命令&#xff0c;获取子进程的输入/输出/错误以及返回码等os 是 Python 的一个标准库模块&#xff0c;它提供了与操作系统交互的功…

【web安全】-- 命令执行漏洞详解

本文将从原理开始介绍命令执行漏洞并附有三个实例来供各位客官学习 文章目录 一、什么是命令执行漏洞二、出现的原因三、有可能存在命令执行漏洞的函数&#xff08;php&#xff09;1、利用一些函数来实现命令执行2、直接执行系统命令的函数 四、命令拼接符号1、Windows2、linux…

【Mac】mac 安装 prometheus 报错 prometheus: prometheus: cannot execute binary file

1、官网下载 Download | Prometheus 这里下载的是prometheus-2.51.2.linux-amd64.tar.gz 2、现象 解压之后启动Prometheus 启动脚本&#xff1a; nohup ./prometheus --config.fileprometheus.yml > prometheus.out 2>&1 & prometheus.out日志文件&#xff…

opencv基础篇 ——(十)非真实感渲染

非真实感渲染&#xff08;Non-Photorealistic Rendering, NPR&#xff09;是指通过一系列图像处理技术&#xff0c;将真实感图像转换为具有特定艺术风格或视觉效果的图像&#xff0c;模拟绘画、素描、卡通等非现实主义表现手法。OpenCV 提供了一些内置函数来实现非真实感渲染&a…