こんにちは、お久しぶりです!
Ayakaです。(なんとなく英語表記にしてみた)
最近イベントごとが多くて東京に何回も行かせていただいてます。
(実は来週の土日にまたゲームジャムの為遠征です)
専門学校入学したときよりも東京に特別感がなく、むしろすごい馴染んでいるのはいいことだと思いたい(笑)
また、インターンシップも何社かエントリーさせていただいたので是非是非よろしくお願いします!
そんな忙しい中でもやっぱりブログ更新はやらないと……。
段々頻度を増やしていけれたらなぁと思ってます!
(悲しいことに、理解不十分なものが……!)
では、久しぶりの今日のお題は……singleton!
デザインパターンですね。4人の技術者の方が考えてくれた23つある設計のうちの1つです。
使ってみるとプログラムが分かりやすく、また再利用もしやすいので非常にありがたい!
……理解していればの話ですがね(;´・ω・)
今年から始まったこの授業、現段階で「singleton」と「Factory method」、「strategy」に「state」とやってきましたが……危うい!
昨日なんとかstateの壁を越えてきました……スッキリ。
とはいえあと3つ!そのうちの1つ!ちゃんとやるぞ!!
では、毎度おなじみコードから……
class Hi_score
{
public://スコア
int hi_score;
private:
Hi_score()
{
hi_score = 1000;
}
public://一つしか作らない
static Hi_score& GetInstance()
{
static Hi_score* instance = new Hi_score();
if (instance == nullptr)
{
instance = new Hi_score();
}
return *instance;
}
};
//スコアを1にする
void One()
{
Hi_score& hiscore = Hi_score::GetInstance();
hiscore.hi_score = 1;
}
//スコアに999足す
void Nine()
{
Hi_score& hiscore = Hi_score::GetInstance();
hiscore.hi_score += 999;
}
int main()
{
Hi_score& hiscore = Hi_score::GetInstance();
cout << hiscore.hi_score << endl;
One();
cout << hiscore.hi_score << endl;
Nine();
cout << hiscore.hi_score << endl;
return 0;
}
なんだこの醜いコードは!!!(声大)
関数名迷ったにせよOneとNineはひどすぎませんか私……
そもそもpublicに変数がある時点で……ま、まぁテストだから!!許してください!!!
さて……実行結果はといいますと
1000
1
1000
となります!
……へ、何が何だか分からない?
ええ、私がです……
まず、疑問なのはGetInstanceの存在。
全部の関数で、普通に宣言させてくれない!(Hi_score hiscore=new Hi_score();のように)
絶対にGetInstanceを取得しなければならない点。
そして、関数に引数を渡していないのに、One()関数を呼び出したらスコアが1になっていること、Nine()関数を呼び出したら1に+999されていること。
今までだったら
//スコアに1を代入
int One(int score)
{
score = 1;
return score;
}
//スコアに+999する
int Nine(int score)
{
score += 999;
return score;
}
int main(void)
{
int Score;
Score = 1000;
cout << Score << endl;
Score = One(Score);
cout << Score << endl;
Score = Nine(Score);
cout << Score << endl;
return 0;
}
こうしなければ同じような結果にはならなかったのに……
……さて、ここでsingletonとはどういうデザインパターンなのか、という事が分かってきたかと。
「single」っていうのは、「単」、「ただ1つ」という意味ですよね。(Google翻訳先生参照)
そして今回のこの結果……。
そう、スコアは多重に存在していない!
1つのスコアを書き換えたり(One()で1を代入)、1つのスコアに対して計算したり(Nine()で+999)していたわけです。
つまり、プログラム上1つしか要らない、必要のないものに対してこのデザインパターンが使える!!
さぁ、問題のクラスを見てみると
class Hi_score
{
public://スコア
int hi_score;
private:
Hi_score()
{
hi_score = 1000;
}
public://一つしか作らない
static Hi_score& GetInstance()
{
static Hi_score* instance = new Hi_score();
if (instance == nullptr)
{
instance = new Hi_score();
}
return *instance;
}
};
よくよく見ると、privateにコンストラクタがあるんです!
問題のGetInstance()を呼び出さないといけない理由は、コンストラクタに直接アクセスできないから……
えーそんな馬鹿な……
GetInstanceを呼び出すと、静的変数であるinstanceにnewされます。
では、順々に関数を呼び出してみます。
int main()
{
Hi_score& hiscore = Hi_score::GetInstance();
cout << hiscore.hi_score << endl;
One();
cout << hiscore.hi_score << endl;
Nine();
cout << hiscore.hi_score << endl;
return 0;
}
まず、main関数内の
Hi_score& hiscore = Hi_score::GetInstance();
で、始めの初期値1000の入ったHi_scoreが生成されます。
次のOne()関数にて
//スコアを1にする
void One()
{
Hi_score& hiscore = Hi_score::GetInstance();
hiscore.hi_score = 1;
}
ここでのHi_score& hiscore = Hi_score::GetInstance();
で、さっきmain関数で生成した初期値1000の入ったHi_scoreが取得されます!
その次の文hiscore.hi_score = 1;であっさりとHi_score内を1にしちゃいますが……
そして最後のNine()関数で
//スコアに999足す
void Nine()
{
Hi_score& hiscore = Hi_score::GetInstance();
hiscore.hi_score += 999;
}
お察しの通り、ここのHi_score& hiscore = Hi_score::GetInstance();
でも、さっき1を代入したHi_scoreさんが取得されます。
そこに+999して,めでたく1000に……!
singletonのポイントは
*コンストラクタはprivate(単一しか存在させないため、外部からの生成を防ぐ)
*その代わりにGetInstance()で取得。
*とにもかくにも単一!
ですね!
……どうしよう、うまくまとまっている気がしない……困った。
とはいえ、今からこのsingletonくんを実装せねば。なのでゆったりとクラシック聴きながらやってきますとも!