首页 > 其他分享 >23种设计模式之建造者模式

23种设计模式之建造者模式

时间:2023-08-29 19:32:22浏览次数:37  
标签:return String 23 Builder 建造 private void 设计模式 public

Builder 模式,也叫生成器模式。创建者模式主要包含以下四个角色:

  1. 产品(Product):表示将要被构建的复杂对象。
  2. 抽象创建者(Abstract Builder):定义构建产品的接口,通常包含创建和获取产品的方法。
  3. 具体创建者(Concrete Builder):实现抽象创建者定义的接口,为产品的各个部分提供具体实现。
  4. 指挥者(Director):负责调用具体创建者来构建产品的各个部分,控制构建过程。

假设我们需要创建一个复杂的HTML文档,它包含了标题、段落和图像等元素。可以使用创建者设计模式来构建HTML文档。

1、产品(Product)类 - HTML文档(HtmlDocument):

public class HtmlDocument {
    private String header = "";
    private String body = "";
    private String footer = "";

    public void addHeader(String header) {
        this.header = header;
    }

    public void addBody(String body) {
        this.body = body;
    }

    public void addFooter(String footer) {
        this.footer = footer;
    }

    @Override
    public String toString() {
        return "<html><head>" + header + "</head><body>" + body + "</body><footer>" + footer + "</footer></html>";
    }
}

2、抽象创建者(Abstract Builder)类 - HtmlDocumentBuilder:

public abstract class HtmlDocumentBuilder {
    protected HtmlDocument document;

    public HtmlDocument getDocument() {
        return document;
    }

    public void createNewHtmlDocument() {
        document = new HtmlDocument();
    }

    public abstract void buildHeader();
    public abstract void buildBody();
    public abstract void buildFooter();
}

3、具体创建者(Concrete Builder)类 - ArticleHtmlDocumentBuilder:

public class ArticleHtmlDocumentBuilder extends HtmlDocumentBuilder {
    @Override
    public void buildHeader() {
        document.addHeader("Article Header");
    }

    @Override
    public void buildBody() {
        document.addBody("Article Body");
    }

    @Override
    public void buildFooter() {
        document.addFooter("Article Footer");
    }
}

4、指挥者(Director)类 - HtmlDirector:

public class HtmlDirector {
    private HtmlDocumentBuilder builder;

    public HtmlDirector(HtmlDocumentBuilder builder) {
        this.builder = builder;
    }

    public void constructDocument() {
        builder.createNewHtmlDocument();
        builder.buildHeader();
        builder.buildBody();
        builder.buildFooter();
    }

    public HtmlDocument getDocument() {
        return builder.getDocument();
    }
}

使用创建者设计模式来构建一个HTML文档对象:

public class Main {
    public static void main(String[] args) {
        HtmlDocumentBuilder articleBuilder = new ArticleHtmlDocumentBuilder();
        HtmlDirector director = new HtmlDirector(articleBuilder);

        director.constructDocument();
        HtmlDocument document = director.getDocument();

        System.out.println("Constructed HTML Document: \n" + document);
    }
}

在这个例子中创建了一个表示HTML文档的产品类(HtmlDocument),一个抽象的创建者类(HtmlDocumentBuilder),一个具体的创建者类(ArticleHtmlDocumentBuilder)和一个指挥者类(HtmlDirector)。当需要创建一个新的HTML文档对象时,可以使用指挥者类来控制构建过程,从而实现了将构建过程与表示过程的分离。

以上是一个创建者设计模式的标准写法,事实,在工作中往往不会写的这么复杂,为了创建一个对象,创建了很多辅助的类,总觉得不太合适,在这个案例中,可以使用内部类来简化代码,以下是修改后的代码(甚至我们还移除了抽象层):

public class HtmlDocument {
    private String header = "";
    private String body = "";
    private String footer = "";

    public void addHeader(String header) {
        this.header = header;
    }

    public void addBody(String body) {
        this.body = body;
    }

    public void addFooter(String footer) {
        this.footer = footer;
    }

    @Override
    public String toString() {
        return "<html><head>" + header + "</head><body>" + body + "</body><footer>" + footer + "</footer></html>";
    }

    public static class Builder {
        protected HtmlDocument document;

        public Builder() {
            document = new HtmlDocument();
        }

        public Builder addHeader(String header) {
            document.addHeader(header);
            return this;
        }

        public Builder addBody(String body) {
            document.addBody(body);
            return this;
        }

        public Builder addFooter(String footer) {
            document.addFooter(footer);
            return this;
        }

        public HtmlDocument build() {
            return document;
        }
    }
}

现在使用以下代码来创建一个HTML文档对象:

public class Main {
    public static void main(String[] args) {
        HtmlDocument.ArticleBuilder builder = new HtmlDocument.ArticleBuilder();
        HtmlDocument document = builder.addHeader("This is the header")
                                       .addBody("This is the body")
                                       .addFooter("This is the footer")
                                       .build();

        System.out.println("Constructed HTML Document: \n" + document);
    }
}

在这个修改后的例子中,将创建者类(Builder)作为HTML文档类(HtmlDocument)的内部类。这样做可以让代码更加紧凑。此外,使用了一种流式接口(Fluent Interface),使得在客户端代码中创建HTML文档对象更加简洁。

当然有些人看了这个案例,已经会觉得

为什么需要建造者模式

需求一、根据复杂的配置项进行定制化构建

首先,先看一个mybaits中经典的案例,这个案例中使用了装饰器和创建者设计模式:

public class CacheBuilder {
    private final String id;
    private Class<? extends Cache> implementation;
    private final List<Class<? extends Cache>> decorators;
    private Integer size;
    private Long clearInterval;
    private boolean readWrite;
    private Properties properties;
    private boolean blocking;

    public CacheBuilder(String id) {
        this.id = id;
        this.decorators = new ArrayList<>();
    }

    public CacheBuilder size(Integer size) {
        this.size = size;
        return this;
    }

    public CacheBuilder clearInterval(Long clearInterval) {
        this.clearInterval = clearInterval;
        return this;
    }

    public CacheBuilder blocking(boolean blocking) {
        this.blocking = blocking;
        return this;
    }

    public CacheBuilder properties(Properties properties) {
        this.properties = properties;
        return this;
    }

    public Cache build() {
        setDefaultImplementations();
        Cache cache = newBaseCacheInstance(implementation, id);
        setCacheProperties(cache);
        // 根据配置的装饰器对原有缓存进行增强,如增加淘汰策略等
        if (PerpetualCache.class.equals(cache.getClass())) {
            for (Class<? extends Cache> decorator : decorators) {
                cache = newCacheDecoratorInstance(decorator, cache);
                setCacheProperties(cache);
            }
            cache = setStandardDecorators(cache);
        } else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
            cache = new LoggingCache(cache);
        }
        return cache;
    }
}

这个案例中的几个特点:

1、参数有必填项id,有很多可选填的内容,通常必选项id通过构造器传入,可选项通过方法传递。

2、真正的构建过程需要调用build方法,构建时需要根据已配置的成员变量的内容选择合适的装饰器,对目标cache进行增强。

需求二、实现不可变对象

创建者设计模式(Builder Design Pattern)可以实现不可变对象,即一旦创建完成,对象的状态就不能改变。这有助于保证对象的线程安全和数据完整性。下面是一个使用创建者设计模式实现的不可变对象的Java示例:

public final class ImmutablePerson {
    private final String name;
    private final int age;
    private final String address;

    private ImmutablePerson(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
        this.address = builder.address;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public String getAddress() {
        return address;
    }

    public static class Builder {
        private String name;
        private int age;
        private String address;

        public Builder() {
        }

        public Builder setName(String name) {
            this.name = name;
            return this;
        }

        public Builder setAge(int age) {
            this.age = age;
            return this;
        }

        public Builder setAddress(String address) {
            this.address = address;
            return this;
        }

        public ImmutablePerson build() {
            return new ImmutablePerson(this);
        }
    }
}

在这个例子中,ImmutablePerson 类具有三个属性:nameage 和 address。这些属性都是 final 的,一旦设置就不能更改。ImmutablePerson 的构造函数是私有的,外部无法直接创建该类的实例。要创建一个 ImmutablePerson 实例,需要使用内部的 Builder 类。通过连续调用 Builder 类的方法,我们可以为 ImmutablePerson 设置属性。最后,通过调用 build() 方法,创建一个具有指定属性的不可变 ImmutablePerson 实例。

实际上,使用建造者模式创建对象,还能避免对象存在无效状态。如定义了一个长方形类,如果不使用建造者模式,采用先创建后 set 的方式,那就会导致在第一个 set 之后,对象处于无效状态。具体代码如下所示:

Rectangle r = new Rectange(); // r is invalid
r.setWidth(2); // r is invalid
r.setHeight(3); // r is valid

为了避免这种无效状态的存在,就需要使用构造函数一次性初始化好所有的成员变量。如果构造函数参数过多,就需要考虑使用建造者模式,先设置建造者的变量,然后再一次性地创建对象,让对象一直处于有效状态。

实际上,如果我们并不是很关心对象是否有短暂的无效状态,也不是太在意对象是否是可变的。比如,对象只是用来映射数据库读出来的数据,那我们直接暴露 set() 方法来设置类的成员变量值是完全没问题的。而且,使用建造者模式来构建对象,代码实际上是有点重复的。


标签:return,String,23,Builder,建造,private,void,设计模式,public
From: https://blog.51cto.com/u_10956218/7279523

相关文章

  • 23种设计模式之工厂模式
    工厂模式分为三种更加细分的类型:简单工厂、工厂方法和抽象工厂。简单工厂(SimpleFactory):简单工厂叫作静态工厂方法模式(StaticFactoryMethodPattern)。假设一个场景,需要一个资源加载器,要根据不同的url进行资源加载,但是如果将所有的加载实现代码全部封装在了一个load方法中,就会导致......
  • 23种设计模式之单例模式
    单例设计模式(SingletonDesignPattern):一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式。如何实现一个单例:常见的单例设计模式,有如下五种写法,在编写单例代码的时候要注意以下几点:1、构造器需要私有化2、暴露一个公共的获取单例对象的接口3......
  • Jeecg-Boot存在前台SQL注入漏洞CVE-2023-1454
    Jeecg-boot简介jeecgBoot是一款基于BPM的低代码平台!前后端分离架构SpringBoot2.x,SpringCloud,AntDesign&Vue,Mybatis-plus,Shiro,JWT,支持微服务。强大的代码生成器让前后端代码一键生成,实现低代码开发!JeecgBoot引领新低代码开发模式OnlineCoding->代码生成器->手工MERGE,帮助J......
  • CCF HPC China2023|澎峰科技:使能先进计算,赋能行业应用
    CCFHPCChina2023圆满落幕! 桂秋八月,为期三天的中国高性能计算领域最高规格盛会——2023CCF全球高性能计算学术年会(HPCChina)在青岛红岛国际展览中心圆满落幕。行业超算大咖、顶级学界精英、先锋企业领袖参会者齐聚山东青岛,共同探讨高性能计算、人工领域、大数据等诸多前沿领域......
  • 2023.08.29T3 - summer - solution
    summerProblemSolution挺好的题,题解也写得很清楚,因此我不过是把题解抄一遍。赛时打了\(40\)分,然后挂了\(20\)分,因为不会前缀和(这个人暴力求区间和,铸币吧)。前\(40\)分就是记忆化搜索+单调栈:首先考察对于一个确定的序列,如何求出一段区间的权值和。那么首先就要知道如......
  • 20230627 java.net.URL
    介绍java.net.URLpublicfinalclassURLimplementsjava.io.SerializableURI是个纯粹的语法结构,包含用来指定Web资源的字符串的各种组成部分URL是URI的一个特例,它包含了用于定位Web资源的足够信息URL语法authority部分具有以下形式:[user-info@]host[:port]......
  • 20230627 java.net.URI
    介绍java.net.URIpublicfinalclassURIimplementsComparable,SerializableURI是个纯粹的语法结构,包含用来指定Web资源的字符串的各种组成部分URL是URI的一个特例,它包含了用于定位Web资源的足够信息URI语法URI具有以下句法:[scheme:]schemeSpecficPart[#fra......
  • 20230627 java.net.Socket
    介绍java.net.SocketpublicclassSocketimplementsjava.io.Closeable套接字(Socket)是网络软件中的一个抽象概念,负责启动该程序内部和外部之间的通信API构造器Socket()Socket(Proxyproxy)Socket(Stringhost,intport)throwsUnknownHostException,IOException......
  • 20230627 java.net.ServerSocket
    介绍java.net.ServerSocketpublicclassServerSocketimplementsjava.io.Closeable服务器套接字ServerSocket类用于建立套接字,accept用于告诉程序不停地等待,直到有客户端连接到这个端口。一旦有人通过网络发送了正确的连接请求,并以此连接到了端口上,该方法就会返回一个表......
  • 20230627 java.net.InetSocketAddress
    介绍java.net.InetSocketAddresspublicclassInetSocketAddressextendsSocketAddressAPI构造器InetSocketAddress(intport)InetSocketAddress(InetAddressaddr,intport)InetSocketAddress(Stringhostname,intport)publiccreateUnresolved创建未解析的I......