[cbuilder:40078] クラス変数、クラスメソッドをどこまで使いますか?

97 views
Skip to first unread message

"山崎創介"

unread,
Jun 24, 2009, 10:02:08 AM6/24/09
to cbui...@sfdata.ne.jp
 山崎と言います。
 C++Builder と直接関わる話題ではないのですが、
皆さんのご意見を伺いたく、お尋ねします。

 アプリケーションで唯一無二のインスタンスを生成したい場合があると思います。
(例えば、アプリケーション全体のコントローラや設定情報など)
 そうした場合、Singletonパターンを利用すると思いますが、
その実装方法として、クラスメソッドでファクトリを提供したり、
クラス変数でインスタンスを管理したりするのは、よくある手法だと
思っています。
 しかし、クラス変数やメソッドで、アプリケーションで唯一の情報を管理するのなら、
わざわざインスタンスを作らずとも、全てをクラス変数として実装するという事も
あり得るように思えて来ています。
 インスタンスのように、それをメソッドの引数として他オブジェクトに
渡すという事は出来ませんが、そもそも引数として渡さずとも、
クラスならばアプリケーション内でグローバルに参照できます。
 変数だけにするのがカプセル化の観点から問題であれば、
インスタンスの場合と同様に、公開レベルを private にして
アクセスメソッドをクラスメソッドとして提供すれば良いことです。
(あるいは、C++ならば、グローバル変数でも同じ事が可能です)
 このような実装スタイルは、どう思われますか?

 基本的に、クラス変数やメソッドは、定数定義やインスタンスの生成や
管理に使うものだと思っています。
 しかし、こうした、染み付いたオブジェクト指向的な考えを捨ててみれば、
クラス変数やクラスメソッドを業務ロジックの実装に用いても、
一向に差し支えないように思われます。
 極論すれば、アプリケーションで同じデータ構造の実体が複数必要に
なるのでなければ、インスタンスなど作る必要がないのでないかとさえ
思えて来ます。
 手続き指向的な考えに立てば少しも奇異ではありませんし、
メモリの動的生成・廃棄のコストを思えば、インスタンスを作らない方が
パフォーマンスも上がるだろうと思われます。
(もちろん、当節のPCの性能向上を思えば、インスタンス生成による
動作の遅延など微々たる遅延でしょうけど)
 実際、C言語で仕事をしていた時代には、malloc など滅多に使いは
しなかったと思うのです。
 いつの間にか、インスタンスを生成する事が当たり前になって
しまっていた(汗)。

 実は、現在従事している業務で、現行ソースの多くが、そのような
クラスメソッドやクラス変数ばかりのソースで面食らっているのですが、
インスタンス化しない事が「悪い」と断じる論拠も見いだせず、
どうしたものかと惑っています。
 バグが多いので全面的に手は加えるのですが、実装スタイルまで
変えずとも良いか、と逡巡している次第です。

 どうでも良い、と言ってしまえばどうでも良い話なのですが、
みなさんのご意見をお聞かせ下さい。

 また、「Singleton パターンは、ここでこそ使え」と言うような局面があれば、
是非、合わせて教えて下さい。

6/24 山崎

-----------------
sent from W-ZERO3


Y.Uchiyama

unread,
Jun 24, 2009, 10:53:26 AM6/24/09
to cbui...@sfdata.ne.jp
こばわ。ゆーちと名乗ってますw

アプリケーション固有の一意な情報に関して、であれば、インスタンス生成の
コストは気にするボリュームではないと思います。
あちきも古き時代から作っているのでw、わずかなサイズの構造体やクラスを
new - delete 繰り返す作りには抵抗を感じる世代です(笑)

しかしながらシングルトン実装は非常に有効な手段だと思いますよ。

たとえば・・・
キャッシュバッファのサイズを固定値として持つ
作業ディレクトリのパス名をユーザー指定で持つ
ソケット通信用のIPアドレスとポートを持つ
RS-232Cデバイスの通信パラメータを持つ
など、アプリケーションによって持つべき値が環境などに依存するケースがあ
ります。

インスタンス化せずに持ってるとしたら設定の変更で再起動みたいになっちゃ
うんじゃないかなぁ。(ま、作り方にもよるでしょうけど。)

C++Builder をつかってバッファサイズを扱うクラスを作るとして・・・
(以下、眠いのでw検証してません)
class AppBaseDefine
{
size_t FBufferSize;
protected:
virtual void SetBufferSize( size_t bufferSize ){
FBufferSize = bufferSize;
}
public:
__property size_t BufferSize =
{ read=FBufferSize, write=SetBufferSize }
};

こいつを、MyApplication なクラスに、AppBaseDefine *Define; をシングル
トンで配置しておけば、Define->BufferSize で参照できますね。
(ま、たしかに間接参照分のコストはかかるかもしれないけどね)

要求仕様の変化で、再起動をしなくてすむようにしろ、って言われちゃったと
きに、効果あるんじゃないかなぁ

class MyAppDefine : public AppBaseDefine
{
extern class ObserverContainer *Observers; // オブザーバパターン実装
protected:
virtual void SetBufferSize( size_t bufferSize ){
AppBaseDefine:: SetBufferSize( bufferSize );
Observers->Notify( this ); // 観察者に値の変更を通知
}
};

というような実装をしておけば(あくまでもイメージw)、おそらく
すくなくとも MyApplication 側については、
Define = new AppBaseDefine();
の部分を
Define = new MyAppDefine();
とするだけで、必要とする場所に変化を通知できるクラスに置き換えることが
できるようになってしまいますよね。
(変化をキャッチする側は当然受け口を実装しなきゃいけないけどね)

ということで、アプリケーションの固有情報は、やはりクラスのインスタンス
としてアサインしておくべきだと思います。

えー。飲んでるので適当な部分たくさんあると思いますが、ご容赦ください。
そもそも本題とはずれてるかもしれない(゜∀゜)
m(_._)m

おっと・・・
class AppBaseDefine : public Singleton
かな・・・(笑)
-----------------------------------------------------------------
ゆーち(内山 康広) mailto:uchi...@seasoft.jp
[PR]============================================================
メールの宛先に敬称を。メール本文に会社名や部署名、宛先のお名前を
自動挿入! BkReplyer 2 好評公開中!
http://www.vector.co.jp/soft/win95/net/se476325.html
-----------------------------------------------------------------


Takeshi Kodama

unread,
Jun 25, 2009, 9:16:33 AM6/25/09
to cbui...@sfdata.ne.jp
論旨を誤解していたらお許しください。

Meyersシングルトンパターンの公式を示しますが、オリジナルのEffective C++
ではなくMatthew WilsonのImperfect C++からの引用で、さらに一部を書き換え
ました。

class Thing
{
public:
static Thing &GetInstance()
{
static Thing s_instance;
return s_instance;
}
private:
Thing() {}
Thing(Thing const &);
Thing &operator =(Thing const &);
};

クラスThingのインスタンスを得る唯一の方法は、

Thing& t(Thing::GetInstance());

で、しかもstaticな寿命を持つs_instanceへの参照です。この公式に従う限り、
間違ってもクラスThingのインスタンスを複数作ることはありません。

そもそもシングルトンパターンとはErich Gammaら(GOF)のDesign Patternsで明
確に定式化された「デザインパターン」の一つであり、以下のように定義されて
います。

「唯一つのインスタンスしか持たないことが保証されたクラスで、グローバルに
アクセスできるもの」

単に特定クラス内からのみアクセスするのであれば、プライベートなネストクラ
スを作り、そのプライベートなstaticメンバ変数を作れば宜しい。つまり、上記
よりも条件が限定されているため、シングルトンパターンに依らなくても構わな
い、というだけではないでしょうか。

S.Yamazaki

unread,
Jun 25, 2009, 12:27:59 PM6/25/09
to cbui...@sfdata.ne.jp
 山崎です。
 ゆーちさん、Takeshi Kodamaさん、コメントありがとうございました。

 近頃の仕事は、規模が大きいのは良いのだけど、孫請け、ひ孫請けみたいな
仕事がほとんどで、自分に裁量権がないものだから、
「納品物なんて、要するに顧客が満足してれば、それで良いじゃん」
と、投げやり気分になっているこの頃です。
 まあ、私が口下手だという事なのでしょうけど、自分の中でほぼ決着のついている
問題について、人を説得するのがウンザリ。
 技術的な整合性とか一貫性などで人と議論するスタミナがなくなって来ちゃってるなあ・・・。

ゆーちさん wrote:
> アプリケーション固有の一意な情報に関して、であれば、インスタンス生成の
> コストは気にするボリュームではないと思います。

 そうなのですけど、「コスト」を気にするか否かは、究極のところ「感性」ではないですか。
 感覚的なロジックを打破するには、実利的なメリットがなければ、説得は不可です。
 もちろん、「信頼性、メンテナンス性の向上」も、長い目で見れば「実利」なのですけど、
なかなか、開発の最中にそこまで思い至ってくれる顧客も少ない。
 それよりか、「さっさとやれ」、「今のままでいいから」と言われるのがオチです。

 例えば、ゆーちさんご提案のクラスも、

class AppBaseDefine
{
static size_t FBufferSize;
public:
static void Reload(){
// ここで、設定ファイル等からBufferSize値を読み込む
}
static size_t GetBufferSize(){ return FBufferSize; }
};

としてしまえば、インスタンスを作らずともいくらでもReloadメソッドで設定変更に
対応できるではないですか。

 考えてみれば、対応出来るのは当たり前、時代が変わったと言っても、
C言語などで手続き指向的な開発が出来なくなった訳ではないのですから。

 あるいは、例えば、私が前回たずさわったプロジェクトでは、シングルトンどころか、
for文の中で使い捨てにする様なオブジェクトでさえ、
「必ず新たに生成すること。インスタンス生成のコストよりも、インスタンスの使い回しに
よるリスクの方が大きいからである」
と、コーディング規約書に明記してありました。
 そこまで明快に言われれば、「ああ。再利用のためにインスタンスをきちんと初期化した
つもりでも、派生継承のルートの中で思わぬ初期化漏れがあるかも知れないものな」と、
多分、このMLに参加しているみなさんならば納得が行きますでしょう?
 しかしそれも、「派生継承を使わなければ良いじゃん」とでも言われてしまえば、
それまでです(実際、昔たずさわったプロジェクトで「派生継承は、思わぬバグの元と
なるので使わぬ事」というコーディングルールが採用されていた現場があったんです)。

 そこまで言うなら、オブジェクト指向言語である C++ など、開発言語に採用しなければ
良いじゃん、という話にもなりますけど、「別に使いたくて使ってるんじゃないもーん。
ほかに選択肢がないから使ってるんだもーん」とでも開き直られればそれまでです。
(ちなみに、指定されているのは Visual C++。私は、この年になって初めて使いましたよ)

 そもそも、こんな話は、1プログラマー風情が頭を悩ます事ではなく、
プロジェクトマネージャが開発指針を定めて顧客とネゴ取れ、という話なのですけど、
今回の元請け会社は●●●だから、ダメだな。

Takeshi Kodamaさん wrote:
> 「唯一つのインスタンスしか持たないことが保証されたクラスで、グローバルに
> アクセスできるもの」

 そうそう、まさにそれが課題です。グローバルにアクセスしたいのです。
 確かに、C++ ならば、オブジェクトを静的に宣言するという手がありましたね。
 それならば、動的生成のコストとは無縁そうです。
 ただし、Java や C# など、これからの言語では使えないのが残念なところです。
 それと・・・、まあ、結局のところ、クラスの static な変数にしてしまうという事
なのですよね。必要な情報や機能を、クラス変数やクラスメソッドで提供するのと
何が違うのだろう。

Takeshi Kodama

unread,
Jun 25, 2009, 9:43:27 PM6/25/09
to cbui...@sfdata.ne.jp
実はまだ論点がつかめていないのですが、

(1)シングルトンパターンを使用すること、
(2)クラスインスタンスをnewでヒープに作成すること、
(3)自由関数やstaticメンバ関数で処理できる事ををわざわざstaticでないメン
バ関数で処理すること、

の三つが問題提起されていたという理解でよろしいのでしょうか。(1)について
既に意見を述べましたが、相互依存するグローバルオブジェクトの初期化順番が
自動的に管理できるというメリットを追加します。

(2)については、ヒープ(フリーストア)オブジェクトとスタックオブジェクトの
どちらを選択するかという教科書的な問題じゃないでしょうか。我らがTObject
派生クラスでは、不幸にして前者を選ばざるを得ないという現実があったりしま
すが。

(3)についても、単にスタイルの問題かと。オブジェクト指向プログラミングを
金科玉条とすれば自由関数やstaticメンバ関数さえも邪道ですが、何たってC++
は生まれからしてピュアなオブジェクト指向プログラミング言語でない。STLの
アルゴリズムなんてテンプレート自由関数の集合体で、総称プログラミングとい
う別パラダイムのメリットを享受するものです。C++0Xではさらに、メタプログ
ラミングのための大胆な言語拡張が予定されていると聞きます。

Herb SutterのExceptional C++ StyleのItem38は、「自由関数で処理できるもの
を、わざわざメンバ関数で処理してはいけない」と主張します。同じくSutterの
Exceptional C++のItem20-24を要約すれば、「コード再利用目的で派生継承して
はいけない」です。さらにItem25は、「そもそもC++はオブジェクト指向プログ
ラミング言語か?」という問いかけです。これらは、我々が遥か昔にC++入門書
で勉強した話と全く異なります。

JavaやC#はよく知らないんですが、これらがよりピュアなオブジェクト指向プロ
グラミング言語であるとすれば、C++の現状がこれらと乖離するのは当然でしょ
う。一方、C++は当然ながら依然としてオブジェクト指向プログラミング可能で
す。コードの統一性を保つため、「できるだけピュアにオブジェクト指向プログ
ラミングの作法に従う」というコーディングルールを設定するのもリーズナブル
だと思います。

S.Yamazaki

unread,
Jun 27, 2009, 1:32:21 AM6/27/09
to cbui...@sfdata.ne.jp
 山崎です。
 Takeshi Kodamaさん、コメントありがとうございました。

> コードの統一性を保つため、「できるだけピュアにオブジェクト指向プログ
> ラミングの作法に従う」というコーディングルールを設定するのもリーズナブル

 煎じ詰めればそういう事で、孫請けワーカとしてはレビューや納品時の
「落しどころ」さえ目星がつけば、後はどーでも良い話なのですけど。

 とは言うものの、ごめんなさい。
 私も、要らぬ事をグチャグチャと書き過ぎました。
 論点を明確にします。
 今回、メーリングリストに投稿したのは、
「シングルトンパターンの有効性について」
ご意見が伺いたかったという事です。
 シングルトンパターンは、どういう場面での利用を想定すればよいもの
なのか、という話です。
 一般論としてです。
 言語やツール、あるいはインスタンスがメモリのどの領域に取られるか、
などは一切関係なしに、です。
 つまりは、

obj = AClass::GetSingleton();
obj->Function();

とするか、

AClass::Function();

とするか、という事です。
 アプリケーションで唯一で、アプリケーションからグローバルに利用したい
「なにか」を提供するならば、インスタンス化せずとも、クラス変数や
クラスメソッドとして実装すれば十分なのではないのか、という事です。
 ただし、上記は、私の先日までの認識の中ではかなり「邪道」と思われたもので、
皆さんの認識や対応を伺ってみたかったのです。

 もちろん、私も、ユーティリティ的なクラスについては機能のみを提供して
インスタンス化を想定しないクラスも作った事は何度もありましたけど、
ビジネスロジックの中核を成して、機能のみでなくデータも持っている実体を
インスタンス化しないというのは、どうも「居心地」が悪かったのです。
(居心地悪さの理由は色々)
 しかし、技術者として「居心地」などという曖昧なものを判断基準にするのも、
いかがかというところがあります。
 インスタンス生成がCPU時間についての「コスト」である以上は、
そのコストを減らすことは、ユーザに対して実利的な「メリット」を提供する
事になります。

 しかし、そうであるならばデザインパターンで提唱されている
「シングルトンパターン」とは何なのか、果たして利用するに足る局面が
あるのか、という事になります。

 ただし、ここ2晩ほどこの問題について考えたところとして、
一般論の題材にするには、提起したサンプルが単純過ぎたかな、と反省している
ところです。
 シングルトンの説明でしばしば提示されるサンプルは、

class AClass {
private:
static AClass *FSingleton;
AClass(){
}
public:
static AClass *GetSingleton(){
if(FSingleton == NULL){
FSingleton = new AClass();
}
return FSingleton;
}
};

という様なものですが、確かにこのレベルでは、インスタンス化するよりも
static メンバを使った方が良い(「良い」というのはコスト的観点から)。
 しかし、考えてみると、シングルトンもこんな単純なケースばかりでは
ないでしょう。
 例えば、シングルトンクラス自体は抽象クラスで、複数の実体クラスから
ファクトリによって動的に選択される、というようなケースでは、
シングルトンはインスタンス化されなければ意味がありません。
 という訳で、「シングルトンパターンは(ケースによっては)有効である」
というのが、今のところの私の結論です。
 お騒がせしました。

> Herb SutterのExceptional C++ StyleのItem38は、「自由関数で処理できるもの
> を、わざわざメンバ関数で処理してはいけない」と主張します。

 拝読して、ちょっとニヤリとしてしまいました。
 なるほどね。他の箇所も総合すると、「オブジェクト指向を金科玉条とするな」
とも読めますね。

 とりあえず、目先の仕事では、現行ソースのスタイルは変えずに、クラス変数、
クラスメソッドを使いまくりで実装しようと思います。

 貴重なご意見、情報を頂きまして、ありがとうございました。

6/27 山崎

Reply all
Reply to author
Forward
0 new messages