Java 设计模式 - Builder_java builder设计模式-程序员宅基地

技术标签: JavaDesignPatterns  java  设计模式  开发语言  

Java Design Parttens - Builder

1、Intent

Separate the construction of a complex object from its representation so that the same construction process can create different representations.

通过将对象的构建过程与其表示分离来解决复杂对象的创建问题。它允许你创建具有不同表示形式的对象,而无需更改它们的构建过程。

2、Explanation

2.1 Real-world example

Imagine a character generator for a role-playing game.

想象一下角色扮演游戏的角色生成器。

The easiest option is to let the computer create the character for you.

最简单的方式就是让电脑生成一个角色。

If you want to manually select the character details like profession, gender, hair color, etc. the character generation becomes a step-by-step process that completes when all the selections are ready.

如果想选择角色细节,类似专业,性别等,当配个细节准备好的时候,角色生成器就会一步一步完成角色。

2.2 In plain words

Allows you to create different flavors of an object while avoiding constructor pollution. Useful when there could be several flavors of an object. Or when there are a lot of steps involved in creation of an object.

允许创建不同类型的 object,避免污染(过于复杂的过滤器). 适用于创建不同类型对象或者创建一个对象有很多步。

2.3 Wikipedia says

The builder pattern is an object creation software design pattern with the intentions of finding a solution to the telescoping constructor anti-pattern.

“Telescoping constructor anti-pattern” 是指在面向对象编程中,使用过多的构造函数参数来创建一个对象,从而使构造函数参数列表变得过于冗长和难以管理的一种设计反模式。这种情况通常会出现在需要创建多个不同的对象变体,而每个变体需要不同数量或类型的构造函数参数时。

Having said that let me add a bit about what telescoping constructor anti-pattern is. At one point or the other, we have all seen a constructor like below :

举个例子

public Hero(Profession profession, String name, HairType hairType, HairColor hairColor, Armor armor, Weapon weapon) {
    
}

As you can see the number of constructor parameters can quickly get out of hand, and it may become difficult to understand the arrangement of parameters. Plus this parameter list could keep on growing if you would want to add more options in the future. This is called telescoping constructor anti-pattern.

大概意思就是 参数太多,不容易理解,容易把握不住。

2.4 Programmatic Example

类似下列 Hero 对象创建使用 Builder 模式就是个很好的选择捏

The sane alternative is to use the Builder pattern. First of all, we have our hero that we want to create:

public final class Hero {
    
  private final Profession profession;
  private final String name;
  private final HairType hairType;
  private final HairColor hairColor;
  private final Armor armor;
  private final Weapon weapon;

  private Hero(Builder builder) {
    
    this.profession = builder.profession;
    this.name = builder.name;
    this.hairColor = builder.hairColor;
    this.hairType = builder.hairType;
    this.weapon = builder.weapon;
    this.armor = builder.armor;
  }
}

Then we have the builder:

  public static class Builder {
    
    private final Profession profession;
    private final String name;
    private HairType hairType;
    private HairColor hairColor;
    private Armor armor;
    private Weapon weapon;

    public Builder(Profession profession, String name) {
    
      if (profession == null || name == null) {
    
        throw new IllegalArgumentException("profession and name can not be null");
      }
      this.profession = profession;
      this.name = name;
    }

    public Builder withHairType(HairType hairType) {
    
      this.hairType = hairType;
      return this;
    }

    public Builder withHairColor(HairColor hairColor) {
    
      this.hairColor = hairColor;
      return this;
    }

    public Builder withArmor(Armor armor) {
    
      this.armor = armor;
      return this;
    }

    public Builder withWeapon(Weapon weapon) {
    
      this.weapon = weapon;
      return this;
    }

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

Then it can be used as:

var mage = new Hero.Builder(Profession.MAGE,"Riobard").withHairColor(HairColor.BLACK).withWeapon(Weapon.DAGGER).build();

3、Class diagram

在这里插入图片描述

4、Applicability

Use the Builder pattern when

  • The algorithm for creating a complex object should be independent of the parts that make up the object and how they’re assembled

将一个复杂对象的构建过程分解成多个简单对象的构建过程,这些简单对象再通过组装起来形成最终的复杂对象

  • The construction process must allow different representations for the object that’s constructed

在构建对象的过程中,必须允许为该对象构建不同的表示形式。换句话说,构建过程必须具有足够的灵活性,以便为构建的对象提供多种不同的表现形式,以满足不同的应用场景和需求。

5、自己实现简单的例子

5.1 Object

@Data
public class User {
    
    private String A;
    private String b;
    private String c;
    private String d;
    private String e;

    public User(UserBuilder builder) {
    
        this.A = builder.getA();
        this.b = builder.getB();
        this.c = builder.getC();
        this.d = builder.getD();
        this.e = builder.getE();
    }
}

5.3 builder

@Data
public class UserBuilder {
    

    private String A;
    private String b;
    private String c;
    private String d;
    private String e;

    public UserBuilder buildA(String a) {
    
        A = a;
        return this;
    }

    public UserBuilder buildB(String b) {
    
        this.b = b;
        return this;
    }

    public UserBuilder buildC(String c) {
    
        this.c = c;
        return this;
    }

    public UserBuilder buildD(String d) {
    
        this.d = d;
        return this;
    }

    public UserBuilder buildE(String e) {
    
        this.e = e;
        return this;
    }

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

5.3 test

public class TestUserBuilder {
    

    public static void main(String[] args) {
    
        User user1 = new UserBuilder().buildA("a").buildB("b").buildC("c").build();
        System.out.println(user1);

        User user2 = new UserBuilder().buildC("2 c").buildD("2 d").buildE("2 e").build();
        System.out.println(user2);
    }
}

User(A=a, b=b, c=c, d=null, e=null)
User(A=null, b=null, c=2 c, d=2 d, e=2 e)

6. Spring Security 中的实际应用

在Spring Security 中,有三个 builder 对象 HttpSecurity, WebSecuroty, AuthorizationManager,以下代码所示。他们都继承自 SecurityBuilder<o>。这三个 builder 对象会通过 SecurityConfigurer来对要构建的类进行配置,也就是 buildA("a").buildB("b").buildC("c")这段代码的意思,最后通过 build()方法构建,非常的妙。

public class AuthenticationManagerBuilder
		extends AbstractConfiguredSecurityBuilder<AuthenticationManager, AuthenticationManagerBuilder>
		implements ProviderManagerBuilder<AuthenticationManagerBuilder> {
    
public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
		implements SecurityBuilder<DefaultSecurityFilterChain>, HttpSecurityBuilder<HttpSecurity> {
    
public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>
		implements SecurityBuilder<Filter>, ApplicationContextAware {
    

以下代码大概模拟下

6.1 Builder

顶级接口,只负责构建一个对象, 构建对象类型通过泛型传递。

public interface Builder<O> {
    
    O build();
}

6.2 AbstractBuilder

抽象类,负责实现 Builder 接口,构建出 object对象,并提供对方法,获取构建出的 object对象

具体构建方式由子类实现

public abstract class AbstractBuilder<O> implements Builder<O> {
    

    private O object;

    @Override
    public O build() {
    
        if (null == object) {
    
            return dobuild();
        }
        return object;
    }

    public O getObject() {
    
        return object;
    }

    protected abstract O dobuild();
}

6.3 StepAbstractBuilder

AbstractBuilder 的子类,实现了dobuild() 方法,定义出按照步骤去构架对象的具体实现逻辑

自身也是个抽象模板类,先去 config() ,配置所构建对象,当然也可以继续扩充,比如加上 init() 方法,初始化。

performBuild() 抽象方法由子类实现,定义做完配置动作之后,如何构建对象。

public abstract class StepAbstractBuilder<O, B extends Builder<O>> extends AbstractBuilder<O> {
    
		
    // 配置对象
    List<Config<O, B>> configs = new ArrayList<>();

    // 模版方法,执行初始化,配置,构建
    @Override
    protected O dobuild() {
    

        // 先配置
        config();
        System.out.println("配置好了" + configs);
				
        O result = performBuild();
        System.out.println("生成了对象" + result.toString());
        return result;
    }
		
    public B addConfig(Config<O, B> config) {
    
        configs.add(config);
        return (B)this;
    }

    private void config() {
    
        for (Config<O, B> config : configs) {
    
            config.config((B)this);
        }
    }

    protected abstract O performBuild();
}

6.4 配置对象

通过 builder 对象实现对被构建对象的配置

泛型 -> <O 被构建对象 , B extends Builder 被构建对象的 builder>

public interface Config<O, B extends Builder<O>> {
    

    // 配置操作
    void config(B builder);
}

6.5 使用方法

6.5.1 构建不同的 House
6.5.1.1 HouseBuilder

继承自 StepAbstractBuilder<House, HouseBuilder>,定义了被构建对象为 House , 构建逻辑为 HouseBuilder

public class HouseBuilder extends StepAbstractBuilder<House, HouseBuilder> {
    

    // 提供 builder 参数
    private String color;
    private String height;
    private String size;

    @Override
    protected House performBuild() {
    
        return new House(this);
    }

    public HouseBuilder buildColor(String color) {
    
        this.color = color;
        return this;
    }

    public HouseBuilder buildHeight(String height) {
    
        this.height = height;
        return this;
    }

    public HouseBuilder buildSize(String size) {
    
        this.size = size;
        return this;
    }
}

6.5.1.2 HoseConfig

HoseConfig 实现 Config 接口,通过 HouseBuilder 来配置 House 对象

public class HoseConfig implements Config<House, HouseBuilder> {
    

    // 提供 builder 参数
    private String color;
    private String height;
    private String size;

    public HoseConfig(String color, String height, String size) {
    
        this.color = color;
        this.height = height;
        this.size = size;
    }

    @Override
    public void config(HouseBuilder builder) {
    
        builder.buildColor(color).buildHeight(height).buildSize(size);
    }
}
6.5.1.3 House
public class House {
    

    public House(HouseBuilder builder) {
    
        this.color = builder.getColor();
        this.height = builder.getHeight();
        this.size = builder.getSize();
    }

    // 提供 builder 参数
    private String color;
    private String height;
    private String size;
}
6.5.1.4 Test
House house1 = new HouseBuilder().addConfig(new HoseConfig("红", "100", "200")).build();
House house2 = new HouseBuilder().addConfig(new HoseConfig("黑", "400", "700")).build();

输出 ->
		配置好了[HoseConfig(color=, height=100, size=200)]
		生成了对象House(color=, height=100, size=200)
		配置好了[HoseConfig(color=, height=400, size=700)]
		生成了对象House(color=, height=400, size=700)
 
6.5.2 构建不同的 Fruit
6.5.2.1 FruitBuilder
@Data
public class FruitBuilder extends StepAbstractBuilder<Fruit, FruitBuilder> {
    

    // 提供 builder 参数
    private String name;
    private String price;

    @Override
    protected Fruit performBuild() {
    
        return new Fruit(this);
    }

    public FruitBuilder buildName(String name) {
    
        this.name = name;
        return this;
    }

    public FruitBuilder buildPrice(String price) {
    
        this.price = price;
        return this;
    }
}
6.5.2.2 FruitConfig
public class FruitConfig implements Config<Fruit, FruitBuilder> {
    

    // 提供 builder 参数
    private String name;
    private String price;

    public FruitConfig(String name, String price) {
    
        this.name = name;
        this.price = price;
    }

    @Override
    public void config(FruitBuilder builder) {
    
        builder.buildName(name).buildPrice(price);
    }
}
6.5.2.3 Fruit
public class Fruit {
    

    public Fruit(FruitBuilder builder) {
    
        this.name = builder.getName();
        this.price = builder.getPrice();
    }

    // 提供 builder 参数
    private String name;
    private String price;
}
Test
Fruit fruit1 = new FruitBuilder().addConfig(new FruitConfig("苹果", "1")).build();
Fruit fruit2 = new FruitBuilder().addConfig(new FruitConfig("梨子", "2")).build();

输出 ->
  	配置好了[FruitConfig(name=苹果, price=1)]
		生成了对象Fruit(name=苹果, price=1)
		配置好了[FruitConfig(name=梨子, price=2)]
		生成了对象Fruit(name=梨子, price=2)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_37813031/article/details/129365751

智能推荐

给Hexo博客添加文章编辑链接_hexo admin添加链接-程序员宅基地

文章浏览阅读843次,点赞25次,收藏20次。用了hexo这个博客之后,好用固然好用,问题就是有时候我在看我博客的时候突然发现有个问题,然后就想要修改,于是乎就要打开github然后一个目录一个目录的点进去,发现效率太低。,Facebook家的东西,用来搞课程笔记很方便,里面有个功能就是:每篇文章的末尾都有一个编辑的URL,可以点一下就跳转到对应的Github,非常方便。的主题里面的文档,发现没有这个需求,给Github的开发组提需求,开发组认为这个可以用注入的方法实现。文件目录的格式,和网站博客文章的URL的格式一样,否则就不可以。_hexo admin添加链接

学python必须得英语精通吗_干货|Python学习必须精通的几个模块-程序员宅基地

文章浏览阅读34次。定义Python 模块(Module),是一个 Python 文件,以 .py 结尾,包含了Python 对象定义和Python语句。模块让你能够有逻辑地组织你的 Python代码段。把相关的代码分配到一个模块里能让你的代码更好用,更易懂。模块能定义函数,类和变量,模块里也能包含可执行的代码。那废话不多说,给大家带来几个必须学会的python模块吧。rere的matche方法和search方法re..._python 模块 英语

[漏洞复现]phpweb 前台任意文件上传漏洞_phpweb 漏洞-程序员宅基地

文章浏览阅读2.4k次。环境搭建phpweb环境搭建很简单,基本就是下一步下一步点点点就可以了。搭建前需要创建好phpweb对应数据库,如图:然后前台下一步下一步一直点,安装成功:前台:http://192.168.242.128/phpweb/3151/后台:http://192.168.242.128/phpweb/3151/admin.php复现:漏洞位于./base/appplus.php文件下..._phpweb 漏洞

gdb源代码文件目录修改_gdb 更改代码目录-程序员宅基地

文章浏览阅读5.8k次。今天用gdb查看core文件,发现找不到源文件。记录一下相关命令在gdb里面查看源码,list 一下提醒找不到相关文件,列出来的是绝对路径的完整文件名。help files 看一下帮助,可以加载符号,源文件等,自己看一下。dir 设置源码路径无效,show directories 看到设置成功,但是还是找不到文件。应该是绝对路径的问题。因为igcc 根据你_gdb 更改代码目录

【Python】Python 打印和输出更多用法。-程序员宅基地

文章浏览阅读251次。Python 打印和输出简述在编程实践中,print 的使用频率非常高,特别是程序运行到某个时刻,要检测产生的结果时,必须用 print 来打印输出。关于 print 函数,前面很多地方已经提及过,可用于写入标准输出。现在,是时候该深入了。注意:这里强调的是“print 函数”,而不是“print 语句”。深入 print在 Python 2.x ..._python 聚合函数打印会多打印出源语句

chariot iperf使用_iperf知识点-程序员宅基地

文章浏览阅读94次。1、多线程问题:但是,有时你将需要使用多台拥有各自Wi-Fi适配器的笔记本来模拟几个不同位置用户体验的性能。这是因为在同一个笔记本上运行的多线程仍然共享同一个Wi-Fi适配器上的占 用时间。2、另外一方面,如果你的笔记本上拥有多个激活的适配器,你可以使用IP地址(-B IPAddress)将iPerf客户端绑定到一个适配器上。这个对于同时连接到Ethernet和Wi-Fi(3G、Wi-Fi)的多连..._chariot management ip

随便推点

Python中的文章纠错和检测抄袭_python 文本纠错-程序员宅基地

文章浏览阅读228次。通过使用Python中的相关技术和库,我们可以自动化地进行文章纠错和检测抄袭的工作。上述示例代码提供了基本的实现思路,你可以根据自己的需求进行进一步的改进和扩展。这些技术可以帮助我们改善文章的质量,并确保我们的写作具有原创性。在上面的示例代码中,我们使用了NLTK库来检测两个文本之间的相似度,从而判断是否存在抄袭行为。接下来,我们通过比较两个文本中的单词集合,计算它们的相似度。为了确保我们的写作具有原创性,我们可以利用Python中的一些工具来检测文章是否存在抄袭行为。在上面的示例代码中,我们使用了。_python 文本纠错

armbian 斐讯n1_斐讯N1-ArmBian系统写入EMMC及优化-程序员宅基地

文章浏览阅读6.8k次。斐讯N1-ArmBian系统写入EMMC及优化免贵姓操•2020 年 07 月 26 日前言最近想搞个本地测试服务器,看到了斐讯N1,所以折腾下,安装Linux系统,再安装宝塔教程详情下载ssh工具,推荐下面这款,该工具可以可视化编辑系统文件FinalShell :Mac版 Winsows版使用SSH工具登录U盘armbian系统,保持screen连接screen -S lnmp将U盘系统..._n1 armbian写入emmc

delphi 用dbgrid控件显示memo字段的问题-程序员宅基地

文章浏览阅读705次。写程序真得花功夫,想用好delphi7下强大的控件不容易,那可以比较出是否理解程序设计的深入程度。我不认为第三方控件怎么好,想更好进行软件开发,还是要会用原本的控件啊,用好了才能更好地学习别的优秀控件。(因为我并不在软件开发上工作,不知道时间的意义,完全是个人爱好)我个人喜好原生态的东西。下面附上代码,没有测试过字符多的情况,但对于我要做的事已经够用了。procedure TSt..._delphi dbgrid 显示memo

git问题记录:fatal: pathspec ‘xxxx‘ did not match any files_fatal: pathspec 'pods.xcodeproj' did not match any-程序员宅基地

文章浏览阅读1.6w次,点赞6次,收藏13次。git问题记录:fatal: pathspec ‘xxxx’ did not match any files使用git pull 到本地后文件改了名字,git add 出现:fatal: pathspec ‘xxxxxx’ did not match any files 报错,导致无法git pushgit add . 先把更新本地仓库git add xxxx 再提交单个文件git commit -m “xxxx” 添加注释正常git push origin master 就可以了..._fatal: pathspec 'pods.xcodeproj' did not match any files

IEDA的安装与配置-程序员宅基地

文章浏览阅读1.7k次。1、安装IDEA的下载与安装较为简单,傻瓜式操作,所以这里就不加赘述。2、配置JDKIDEA的使用首先要安装JDK,JDK的安装和环境变量配置这里就不赘述了,配置好环境变量后,点击configure下的Project Defaults的Project Structure。选择Project--点击New,选择JDK安装目录。3、Tomcat配置创建一个JavaWeb..._ieda

基于ssm+vue.js+uniapp小程序的OA办公系统附带文章和源代码设计说明文档ppt_uniapp oa系统-程序员宅基地

文章浏览阅读639次,点赞16次,收藏21次。博主介绍:CSDN特邀作者、985计算机专业毕业、某互联网大厂高级全栈开发程序员、码云/掘金/华为云/阿里云/InfoQ/StackOverflow/github等平台优质作者、专注于Java、小程序、前端、python等技术领域和毕业项目实战,以及程序定制化开发、全栈讲解、就业辅导、面试辅导、简历修改。精彩专栏 推荐订阅2023-2024年最值得选的微信小程序毕业设计选题大全:100个热门选题推荐2023-2024年最值得选的Java毕业设计选题大全:500个热门选题推荐。_uniapp oa系统

推荐文章

热门文章

相关标签