こちらの記事について社内外でちょっとした議論になったので、その内容をまとめてみました。
Singletonパターンを利用する理由は「外部からnewさせたくないから」だと思っていましたが、「そう書いた方がわかりやすいから」「staticではないメソッドも利用可能になるから」という理由の方が大きそうです。
【指摘】
・privateコンストラクタを持った時点でnewできない
newさせたくないだけなら、オブジェクト取得メソッドは不要。
Singletonパターンで無くても良い。
・Singletonパターンの思想の表現としてオブジェクト取得メソッドが必要
書籍ではSingletonパターンはオブジェクト取得メソッドが必要とされている。
確かに、privateでオブジェクトを1つ生成、外部にはメソッド経由で提供する、とすれば、「オブジェクトは1つのみ存在する」という思想をわかりやすく表現できる。
人によってstaticの方が分かりやすいかSingletonパターンの方が分かりやすいかは違うと思うが、オブジェクト指向に慣れた技術者であればSingletonパターンの方が設計思想が分かりやすいというのはありそう。
・Singletonパターンだとstaticではないメソッドも参照可能になる
Singletonパターンであればクラスへの参照ではなくオブジェクトへの参照となる。
そのため、staticではないメソッドの参照も可能となる。
具体的には、継承・オーバーライドができるというメリットがある。
【サンプルコード】
・StaticMemory.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
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()); } } |
【実行結果】
1 2 3 4 5 6 |
Staticクラス1:1 メソッドでget1:2 メソッドでget2:4 直接参照1:3 メソッドでget1(継承):4 メソッドでget2(継承):16 |
この話、社内でちょっとした議論になりました。
おかげで色々理解が深まりました。
議論のきっかけを作れるというのも、技術ブログの良い所だと思いました。
コメント