共通化の考えを共通化したい

# 同じ形の共通化
これはやってはいけない、共通化です。

・数量が10以上の場合には、１割引きします。
・合計金額から消費税として10％を加算します。

~~~java:
public static int 割合計算(int 金額){
    return 金額 * 0.1;
}

public static int 支払金額計算(int 数量, int 単価){
    int 合計金額 = 数量 * 単価;

    if(数量 >= 10){
        合計金額 = 合計金額 - 割合計算(合計金額);    
    } 
    
    int 税額 = 割合計算(合計金額);
    return 合計金額 + 税額;
}
~~~

なんとなく、察しがつくと思いますがシステムの仕様が変更になりました。
・数量が10以上の場合には、2 ~~1~~ 割引きします。
・合計金額から消費税として10％を加算します。

~~~java:
public static int 支払金額計算(int 数量, int 単価){
    int 合計金額 = 数量 * 単価;

    if(数量 >= 10){
        合計金額 = 合計金額 - 割合計算(合計金額);    
    } 
    
    int 税額 = 割合計算(合計金額);
    return 合計金額 + 税額;
}

public static int 割合計算(int 金額){
    return 金額 * 0.2;
}
~~~

さぁ、大変なことになりました。消費税を20％もとってしまう大問題なシステムとなってしまいました。

同じ形ではなく、**同じ意味を共通化**する。
これは、どの共通化に対しても言える大前提です。

# 同一処理の共通化
これが、最も定番の共通化だと思います。
りんごもみかんも、どちらも同じ計算式を利用したいので共通化して同じ処理を呼ぶことにしました。

~~~java:
public static int りんごの計算(int 数量){
    return 支払金額計算(数量, 20) + 100;
}

public static みかんの計算(int 数量){
    return 支払金額計算(数量, 15);
}

public static int 支払金額計算(int 数量, int 単価){
    int 合計金額 = 数量 * 単価;

    if(数量 >= 10){
        合計金額 = 合計金額 - 割合計算(合計金額);    
    } 
    
    int 税額 = 合計金額 * 0.1;
    return 合計金額 + 税額;
}

public static int 割引計算(int 金額){
    return 金額 * 0.2;
}
~~~

どんなに処理内容が変化してもりんごとみかんで**同じ処理がしたい**場合に行います。
逆に処理内容が変わってしまった場合に、影響を確認するのが大変なら使うべきではありません。

共通化の変化は、共通化を呼ぶ側にも影響することを忘れてはいけません。

# 変化を制御する共通化
DBアクセスやファイルアクセスなどの外部とのやりとなどに利用される共通化です。
共通化しておくことにより、外部の仕様がどうかわろうとも利用する側は変更する必要がなくなります。

~~~java:
public static int りんごの計算(int 数量){
    return 支払金額計算(数量, 単価取得("りんご")) + 100;
}

public static みかんの計算(int 数量){
    return 支払金額計算(数量, 単価取得("みかん"));
}

public static 単価取得(String 種類){
    int 単価 = 0;
    単価 = //～ファイル読み込み処理～
    return 単価;
}

public static int 支払金額計算(int 数量, int 単価){
    int 合計金額 = 数量 * 単価;

    if(数量 >= 10){
        合計金額 = 合計金額 - 割合計算(合計金額);    
    } 
    
    int 税額 = 合計金額 * 0.1;
    return 合計金額 + 税額;
}

public static int 割引計算(int 金額){
    return 金額 * 0.2;
}
~~~

もしこれが、

~~~java:
public static 単価取得(String 種類){
    int 単価 = 0;
    単価 = //～DB読み込み処理～
    return 単価;
}
~~~

になっても、全体の処理に影響しなくて済みます。

# ミスをなくす共通化

処理の中には、ほぼ変化することのないものがあります。
~~~java:
public static 三角形の面積(int 底辺, int 高さ){
    return 底辺 * 高さ / 2;
}
~~~

こういった処理は、一度書いてしまえば変化することはない不変処理のためコピペでも設計的には変更をすることもないので問題がありません。
ただし、**人間はミス**をします

~~~java:
底辺 * 高さ / 2;底辺 * 高さ / 2;底辺 * 高さ / 2;底辺 * 高さ / 2;底辺 * 高さ / 2;
底辺 * 高さ / 2;底辺 * 高さ / 2;底辺 * 高さ / 3;底辺 * 高さ / 2;底辺 * 高さ / 2;
底辺 * 高さ / 2;底辺 * 高さ / 2;底辺 * 高さ / 2;底辺 * 高さ / 2;底辺 * 高さ / 2;
底辺 * 高さ / 2;底辺 * 高さ / 2;底辺 * 高さ / 2;底辺 * 高さ / 2;底辺 * 高さ / 2;
底辺 * 高さ / 2;底辺 * 高さ / 2;底辺 * 高さ / 2;底辺 * 高さ / 2;底辺 * 高さ / 2;
底辺 * 高さ / 2;底辺 * 高さ / 2;底辺 * 高さ / 2;底辺 * 高さ / 2;底辺 * 高さ / 2;
~~~

どこで、間違えているんだと絶望する前に共通化しましょう。

# タイミングの共通化

継承を使うことで、処理をするタイミングをそろえることができる。
あまり、共通化として使うべきではなと考えています。

~~~java:
public abstract class 計算装置{
    
    abstract private int 単価取得();

    public int 支払金額計算(int 数量){
        int 合計金額 = 数量 * 単価取得();

        if(数量 >= 10){
            合計金額 = 合計金額 - 割合計算(合計金額);    
        } 
    
        int 税額 = 合計金額 * 0.1;
        return 合計金額 + 税額;
    }
}

public class 計算 extends 計算装置()
{
    private int 単価取得(){
        return 20;
    }
} 

public class みかん計算 extends 計算装置()
{
    private int 単価取得(){
        return 15;
    }
}

public class メインクラス{
    private static int 計算する(計算装置 装置, 数量){
        return 装置.支払金額計算(数量);
    }
}

~~~

### あらためて、正しい共通化を考えてみるといいかもしれません。