BETA

【OODメモ】ファーストクラスコレクション

投稿日:2020-03-29
最終更新:2020-03-29

ファーストクラスコレクションとは

ファーストクラスコレクションとは書籍『現場で役立つシステム設計の原則』で紹介されている実装プラクティス(初出は『ThoughtWorksアンソロジー』?)。
ドメインオブジェクトが他のドメインオブジェクトや値オブジェクトを複数個保持するときに、java.util.Listなどの言語標準の型を使うのではなく、専用の型を用意しようというもの。
ドメイン固有の型を定義して第一級オブジェクトとして扱おうということからファーストクラスコレクションと名付けられている(たぶん)。

実装例

以下はjava.util.Listを使った通常の実装。
UserオブジェクトはLoan(貸出)オブジェクトを保持している。notifyDeadlineメソッド内で最早の返却日を取得するためにListの中身を走査して処理をしている。

    public class User {  
        private String name;  
        private LocalDate expirationDate;  
        private List<Loan> loans;  

        public User(String name, LocalDate expirationDate, List<Loan> loans) {  
            this.name = name;  
            this.expirationDate = expirationDate;  
            this.loans = loans;  
        }  

        /**  
         * 返却日が迫っていたら通知をする.  
         *  
         * @param today 本日  
         */  
        public void notifyDeadline(LocalDate today) {  
            // 貸出オブジェクトの中で最も早い返却日を取得する  
            Optional<LocalDate> earliestDueDate =  
                    loans.stream().map(Loan::getDueDate).min(Comparator.comparing(it -> it));  

            earliestDueDate.ifPresent(dueDate -> {  
                if (dueDate.minusDays(3L).isBefore(today)) {  
                    this.doNotify();  
                }  
            });  
        }  

        private void doNotify() {}  
    }  

この処理は果たしてUserの責務として相応しいだろうか?同じような処理があちこちに重複してしまうリスクもある。

そこで、ファーストクラスコレクションとして専用のコレクション型を導入する。
Loansオブジェクトは内部ではjava.util.Listを使ってLoanオブジェクトを管理するが、外部に対してはドメインの要件で必要な振る舞いのみを公開する。User#notifyDeadline内に記述されていた処理を抜き出して、getEarliestDueDateメソッドとして定義した。

public class Loans {  

    private List<Loan> loanList;  

    public static Loans of(Loan... loans) {  
        List<Loan> loanList = Arrays.asList(loans);  
        return new Loans(loanList);  
    }  

    public static Loans empty() {  
        return new Loans(new ArrayList<>());  
    }  

    private Loans(List<Loan> loanList) {  
        this.loanList = loanList;  
    }  

    public void add(Loan loan) {  
        loanList.add(loan);  
    }  

    public int count() {  
        return loanList.size();  
    }  

    public Optional<LocalDate> getEarliestDueDate() {  
        return loanList.stream().map(Loan::getDueDate).min(Comparator.comparing(it -> it));  
    }  
}  

Userクラスはjava.util.ListではなくLoansオブジェクトを保持するように修正し、Loans#getEarliestDueDateを呼び出して処理を行うようにする。

public class User {  

    private String name;  
    private Loans loans;  
    private LocalDate expirationDate;  

    public User(String name, LocalDate expirationDate, Loans loans) {  
        this.name = name;  
        this.expirationDate = expirationDate;  
        this.loans = loans;  
    }  

    public Loans getLoans() {  
        return loans;  
    }  

    /**  
     * 返却日が迫っていたら通知をする.  
     *  
     * @param today 本日  
     */  
    public void notifyDeadline(LocalDate today) {  
        // 貸出オブジェクトの中で最も早い返却日を取得する  
        Optional<LocalDate> earliestDueDate = loans.getEarliestDueDate();  

        earliestDueDate.ifPresent(dueDate -> {  
            if (dueDate.minusDays(3L).isBefore(today)) {  
                this.doNotify();  
            }  
        });  
    }  

}  

まとめ

  • ファーストクラスコレクションは、オブジェクトの集合に対する操作を定義する場所として適している。
  • ドメインの知識を型で表現することはオブジェクト指向設計の肝である。
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

日々学んだことや感じたことをゆる〜く書いていこうと思います

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう