再包装-味6可変データ


に入る


このヒントは、インフラストラクチャの白起善の「改造する」を学んで整理したものです.

におい可変データ


Mutable Data

  • のデータを変更すると、予期せぬ結果や解決しにくいエラーが発生する可能性があります.
  • 関数プログラミング言語は、データを変更せずにコピーを渡します.しかし、他のプログラミング言語では、データの変更が許可されています.したがって、データの変更に伴うリスクを管理する方法をお勧めします.
  • 再包装
  • 「変数のカプセル化」を使用して、データを変更できる方法を制限および管理できます.
  • 「変数の分割」を使用して、複数のデータを格納する変数を分割できます.
  • 「クリーンアップコード」を使用して、データの変更を回避するコードを分離できます.
  • 「抽出関数」により、変更データのコードから枠線のないコードを分離できます.
  • 「クエリー関数と変更関数の分離」(Separate Query from Modifier)を適用すると、クライアントが必要とする場合にのみサイドシフト部品を持つ関数を呼び出すためにAPIを改善できます.
  • 可能であれば「移動設定方法」が適用されます.
  • で計算された値には、「派生変数をクエリー関数に変換」を適用します.
  • 変数の使用範囲を制限するには、「複数の関数をクラスに結合」または「複数の関数を変換関数に結合」を適用します.
  • 「参照を値に変換」を使用すると、データの一部を変更するのではなく、データ全体を置き換えることができます.
  • 変数の分割


    Split Variable

  • 変数の再割り当てが必要な場合
    ループ
  • の文を繰り返すための変数またはインデックス
  • の値を蓄積するための変数
  • また、再割り当てされた変数がある場合、この変数は多様な用途に使用でき、変数を分離してこそ、より理解しやすいコードを生成することができる.
  • 変数には責任が与えられます.
  • 定数を活用します.(javascriptのconst、javaのfinal)
  • サンプルコード

    public class Order {
    
        public double discount(double inputValue, int quantity) {
            if (inputValue > 50) inputValue = inputValue - 2;
            if (quantity > 100) inputValue = inputValue - 1;
            return inputValue;
        }
    }

    におい


    パラメータinputValueの値の使用

    解決する


    変数を分けましょう.

    改造後

    public class Order {
    
        public double discount(double inputValue, int quantity) {
            double result = inputValue;
            if (inputValue > 50) result -= 2;
            if (quantity > 100) result -= 1;
            return result;
        }
    }
    

    説明:


    変数としてinputValueを使用します.

    クエリー関数と変更関数の分離


    Separate Query from Modifier

  • 「枠線なしで値を問い合せる」方法はテストしやすく、方法は簡単です.
    移動障害物も便利です.
  • コマンド-クエリーの分離ルール:
    値を返す関数には、サイドシフト係数がない必要があります.
  • 「目立つ(観察可能な)側点」
    キャッシュが重要なオブジェクト状態の変化ではないと仮定します.したがって、キャッシュデータを
  • に変更する必要はありません.

    サンプルコード

    
    public class Billing {
    
        private Customer customer;
    
        private EmailGateway emailGateway;
    
        public Billing(Customer customer, EmailGateway emailGateway) {
            this.customer = customer;
            this.emailGateway = emailGateway;
        }
    
        public double getTotalOutstandingAndSendBill() {
            double result = customer.getInvoices().stream()
                    .map(Invoice::getAmount)
                    .reduce((double) 0, Double::sum);
            sendBill();
            return result;
        }
    
        private void sendBill() {
            emailGateway.send(formatBill(customer));
        }
    
        private String formatBill(Customer customer) {
            return "sending bill for " + customer.getName();
        }
    }

    におい


    getTotalOutstandingAndSendBill関数を呼び出すとsendBill()不要な関数が呼び出されます.

    解決する


    クエリー関数と名廉関数を分けて!

    改造後

    public class Billing {
    
        private Customer customer;
    
        private EmailGateway emailGateway;
    
        public Billing(Customer customer, EmailGateway emailGateway) {
            this.customer = customer;
            this.emailGateway = emailGateway;
        }
    
        public double getTotalOutstanding() {
            return customer.getInvoices().stream()
                    .map(Invoice::getAmount)
                    .reduce((double) 0, Double::sum);
        }
    
        public void sendBill() {
            emailGateway.send(formatBill(customer));
        }
    
        private String formatBill(Customer customer) {
            return "sending bill for " + customer.getName();
        }
    }
    

    説明:


    getTotalOutState()関数はクエリーにのみ使用され、senBill()関数はコマンドとクエリーを個別に呼び出すことで分離します.

    セットの削除


    Remove Setting Method


    •テナントの提供は、このフィールドが変更される可能性があることを意味します.
    •オブジェクトの作成時に最初に設定した値を変更する必要がない場合は、その値を設定できるジェネレータを作成し、変更の可能性を排除するためにセッションを削除する必要があります.

    サンプルコード

    public class Person {
    
        private String name;
    
        private int id;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    }
    

    におい


    メンバーを識別するフィールドidは変更できませんが、setterを使用します.

    解決する


    setterメソッドを除去します.

    派生変数をクエリー関数に置換


    Replace Derived Variable with Query

  • で変更可能なデータを最小限に抑える必要があります.
  • で計算された変数は削除できます.
  • 計算自体もデータの意味をうまく表現できます.
  • は、変数がどこでもエラー値に変更される可能性を排除することができる.
  • 計算に必要なデータが不変である場合、計算結果のデータも不変であるため、変数は不変のままであることができる.
  • サンプルコード

    public class ProductionPlan {
    
        private double production;
        private List<Double> adjustments = new ArrayList<>();
    
        public void applyAdjustment(double adjustment) {
            this.adjustments.add(adjustment);
            this.production += adjustment;
        }
    
        public double getProduction() {
            return this.production;
        }
    }

    におい


    applyadjustment()関数はパラメータの値をlistに挿入し、profusionフィールドに合計を保存します.これはlistで合計が分かる不要な変数です.

    解決する


    本番フィールドを削除するとlistに格納されている合計が返されます.

    改造後

    public class ProductionPlan {
    
        private List<Double> adjustments = new ArrayList<>();
    
        public void applyAdjustment(double adjustment) {
            this.adjustments.add(adjustment);
        }
    
        public double getProduction() {
            return adjustments.stream().mapToDouble(Double::valueOf).sum();
        }
    }
    

    説明:


    不要な生産フィールドを削除した後、getProductionで要約を計算します.

    複数の関数を変換関数に結合


    Combine Functions into Transform

  • に関連する複数の派生変数を複数の場所で作成して使用する場合は、これらの派生変数を変換関数で1つの場所に集約できます.
  • ソースデータを変更できる場合は、「複数の関数をクラスに結合」を使用します.
  • ソースデータが一定のままの場合、2つの方法を使用できますが、変換関数を使用して不変データのフィールドとして作成し、再使用することもできます.
  • オブジェクトを柱に移動(preserve whole method)と同じ方法を使用します.

    参照を値に置換


    -Change Reference to Value

  • 参照オブジェクトvs値オブジェクト
  • https://martinfowler.com/bliki/ValueObject.html
  • “Objects that are equal due to the value of their properties, in this case their x and y coordinates, are called value objects.”
  • 値オブジェクトは、オブジェクトが存在するフィールドの値を使用して同一性を検証します.
  • のオブジェクトは変更されません.
  • オブジェクトの変更履歴を他の場所に伝播する場合は、Reference(そうでない場合)値オブジェクトを使用します.
  • サンプルコード

    private TelephoneNumber officeTelephoneNumber;
    
        public String getOfficeAreaCode() {
            return this.officeTelephoneNumber.getAreaCode();
        }
    
        public void setOfficeAreaCode(String areaCode) {
            this.officeTelephoneNumber.setAreaCode(areaCode);
        }
    
        public String getOfficeNumber() {
            return this.officeTelephoneNumber.getNumber();
        }
    
        public void setOfficeNumber(String number) {
            this.officeTelephoneNumber.setNumber(number);
        }

    におい


    TelphoneNumberオブジェクトを参照オブジェクトではなく値オブジェクトとして使用する場合の設定

    解決する


    変更せずにsetterで新しい値オブジェクトを作成します.

    改造後


    Person

    public class Person {
    
        private TelephoneNumber officeTelephoneNumber;
    
        public String getOfficeAreaCode() {
            return this.officeTelephoneNumber.getAreaCode();
        }
    
        public String getOfficeNumber() {
            return this.officeTelephoneNumber.getNumber();
        }
    
        public void setOfficeAreaCode(String areaCode) {
            this.officeTelephoneNumber = new TelephoneNumber(areaCode, this.getOfficeNumber());
        }
    
        public void setOfficeNumber(String number) {
            this.officeTelephoneNumber = new TelephoneNumber(this.getOfficeAreaCode(), number);
        }
    
    }
    

    TelephoneNumber

    public class TelephoneNumber {
    
        private final String areaCode;
    
        private final String number;
    
        public TelephoneNumber(String areaCode, String number) {
            this.areaCode = areaCode;
            this.number = number;
        }
    
        public String getAreaCode() {
            return areaCode;
        }
    
        public String getNumber() {
            return number;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            TelephoneNumber that = (TelephoneNumber) o;
            return Objects.equals(areaCode, that.areaCode) && Objects.equals(number, that.number);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(areaCode, number);
        }
    }
    
    値オブジェクトであるためsetterを削除しequals,hashCodeメソッドを実装する.