JavaチェーンプログラミングとBuilder(ビルダー)設計パターン

1. チェーンプログラミング

1.1. 解釈 

        カスケード プログラミングとも呼ばれる連鎖プログラミングは、オブジェクトの関数を呼び出すときにオブジェクト自体を指す this オブジェクトを返し、連鎖効果を実現し、カスケードで呼び出すことができます。 

1.2. 特徴 

1 つのメソッド        を通じて複数のメソッド呼び出しを呼び出すことができ、複数のメソッド呼び出しをリンクして「チェーン」を形成できるため、コードの可読性が向上します。 

 1.2. 原則

        チェーン プログラミングの原理は、 this objectを返すこと、つまりオブジェクト自体を返してチェーン効果を実現することです。 

1.3. 利点 

        強力なプログラミング、簡潔なコード、そして優れた可読性。

1.4. 使用条件 

        メソッドは現在のオブジェクトを返す (つまり this を返す)、またはメソッドはチェーン呼び出しをサポートします (つまり、メソッドを呼び出したオブジェクトを返す)。 

1.4.1. 使用例 

import lombok.Data;

/**
 * 要实现链式编程,我们需要让setParam1()和setParam2()都返回this。
 * 当手写set方法时,无需 @Data注解
 * 在此仅为了使用 @Data的toString()
 */
@Data
public class Sample {

    private String param1;
    private String param2;
    private String param3;

    public Sample setParam1(String param1) {
        this.param1 = param1;
        return this;
    }
    public Sample setParam2(String param2) {
        this.param2 = param2;
        return this;
    }
    public Sample setParam3(String param3) {
        this.param3 = param3;
        return this;
    }
}

 1.4.2. 出力例

2、文字列チェーンの例

2.1. ソースコード 

         たとえば、オブジェクト toString() を String に変換した後、または String オブジェクト valueOf() を割り当てた後、 concat() スプライシング、 replace() 置換、および substring() インターセプトを実行します準拠するメソッドは多数あり、そのほとんどはチェーン プログラミングに準拠するようにアセンブルできます。

3、StringBuffer と StringBuilder チェーンの例

3.1. ソースコード 

3.2. 使用例 

        同時に、Collection インターフェースの下の List のストリームもチェーン プログラミングを使用しますが、ここでは紹介しません。 

4. @Accessors アノテーションによりチェーン プログラミングが可能になります 

4.1. ソースコード 

@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface Accessors {
	/**
	 * If true, accessors will be named after the field and not include a {@code get} or {@code set}
	 * prefix. If true and {@code chain} is omitted, {@code chain} defaults to {@code true}.
	 * <strong>default: false</strong>
	 * 
	 * @return Whether or not to make fluent methods (named {@code fieldName()}, not for example {@code setFieldName}).
	 */
	boolean fluent() default false;
	
	/**
	 * If true, setters return {@code this} instead of {@code void}.
     * 如果是true,setters方法返回{this} 而不是{void}
     * 
	 * <strong>default: false</strong>, unless {@code fluent=true}, then <strong>default: true</strong>
	 * 
	 * @return Whether or not setters should return themselves (chaining) or {@code void} (no chaining).
	 */
	boolean chain() default false;
	
	/**
	 * If present, only fields with any of the stated prefixes are given the getter/setter treatment.
	 * Note that a prefix only counts if the next character is NOT a lowercase character or the last
	 * letter of the prefix is not a letter (for instance an underscore). If multiple fields
	 * all turn into the same name when the prefix is stripped, an error will be generated.
	 * 
	 * @return If you are in the habit of prefixing your fields (for example, you name them {@code fFieldName}, specify such prefixes here).
	 */
	String[] prefix() default {};
}

 4.2. アノテーションの使用

@Data
@Accessors(chain = true)
public class Sample {

    private String param1;
    private String param2;
    private String param3;

}

        上記のコードは 1.4.1 のサンプルコードを簡略化したものですが、アノテーションを使用する場合、特定のチェーン実装を自分で設計する必要はなく、アノテーションによってチェーン操作が開始されることがわかります。このアノテーションは Spring のエンティティ クラスでよく使用されます。 

        同時に @Accessors(fluent = true) を使用すると、オブジェクトに値を代入および取得するときに set と get プレフィックスを省略し、set なしでオブジェクトを直接使用して要素を指すことができます。 

 4.3. テスト

 5. Builder モードを使用して Java チェーン プログラミングを実現する

        Builder パターンは、オブジェクトを作成してそのプロパティ値を設定できるようにするオブジェクト作成パターンです。Builder パターンの実装例は次のようになります。        

public class Sample {

    private String name;
    private String gender;
    private String age;

    private Sample(){}
    public static SampleBuilder builder() {
        return new SampleBuilder();
    }

    public static class SampleBuilder {

        private Sample sample = new Sample();

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

        public SampleBuilder setGender(String gender) {
            sample.gender = gender;
            return this;
        }
        public SampleBuilder setAge(String age) {
            sample.age = age;
            return this;
        }

        public Sample build() {
            return sample;
        }
    }
}

        上記のコード例では、最初に Sample クラスを作成しました。Builder パターンを実装するために、SampleBuilder クラスも作成しました。SampleBuilder クラスには 2 つの with メソッドがあり、それぞれ名前属性、性別属性、年齢属性を設定するために使用されます。これらはすべて SampleBuilder オブジェクトを返すため、チェーン呼び出しを実現できます。パラメータを設定した後、build メソッドを使用して Sample オブジェクトを作成します。Builder パターンを使用したチェーン プログラミングの例は次のとおりです。

         上記の例では、Sample.builder() を通じて SampleBuilder オブジェクトを作成し、set メソッドを使用してパラメータを設定しました。セットアップが完了したら、build メソッドを使用して Sample オブジェクトを作成します。

        上記は最も単純なビルダー チェーン プログラミングですが、そのシンプルさは、チェーンを形成して値を割り当てることができるという点にあります。しかし、値を取得してパラメータを確認する必要がある場合、このクラスでは不十分であり、さらに多くの規則を追加する必要があり、これが Java の重要な設計パターンの 1 つであるビルダー パターンにつながります。

6、Builder(ビルダー)デザインパターン 

6.1. 実用範囲

複雑なオブジェクト        を作成するアルゴリズムが、そのオブジェクトの構成要素とその組み立て方法から独立している必要がある場合。

        構築手順で、構築されるオブジェクトのさまざまな表現を許可する必要がある場合。

 6.2. 役割

このようなデザインパターン        では、次の役割があります。

        1 ビルダー: 製品オブジェクトを作成する各コンポーネントの抽象インターフェイスを指定します。

        2 ConcreteBuilder: Builder のインターフェイスを実装して、製品のさまざまな部分を構築および組み立て、作成する表現を定義および指定し、製品を取得するためのインターフェイスを提供します。

        3 Director: Builder インターフェイスを使用してオブジェクトを構築します。

        4 製品: 構築される複雑なオブジェクトを表します。ConcreteBuilder は、製品の内部表現を作成し、構成部品を定義するクラスや、これらの部品を最終製品に組み立てるためのインターフェイスなど、そのアセンブリ プロセスを定義します。

 6.3. ビルダーモードの作成手順

        1) クラス内で、外部クラスのすべてのプロパティを含む静的内部クラスを作成します。

        2) 内部クラスで、各属性に値を割り当てます。

        3) 内部クラスでbuild メソッドを作成し、外部クラスのオブジェクトを返します。

        4)外部クラスで 静的ビルダーメソッドを作成し、内部クラス オブジェクトを返します。

6.4. 不良品の例 

public class Sample {

    //Sample 类的属性都是不可变的。所有的属性都添加了final修饰符,在构造方法中设置了值。
    private final String name;
    private final String gender;
    private final Double age;
    //对外只提供getters方法。
    public String getName() {
        return name;
    }
    public String getGender() {
        return gender;
    }
    public Double getAge() {
        return age;
    }
    //Sample 类的构造方法是私有的。也就是说调用者不能直接创建User对象。
    private Sample(SampleBuilder builder){
        this.name = builder.name;
        this.gender = builder.gender;
        this.age = builder.age;
    }
    public static class SampleBuilder {
        private String name;
        private String gender;
        private Double age;
        public SampleBuilder SampleBuilder(){
            return new SampleBuilder();
        }

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

        public SampleBuilder setGender(String gender) {
            this.gender = gender;
            return this;
        }
        public SampleBuilder setAge(Double age) {
            this.age = age;
            return this;
        }

        public Sample build() {
            //添加参数校验
            Sample sample = new Sample(this);
            if (sample.getAge() < 0 || sample.getAge() > 125){
                throw new IllegalStateException("Age out of range:" + sample.getAge());
            }
            if (!("男".equals(sample.getGender()) || "女".equals(sample.getGender()))){
                throw new IllegalStateException("Error Gender Input:" + sample.getGender());
            }
            return sample;
        }
    }
}

         基本的なチェーン プログラミングが形成され、同時にパラメーターが検証され、要件を満たしていることがわかります。しかし、なぜ欠陥品と言われるのかというと、それは単なるビルドチェーン開発であって、デザインパターンとなると明らかに「美徳に反する」からです。

6.5. 簡単な例

6.5.1. ディレクトリの例 

 

6.5.2. 各層のコード 

        ビルダー モードのコア機能は、複雑なオブジェクト (特に、複数のオブジェクトで構成されるオブジェクト)の作成を抽出して実現するという利点もあり、ユーザーがそのような複雑なオブジェクトを使用する際の利便性を提供します。複雑なオブジェクトの部分的な作成と全体的なアセンブリの間に橋が架けられ、部分的なアセンブリと全体的なアセンブリの分離が実現されます。

        ビルダー パターンは主に複雑なオブジェクトで使用され、単純なオブジェクトには役に立ちません。ただし、ここでもビルダー パターンを理解するためにいくつかの単純なオブジェクトが必要です。

動物: 抽象プロパティを設定する

package com.test5;

public class Animal {
    //名字
    private String name;
    //时速
    private Long speed;

    public String getName() {
        return name;
    }

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

    public Long getSpeed() {
        return speed;
    }

    public void setSpeed(Long speed) {
        this.speed = speed;
    }
}

アニマルビルダー : 

package com.test5;

public interface AnimalBuilder {
    void buildName();
    void buildSpeed();

    Animal builderAnimal();
}

 動物監督:

package com.test5;

public class AnimalDirector {
    public Animal constructAnimal(AnimalBuilder pb) {
        pb.buildName();
        pb.buildSpeed();
        return pb.builderAnimal();
    }
}

 イーグル: キャラクター 1 を作成します

package com.test5;

public class Eagle extends Animal{
}

EagleBuilder: キャラクター 1 を構築する 

package com.test5;

public class EagleBuilder implements AnimalBuilder{

    Animal animal;

    public EagleBuilder(){
        animal = new Animal();
    }

   public void buildName(){
        animal.setName("雄鹰");
   }

   public void buildSpeed(){
        animal.setSpeed(80L);
   }

    public Animal builderAnimal(){
        return animal;
   }
}

ウサギ: ロール 2 を作成します 

package com.test5;

public class Rabbit extends Animal{
}

RabbitBuilder: キャラクター 2 を作成する 

package com.test5;

public class RabbitBuilder implements AnimalBuilder{

    Animal animal;

    public RabbitBuilder(){
        animal = new Animal();
    }

   public void buildName(){
        animal.setName("兔子");
   }

   public void buildSpeed(){
        animal.setSpeed(70L);
   }

    public Animal builderAnimal(){
        return animal;
   }
}

        新しいキャラクターを追加するたびに、新しいキャラクターを追加し、後で使用できるようにそれにビルドを追加するだけで済みます。 

テスト: 

package com.test5;

public class test {

    public static void main(String[] args) {

        AnimalDirector director = new AnimalDirector();

        Animal eagle = director.constructAnimal(new EagleBuilder());
        System.out.println(eagle.getName()+"的时速为:"+eagle.getSpeed());

        Animal rabbit = director.constructAnimal(new RabbitBuilder());
        System.out.println(rabbit.getName()+"的时速为:"+rabbit.getSpeed());

    }
}

結果の例: 

雄鹰的时速为:80
兔子的时速为:70

おすすめ

転載: blog.csdn.net/weixin_52255395/article/details/131553891