zoukankan      html  css  js  c++  java
  • Fun论设计模式之5:建造者模式(Builder Pattern)

      建造者模式在程序设计中经常被运用,下面是建造者模式的概述。

      意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

      主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

      何时使用:一些基本部件不会变,而其组合经常变化的时候。

      如何解决:将变与不变分离开。

      关键代码:建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系。

      应用实例: 1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。 2、JAVA 中的 StringBuilder。

      说来复杂,其实要实现一个也很简单。

      跟标准模式相似,不同之处是建造者模式可以直接用本身的复杂组合。

      Java web系统多数都有MySQL表转实体类的组件,直接把生成的实体类的setter返回类型改成实体类本身,返回对象改成本身(this),一个属性也是组合,也能做成一个类builder,跟builder类似的地方是这个对象也可以链式调用改属性的函数;不同的是这个User本身不是单独的builder,且不是延迟build(构造对象)。

     1 public class User {
     2 
     3     private Integer id;
     4 
     5     private String password;
     6 
     7     private String username;
     8 
     9     private Integer userIdentity;
    10 
    11     private String userMobile;
    12     
    13     private String classId;
    14 
    15     /**
    16      * 班级ID
    17      */
    18     @Column(name = "`class_id`")
    19     @ApiModelProperty(value = "班级ID(id1,id2,id3...)",example = "班级ID(id1,id2,id3...)",required = false)
    20     private String classId;
    21 
    22     public Integer getId() {
    23         return id;
    24     }
    25 
    26     public User setId(Integer id) {
    27         this.id = id;
    28         return this;
    29     }
    30 
    31     public String getPassword() {
    32         return password;
    33     }
    34 
    35     public User setPassword(String password) {
    36         this.password = password;
    37         return this;
    38     }
    39 
    40     public String getUsername() {
    41         return username;
    42     }
    43 
    44     public User setUsername(String username) {
    45         this.username = username;
    46         return this;
    47     }
    48 
    49     public Integer getUserIdentity() {
    50         return userIdentity;
    51     }
    52 
    53     public User setUserIdentity(Integer userIdentity) {
    54         this.userIdentity = userIdentity;
    55         return this;
    56     }
    57 
    58     public String getUserMobile() {
    59         return userMobile;
    60     }
    61     
    62     public User setUserMobile(String userMobile) {
    63         this.userMobile = userMobile;
    64         return this;
    65     }
    66 
    67     public String getClassId() {
    68         return classId;
    69     }
    70     
    71     public User setClassId(String classId) {
    72         this.classId = classId;
    73         return this;
    74     }
    75 }
    builder化的User实体类

      (当然这里不推荐用这种方式,一般用withXXX这种函数名格式,setXXX用这个不符合规范)

      

      当然这么做也有问题。如果这个User不是单纯在本地程序中使用,还需要请求外部返回数据(这里builder化的实体类也可以与MySQL交互),并且返回实体类的数据结构与请求的数据结构不同(很多rest API的请求数据结构就是这样),请求的builder和响应的实体类也要区分开。User的builder就要这样写:

     1 public class UserBuilder {
     2 
     3     private Integer id;
     4 
     5     private String password;
     6 
     7     private String username;
     8 
     9     private Integer userIdentity;
    10 
    11     private String userMobile;
    12     
    13     private String classId;
    14 
    15     public User build(){
    16         User user = new User();
    17         user.id = id;
    18         user.password = password;
    19         user.username = username;
    20         user.userIdentity = userIdentity;
    21         user.userMobile = userMobile;
    22         user.classId = classId;
    23         return user;
    24     }
    25     
    26     /**
    27      * 班级ID
    28      */
    29     @Column(name = "`class_id`")
    30     @ApiModelProperty(value = "班级ID(id1,id2,id3...)",example = "班级ID(id1,id2,id3...)",required = false)
    31     private String classId;
    32 
    33     public Integer getId() {
    34         return id;
    35     }
    36 
    37     public User withId(Integer id) {
    38         this.id = id;
    39         return this;
    40     }
    41 
    42     public String getPassword() {
    43         return password;
    44     }
    45 
    46     public User withPassword(String password) {
    47         this.password = password;
    48         return this;
    49     }
    50 
    51     public String getUsername() {
    52         return username;
    53     }
    54 
    55     public User withUsername(String username) {
    56         this.username = username;
    57         return this;
    58     }
    59 
    60     public Integer getUserIdentity() {
    61         return userIdentity;
    62     }
    63 
    64     public User withUserIdentity(Integer userIdentity) {
    65         this.userIdentity = userIdentity;
    66         return this;
    67     }
    68 
    69     public String getUserMobile() {
    70         return userMobile;
    71     }
    72     
    73     public User withUserMobile(String userMobile) {
    74         this.userMobile = userMobile;
    75         return this;
    76     }
    77 
    78     public String getClassId() {
    79         return classId;
    80     }
    81     
    82     public User withClassId(String classId) {
    83         this.classId = classId;
    84         return this;
    85     }
    86 }
    真正的builder

      对应的User就可以按照返回的数据结构定制了。

      

      还有一种情况,请求的数据结构比较复杂,某些属性下还有字典和数组,这种情况类似于汽车的生产,里面有些部件不是标准的,一定要预先搭建好(对应于某些属性下的字典),有些组件还不止一个(对应于某些属性下的数组),有些组件如果要修改,与之相关的组件也要进行改动(对象内属性关联修改),那时就需要给类设定一次设置好相关属性的函数。

      像连接kubernetes创建副本控制器(replicationController)的builder,就使用了批量设置复杂对象的函数。

      创建副本控制器的builder:

     1 package io.kubernetes.client.models;
     2 
     3 import io.kubernetes.client.fluent.*;
     4 
     5 public class V1ReplicationControllerBuilder extends V1ReplicationControllerFluentImpl<V1ReplicationControllerBuilder> implements VisitableBuilder<V1ReplicationController, V1ReplicationControllerBuilder>
     6 {
     7     V1ReplicationControllerFluent<?> fluent;
     8     Boolean validationEnabled;
     9     
    10     public V1ReplicationControllerBuilder() {
    11         this(Boolean.valueOf(true));
    12     }
    13     
    14     public V1ReplicationControllerBuilder(final Boolean validationEnabled) {
    15         this(new V1ReplicationController(), validationEnabled);
    16     }
    17     
    18     public V1ReplicationControllerBuilder(final V1ReplicationControllerFluent<?> fluent) {
    19         this(fluent, Boolean.valueOf(true));
    20     }
    21     
    22     public V1ReplicationControllerBuilder(final V1ReplicationControllerFluent<?> fluent, final Boolean validationEnabled) {
    23         this(fluent, new V1ReplicationController(), validationEnabled);
    24     }
    25     
    26     public V1ReplicationControllerBuilder(final V1ReplicationControllerFluent<?> fluent, final V1ReplicationController instance) {
    27         this(fluent, instance, true);
    28     }
    29     
    30     public V1ReplicationControllerBuilder(final V1ReplicationControllerFluent<?> fluent, final V1ReplicationController instance, final Boolean validationEnabled) {
    31         (this.fluent = fluent).withApiVersion(instance.getApiVersion());
    32         fluent.withKind(instance.getKind());
    33         fluent.withMetadata(instance.getMetadata());
    34         fluent.withSpec(instance.getSpec());
    35         fluent.withStatus(instance.getStatus());
    36         this.validationEnabled = validationEnabled;
    37     }
    38     
    39     public V1ReplicationControllerBuilder(final V1ReplicationController instance) {
    40         this(instance, Boolean.valueOf(true));
    41     }
    42     
    43     public V1ReplicationControllerBuilder(final V1ReplicationController instance, final Boolean validationEnabled) {
    44         ((V1ReplicationControllerFluentImpl<V1ReplicationControllerFluent>)(this.fluent = this)).withApiVersion(instance.getApiVersion());
    45         this.withKind(instance.getKind());
    46         this.withMetadata(instance.getMetadata());
    47         this.withSpec(instance.getSpec());
    48         this.withStatus(instance.getStatus());
    49         this.validationEnabled = validationEnabled;
    50     }
    51     
    52     @Override
    53     public V1ReplicationController build() {
    54         final V1ReplicationController buildable = new V1ReplicationController();
    55         buildable.setApiVersion(this.fluent.getApiVersion());
    56         buildable.setKind(this.fluent.getKind());
    57         buildable.setMetadata(this.fluent.getMetadata());
    58         buildable.setSpec(this.fluent.getSpec());
    59         buildable.setStatus(this.fluent.getStatus());
    60         return buildable;
    61     }
    62     
    63     @Override
    64     public boolean equals(final Object o) {
    65         if (this == o) {
    66             return true;
    67         }
    68         if (o == null || this.getClass() != o.getClass()) {
    69             return false;
    70         }
    71         if (!super.equals(o)) {
    72             return false;
    73         }
    74         final V1ReplicationControllerBuilder that = (V1ReplicationControllerBuilder)o;
    75         Label_0088: {
    76             if (this.fluent != null && this.fluent != this) {
    77                 if (this.fluent.equals(that.fluent)) {
    78                     break Label_0088;
    79                 }
    80             }
    81             else if (that.fluent == null || this.fluent == this) {
    82                 break Label_0088;
    83             }
    84             return false;
    85         }
    86         if (this.validationEnabled != null) {
    87             if (this.validationEnabled.equals(that.validationEnabled)) {
    88                 return true;
    89             }
    90         }
    91         else if (that.validationEnabled == null) {
    92             return true;
    93         }
    94         return false;
    95     }
    96 }
    ReplicationControllerBuilder

      这里只声明了构造函数和build函数,属性的变更是继承ReplicationController的:

      1 package io.kubernetes.client.models;
      2 
      3 import com.google.gson.annotations.*;
      4 import io.swagger.annotations.*;
      5 import java.util.*;
      6 
      7 @ApiModel(description = "ReplicationController represents the configuration of a replication controller.")
      8 public class V1ReplicationController
      9 {
     10     @SerializedName("apiVersion")
     11     private String apiVersion;
     12     @SerializedName("kind")
     13     private String kind;
     14     @SerializedName("metadata")
     15     private V1ObjectMeta metadata;
     16     @SerializedName("spec")
     17     private V1ReplicationControllerSpec spec;
     18     @SerializedName("status")
     19     private V1ReplicationControllerStatus status;
     20     
     21     public V1ReplicationController() {
     22         this.apiVersion = null;
     23         this.kind = null;
     24         this.metadata = null;
     25         this.spec = null;
     26         this.status = null;
     27     }
     28     
     29     public V1ReplicationController apiVersion(final String apiVersion) {
     30         this.apiVersion = apiVersion;
     31         return this;
     32     }
     33     
     34     @ApiModelProperty("APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources")
     35     public String getApiVersion() {
     36         return this.apiVersion;
     37     }
     38     
     39     public void setApiVersion(final String apiVersion) {
     40         this.apiVersion = apiVersion;
     41     }
     42     
     43     public V1ReplicationController kind(final String kind) {
     44         this.kind = kind;
     45         return this;
     46     }
     47     
     48     @ApiModelProperty("Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds")
     49     public String getKind() {
     50         return this.kind;
     51     }
     52     
     53     public void setKind(final String kind) {
     54         this.kind = kind;
     55     }
     56     
     57     public V1ReplicationController metadata(final V1ObjectMeta metadata) {
     58         this.metadata = metadata;
     59         return this;
     60     }
     61     
     62     @ApiModelProperty("If the Labels of a ReplicationController are empty, they are defaulted to be the same as the Pod(s) that the replication controller manages. Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata")
     63     public V1ObjectMeta getMetadata() {
     64         return this.metadata;
     65     }
     66     
     67     public void setMetadata(final V1ObjectMeta metadata) {
     68         this.metadata = metadata;
     69     }
     70     
     71     public V1ReplicationController spec(final V1ReplicationControllerSpec spec) {
     72         this.spec = spec;
     73         return this;
     74     }
     75     
     76     @ApiModelProperty("Spec defines the specification of the desired behavior of the replication controller. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status")
     77     public V1ReplicationControllerSpec getSpec() {
     78         return this.spec;
     79     }
     80     
     81     public void setSpec(final V1ReplicationControllerSpec spec) {
     82         this.spec = spec;
     83     }
     84     
     85     public V1ReplicationController status(final V1ReplicationControllerStatus status) {
     86         this.status = status;
     87         return this;
     88     }
     89     
     90     @ApiModelProperty("Status is the most recently observed status of the replication controller. This data may be out of date by some window of time. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status")
     91     public V1ReplicationControllerStatus getStatus() {
     92         return this.status;
     93     }
     94     
     95     public void setStatus(final V1ReplicationControllerStatus status) {
     96         this.status = status;
     97     }
     98     
     99     @Override
    100     public boolean equals(final Object o) {
    101         if (this == o) {
    102             return true;
    103         }
    104         if (o == null || this.getClass() != o.getClass()) {
    105             return false;
    106         }
    107         final V1ReplicationController v1ReplicationController = (V1ReplicationController)o;
    108         return Objects.equals(this.apiVersion, v1ReplicationController.apiVersion) && Objects.equals(this.kind, v1ReplicationController.kind) && Objects.equals(this.metadata, v1ReplicationController.metadata) && Objects.equals(this.spec, v1ReplicationController.spec) && Objects.equals(this.status, v1ReplicationController.status);
    109     }
    110     
    111     @Override
    112     public int hashCode() {
    113         return Objects.hash(this.apiVersion, this.kind, this.metadata, this.spec, this.status);
    114     }
    115     
    116     @Override
    117     public String toString() {
    118         final StringBuilder sb = new StringBuilder();
    119         sb.append("class V1ReplicationController {
    ");
    120         sb.append("    apiVersion: ").append(this.toIndentedString(this.apiVersion)).append("
    ");
    121         sb.append("    kind: ").append(this.toIndentedString(this.kind)).append("
    ");
    122         sb.append("    metadata: ").append(this.toIndentedString(this.metadata)).append("
    ");
    123         sb.append("    spec: ").append(this.toIndentedString(this.spec)).append("
    ");
    124         sb.append("    status: ").append(this.toIndentedString(this.status)).append("
    ");
    125         sb.append("}");
    126         return sb.toString();
    127     }
    128     
    129     private String toIndentedString(final Object o) {
    130         if (o == null) {
    131             return "null";
    132         }
    133         return o.toString().replace("
    ", "
        ");
    134     }
    135 }
    ReplicationController

      本例子中,副本控制器包含了以下属性:

    Kind
    ApiVersion
    Metadata
    --labelMap
    --name
    Spec
    --Replicas
    --labelMap
    --template
    ----Metadata
    ------labelMap
    --------labelSelector
    ------name
    ----podSpec
    ------containers
    ------volumes

      里面有好些属性是重复的,如果不用建造者模式控制会造成某些地方的属性不一致;并且,创建副本控制器的参数多且杂,如果全部平摊成单独属性,改变某些组属性要一行行改,很繁琐,且容易出错。使用建造者模式,像上面的标签集就不用一个个传,对于批量创建某些标签集类似的容器的场合很方便。

      这样既可实现比较简单的builder,又无需重复编写代码,复用对应的实体类函数。

      优点: 1、建造者独立,易扩展。 2、便于控制细节风险。

      缺点: 1、产品必须有共同点,范围有限制。(像本例的副本控制器,换了品类就得推到重来) 2、如内部变化复杂,会有很多的建造类。(还好本例只有一种副本控制器)

      使用场景: 1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。

      注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。

  • 相关阅读:
    Investment_完全背包
    Recaman's Sequence_递推
    MooFest_二维树状数组
    Mobile phones_二维树状数组
    Matrix_二维树状数组
    Get Many Persimmon Trees_枚举&&二维树状数组
    树状数组基础知识
    The Cow Lineup_找规律
    [SOJ] 简单哈希
    sscanf() 函数
  • 原文地址:https://www.cnblogs.com/dgutfly/p/11617867.html
Copyright © 2011-2022 走看看