SingletonとStaticの違いとは?【続編】」

java

こちらの記事について社内外でちょっとした議論になったので、その内容をまとめてみました。
Singletonパターンを利用する理由は「外部からnewさせたくないから」だと思っていましたが、「そう書いた方がわかりやすいから」「staticではないメソッドも利用可能になるから」という理由の方が大きそうです。

【指摘】

・privateコンストラクタを持った時点でnewできない

newさせたくないだけなら、オブジェクト取得メソッドは不要。
Singletonパターンで無くても良い。

・Singletonパターンの思想の表現としてオブジェクト取得メソッドが必要

書籍ではSingletonパターンはオブジェクト取得メソッドが必要とされている。
確かに、privateでオブジェクトを1つ生成、外部にはメソッド経由で提供する、とすれば、「オブジェクトは1つのみ存在する」という思想をわかりやすく表現できる。
人によってstaticの方が分かりやすいかSingletonパターンの方が分かりやすいかは違うと思うが、オブジェクト指向に慣れた技術者であればSingletonパターンの方が設計思想が分かりやすいというのはありそう。

・Singletonパターンだとstaticではないメソッドも参照可能になる

Singletonパターンであればクラスへの参照ではなくオブジェクトへの参照となる。
そのため、staticではないメソッドの参照も可能となる。
具体的には、継承・オーバーライドができるというメリットがある。

【サンプルコード】

・StaticMemory.java

public class StaticMemory {

    // 利用者側でnewさせたくないだけならこれだけで良い
    private StaticMemory() {
    }

    private static int num = 0;

    public static void setNum(int arg) {
        num = arg;
    }

    public static int getNum() {
        return num;
    }

}

・SingletonMemory.java

public class SingletonMemory {

    private static int num = 0;
    private static SingletonMemory instance = new SingletonMemory();

    protected SingletonMemory(){
    }

    public static SingletonMemory getInstance(){
        return instance;
    }

    public static void setNum(int arg) {
        num = arg;
    }

    public static int getNum() {
        return num;
    }

    public int getNumCalc() {
        return num * 2;
    }

}

・SingletonMemoryChild.java

public class SingletonMemoryChild extends SingletonMemory {

    private static SingletonMemoryChild instance = new SingletonMemoryChild();

    private SingletonMemoryChild(){
    }

    public static SingletonMemoryChild getInstance(){
        return instance;
    }

    // 下記はビルドエラー
    // @Override
    // public static int getNum() {
    //    return 100;
    //}

    @Override
    public int getNumCalc() {
        return super.getNumCalc() * 2;
    }

}

・MemoryTestMain.java

public class MemoryTestMain {

    // 要件:最新の値をメモリに保持したい
    @SuppressWarnings("static-access")
    public static void main(String[] args) {

        int recentNum = 0; // 最新の値

        // 普通のStaticなクラスを使った場合
        recentNum = 1;
        StaticMemory.setNum(recentNum);
        System.out.println("Static1:"+StaticMemory.getNum());

        // privateコンストラクタを定義すれば使う側でオブジェクトを作れない
        // (下記はエラー)
        // StaticMemory instance2 = new StaticMemory();
        // instance2.setNum(recentNum);
        // System.out.println("new1:"+instance2.getNum());

        // オブジェクト取得メソッドを利用すれば非staticメソッドも参照可能
        recentNum = 2;
        SingletonMemory.getInstance().setNum(recentNum);
        System.out.println
            ("Shingleton1:"+SingletonMemory.getInstance().getNum());
        System.out.println
            ("Shingleton2:"+SingletonMemory.getInstance().getNumCalc());

        // 直接参照だと非staticメソッドを参照できない
        recentNum = 3;
        SingletonMemory.setNum(recentNum);
        System.out.println
            ("直接参照1:"+SingletonMemory.getNum());
        // System.out.println
        //     ("直接参照2:"+SingletonMemory.getNumCalc());

        // Singletoneなクラスを継承したクラスを利用
        // 非staticメソッドはオーバーライドしたメソッドが呼び出される
        recentNum = 4;
        SingletonMemoryChild.getInstance().setNum(recentNum);
        System.out.println
            ("Child1:"+SingletonMemoryChild.getInstance().getNum());
        System.out.println
            ("Child2:"+SingletonMemoryChild.getInstance().getNumCalc());

    }

}

【実行結果】

Staticクラス1:1
メソッドでget1:2
メソッドでget2:4
直接参照1:3
メソッドでget1(継承):4
メソッドでget2(継承):16

この話、社内でちょっとした議論になりました。
おかげで色々理解が深まりました。

議論のきっかけを作れるというのも、技術ブログの良い所だと思いました。

コメント

タイトルとURLをコピーしました