『設計』の基本中の基本その③:いよいよラスト!『段階的な詳細化』、そして伝説へ。。。♪


<アイデアが確定した日>
2022年04月03日。。。だな、うん(;´∀`)|||

<ざっくりとした環境情報>
OS:Windows11
言語:Java (azul-13.0.9)
追加ライブラリ:commons-lang3-3.12.0.jar(※commons-lang3-3.12.0-bin.zip)

『ソフトウェア設計における基礎の基礎』シリーズ!?もいよいよ大詰め!たぶんみなさま《無意識》にやってる『段階的な詳細化』について♪♪♪

ふぁい♡
みなさま
こんばんは!!

さいきん
すぅぃぃいー--っかり
スクラップの立ち上げる時の気軽さにハマってる
かがみんどぇす♪♪

さて、今回は


その決定は、
何十年にもわたって進化してきた
ヒューリスティックを用いて実現される基礎的な設計コンセプト
(関心事の分離、段階的な詳細化、機能の独立)や、

ロジャー・プレスマン; ブルース・マキシム. 実践ソフトウェアエンジニアリング (第9版) (p.212). Kindle 版.


などと超著名なソフトウェアエンジニアリング本
書かれていたので、
かがみが勝手に『NY三部作』みたいにしちゃった(;´∀`)、
【『設計』の基本中の基本その③:段階的詳細化】
についてご投稿させて戴きま~す♪♪♪

あ、ちなみに関連記事は下記になりまぁす

その①:関心事の分離

https://zenn.dev/kagaminpower003/articles/6fbb75699a5a28

その②:機能の独立

https://zenn.dev/kagaminpower003/articles/aa273faa24982e

ではではさっそくサンプルコード♪♪

パターンその①:

→『親=メイン処理』中身の一部を、
『親=メイン処理』とは別のメソッドに分けて実装する。

Judge_SoshikiMeiConstraint.java
package chapter02.section02.domain.model.organizationhierarchy;

import chapter02.section02.domain.model.organizationhierarchy.value_object.*;

//『組織名制約』クラス(の、つもり。。。)
class Judge_SoshikiMeiConstraint {
    private final KaisouMei kaisouMei;
    private final SoshikiMei soshikiMei;

    //コンストラクタ
    Judge_SoshikiMeiConstraint( KaisouMei kaisouMei , SoshikiMei soshikiMei ){
        this.kaisouMei = kaisouMei;
        this.soshikiMei = soshikiMei;
    }

    //////////////////////////////////////////////
    //組織名チェック
    //※段階的詳細化でめっちゃスッキリ♪♪♪
    //////////////////////////////////////////////
    Boolean isSoshikiMei() {

        //内容チェック(偽)
        if(this.isBadContents()) { return false; }

        //二重登録チェック
        if(this.isDouble())      { return false; }

        //最終結果
        return true;
    }

    //組織名チェック(偽)
    Boolean isBadSoshikiMei() {
        return !this.isSoshikiMei();
    }

    //内容チェック
    private Boolean isContents() {

        //『組織名内容チェック』クラスにマトリョーシカ♪
        return new Check_SoshikiMeiContents(kaisouMei , soshikiMei)
                .isContents();
    }

    //内容チェック(偽)
    private Boolean isBadContents() {
        return !this.isContents();
    }

    //二重登録チェック
    private Boolean isDouble() {
        
	//『組織名二重登録チェック』クラスにマトリョーシカ♪♪
        return new Check_DoubleEntrySoshikiMei(kaisouMei, soshikiMei)
                .isDouble();
    }
}

→ほい、『組織名チェック処理( =isSoshikiMei() )』の具体的な
中身である、

内容チェック

二重登録チェック
について、

『組織名チェック処理( =isSoshikiMei() )』とは
別メソッド

『内容チェック(偽)処理( =isBadContents() )』

『二重登録チェック処理( =isDouble() )』

分けておりますよね(^^

とまぁこんな風に、
処理内容が、
どーしても大きな粒度になる場合に、

「メインルーチン>サブルーチン」のように、
プログラムを分けて、
1つ1つの処理の流れを読みやすくする、
という設計手法が

『段階的詳細化』

というものになります(^^♪


パターンその②:

→パターン①と同じように『段階的詳細化』を実施し、その後進化した結果、
『親=メイン処理』クラスとは別クラスとして分けて実装する。

①『段階的詳細化』済み、かつ 『機能の独立』【前】

組織.java
class 組織 implements 組織階層{

    private List<組織階層> list = null;
    private int level = 0;
    private String 階層名 = null;
    private String 組織名 = null;

    public 組織(int level,String 階層名,String 組織名){
        this.level = level;
        this.階層名 = 階層名;
        this.組織名 = 組織名;
        list = new ArrayList<組織階層>();
    }

    public void add(組織階層 entry){
        list.add(entry);
    }

    public void 組織一覧表(){
        System.out.println(makeTree());
        組織一覧表生成();
    }

    private void 組織一覧表生成(){
        Iterator<組織階層> itr = list.iterator();
        while(itr.hasNext()){
            組織階層 entry = itr.next();
            entry.組織一覧表();
        }
    }

    private String makeTree(){
        String Branch = null;
        String leaf = 階層名 + ":" + 組織名;
        String root = 組織名;

        switch (level){
            case 1:
                Branch = root;
                break;
            case 2:
                Branch = "\t" + leaf;
                break;
            case 3:
                Branch = "\t" + "\t" + leaf;
                break;
            case 4:
                Branch = "\t" + "\t" + "\t" + leaf;
                break;
        }
        return  Branch;
    }
}

②『段階的詳細化』済み、そして 『機能の独立』【後】

組織.java

class 組織 implements 組織階層{

    private List<組織階層> list = null;
    private int level = 0;
    private String 階層名 = null;
    private String 組織名 = null;

    public 組織(int level,String 階層名,String 組織名){
        this.level = level;
        this.階層名 = 階層名;
        this.組織名 = 組織名;
        list = new ArrayList<組織階層>();
    }

    public void add(組織階層 entry){
        list.add(entry);
    }

    public void 組織一覧表(){

        System.out.println(
                new 樹形図作成(this.level,this.階層名,this.組織名).実行()
        );

        new 組織一覧表作成(list).実行();
    }
}

樹形図作成.java

class 樹形図作成{

    private int level = 0;
    private String 階層名 = null;
    private String 組織名 = null;

    樹形図作成(int level,String 階層名,String 組織名){
        this.level = level;
        this.階層名 = 階層名;
        this.組織名 = 組織名;
    }

    public String 実行(){
        String Branch = null;
        String leaf = 階層名 + ":" + 組織名;
        String root = 組織名;

        switch (level){
            case 1:
                Branch = root;
                break;
            case 2:
                Branch = "\t" + leaf;
                break;
            case 3:
                Branch = "\t" + "\t" + leaf;
                break;
            case 4:
                Branch = "\t" + "\t" + "\t" + leaf;
                break;
        }
        return  Branch;
    }
}

組織一覧表作成.java

class 組織一覧表作成 {
    private List<組織階層> list = null;

    組織一覧表作成(List<組織階層> list){
        this.list = list;
    }

    public void 実行(){
        Iterator<組織階層> itr = list.iterator();
        while(itr.hasNext()){
            組織階層 entry = itr.next();
            entry.組織一覧表();
        }
    }
}

→ほいほい分けちゃった(^^♪
てか①と②でメソッド順とクラス順の順番一部逆さまだな。。。(;´∀`)||

ところで、
クラス分けをすると、
単純に考えるとコード量が増えるだけなのに、
いったいこれの何が楽しいでしょうか?

はい、
みなさま
過去記事をお読みになっている場合は、
すでにお分かりですよね、
そうです、
『機能の独立』をすると、
後からいろんな処理で『再利用』ができるんです♪
♪♪♪It's DRY!(^-^)♪♪♪

例1)

テキスト出力でもHTML出力でも『同じ』計算BLを使用


例2)

異なるドメインモデルで『同じ』判定BLを使用

※boy’s L...ぢゃないっすよ ||(´Д`;)

結びに

はい、今日お伝えしたいことは
ここまでです(^-^)

みなさま
『段階的詳細化』を実践することの利点を
ご納得されましたでしょうか?

そうそう、
そーいえば
過去のあっしのZenn記事にも、
元々は『段階的詳細化』を実施し、
その後『機能の独立』を果たしたこんなクラスが。。。

勤務状況サブステータス区分サービス.java
public class 勤務状況サブステータス区分問合せサービス {

    private String my勤務状況 = "";
    private List<勤務状況サブステータス区分アダプター> my勤務状況サブステータス区分list
            = new ArrayList<>();
~中略~
    private List<勤務状況サブステータス区分アダプター> 出社設定() {
        my勤務状況サブステータス区分list.add(new 勤務状況サブステータス区分アダプター(状態区分.稼働));
        my勤務状況サブステータス区分list.add(new 勤務状況サブステータス区分アダプター(場所区分.社内));

        return Collections.unmodifiableList(my勤務状況サブステータス区分list);
    }
~中略~
}

んで、こんな仕様変更がきて、



そぉすると、
それまでふつーに『単一』だと思っていたクラスが、
仕様変更に対応するのが『困難に』なって。。。。


Before:
<出力イメージ>

『勤務状況』が出社の場合、該当する『勤務状況サブステータス区分』は
  ●稼働
  ●社内
です。
『勤務状況サブステータス区分』が稼働の場合、
  ⇒一日のうちなんらかの仕事をした状態を指します。テレワークも含みます
『勤務状況サブステータス区分』が社内の場合、
  ⇒一日のうちなんらかの仕事をした状態を指します。ただし、テレワークは含みません。
です。

Process finished with exit code 0

After:
<出力イメージ>

『勤務状況』が出社の場合、該当する『勤務状況サブステータス区分』は
  ●稼働
  ●社内
  ●内勤
  ●------------または-------------------
  ●稼働
  ●社外
  ●外勤
です。
『勤務状況サブステータス区分』が
  ●稼働の場合、
    ⇒一日のうちなんらかの仕事をした状態を指します。テレワークも含みます
  ●社内の場合、
    ⇒一日のうちなんらかの仕事をした状態を指します。ただし、テレワークは含みません。
  ●社外の場合、
    ⇒社内以外の場所での働き方を行い、一日のうちなんらかの仕事をした状態を指します。
  ●内勤の場合、
    ⇒主にデスクワークに該当する働き方を行い、建物内での仕事をする状態を指します。
  ●外勤の場合、
    ⇒外回りの営業など、勤務時間の大半が建物外での仕事をする状態を指します。そのため、テレワークは含みません。
です。

Process finished with exit code 0

だもんで、
『関心事の分離』
の設計手法に則り分けちゃった!!
ってのが
ございましたなぁ(´∀`)♪♪♪

改修後コード_①)勤務状況サブステータス区分サービス.java

勤務状況サブステータス区分サービス.java
~中略~
    private List<勤務状況サブステータス区分インターフェース> 出社設定() {
        my勤務状況サブステータス区分list.add(勤務状況サブステータス区分アダプター.稼働);
        my勤務状況サブステータス区分list.add(勤務状況サブステータス区分アダプター.社内);
        my勤務状況サブステータス区分list.add(勤務状況サブステータス区分アダプター.内勤);
        my勤務状況サブステータス区分list.add(new 区分値区切り線());
        my勤務状況サブステータス区分list.add(勤務状況サブステータス区分アダプター.稼働);
        my勤務状況サブステータス区分list.add(勤務状況サブステータス区分アダプター.社外);
        my勤務状況サブステータス区分list.add(勤務状況サブステータス区分アダプター.外勤);

        return Collections.unmodifiableList(my勤務状況サブステータス区分list);
    }
~中略~

改修後コード_②)勤務状況サブステータス区分補足説明サービス.java


~中略~
    private List<勤務状況サブステータス区分インターフェース> 出社設定() {
        my勤務状況サブステータス区分list.add( 勤務状況サブステータス区分アダプター.稼働 );
        my勤務状況サブステータス区分list.add( 勤務状況サブステータス区分アダプター.社内 );
        my勤務状況サブステータス区分list.add( 勤務状況サブステータス区分アダプター.社外 );
        my勤務状況サブステータス区分list.add( 勤務状況サブステータス区分アダプター.内勤 );
        my勤務状況サブステータス区分list.add( 勤務状況サブステータス区分アダプター.外勤 );

        return Collections.unmodifiableList(my勤務状況サブステータス区分list);
    }
~中略~

とまぁ、みなさま♪
いろいろと無理難題そうな
ごんぶと本とかオススメしちゃってる
かがみですが、

オブジェクト指向とか
デザインパターンとか
レイヤーアーキテクチャーとか
言う前に、

まずは
気軽に
『倣うより慣れろ』で、

そんな感じで良いんじゃないのかなぁ~っと、
ガチでそう思いますよ~♪♪♪(´∀`)♪♪♪ ♪

みなさま、『ソフトウェア開発』をぜひとも楽しみましょう♪
おべんきょよりもまずはそれから♪♪♪

それでは、
また~♪♪♪


まとめ♡


その①:関心事の分離

https://zenn.dev/kagaminpower003/articles/6fbb75699a5a28

その②:機能の独立

https://zenn.dev/kagaminpower003/articles/aa273faa24982e

その③:段階的詳細化(※当記事)

https://zenn.dev/kagaminpower003/articles/2a7b1caa3c9aba

そしてDDDの道へ。。。