気難しいプログラマとの人間関係に必要ないくつかのポイント

優れたプログラムとは (2)

»

 前回の続きです。

○モジュール設計

 優れたプログラムは、極めて慎重にモジュール設計がなされています。機能ごとに分けられたコンポーネントや適切に階層化されたレイヤーは、なるべく互いに関係性を持たないように区切られています。

 例えば、上位に位置する層はOSやRDBMSに直接アクセスすることはありませんし、逆に下位層がユーザーインターフェイスの変更に影響を受けることはありません。重要なのは、そのモジュールをいかに外部を意識しないように作るか、ということです。

 参照する側の事情に左右されない独立性・汎用性が、設計のカギとなることでしょう。オブジェクト指向言語においては、これら論理構造上の区分けは、クラスの階層やJavaのpackageなどでより明示化された実装が可能となります。

○疎結合性を阻害するスコープの問題

 プログラム品質を決定づける大きな要因の一つがスコープです。スコープが広がれば広がるほどプログラム品質は低下します。もしスコープが当該モジュール外に出ている要素が存在していたり、他モジュールと密接なインターフェイスを持っている場合、このモジュール内でのプログラム修正は慎重に行わなければなりません。他モジュールへの影響を考慮しなければならず、厳密にソフトウェアの品質を保つのであれば、影響箇所すべてを再テストする必要があります。

 あらゆる要素のスコープは可読性を損なわない範囲で可能な限り狭めるべきであり、モジュール間の関係性をなるべく減らすことがプログラム品質を保ち、テスト工数を圧縮する重要なポイントであると私は考えます。スコープ拡大の元凶であるpublic staticは、可読性の向上以外に使用用途はほぼないといっていいと思います(逆説的に聞こえたかもしれませんが、public staticにはラベリング、ユーティリティやプロセス一意であることの明示化という可読性向上の用途があります)。

 またスコープはその要素への実際のアクセスに関係なく、範囲が広がるだけで疎結合性を低下させます。その要素へのアクセスを適切に遮断しておかないと、いつその要素の状態・振る舞いを変化させるコードの修正が行われるか分かりません。これは設計者の意図しない結合度の増加を招く原因となり、修正者がコードに手を加えるたびにソフトウェア全体の品質を徐々に劣化させてしまう危険性を孕んでしまいます。特にコードの修正・追加が頻繁に発生する保守工程における結合度では、このような実際の参照関係を元にした指標値よりも、スコープ範囲を考慮した潜在的な結合度の方が重要な指標となるでしょう。

○疎結合性と可読性

 疎結合性を実現する上で注意すべきことは、疎結合を意識するあまりソースコードの可読性を損ねてしまう可能性が出てくることです。極端な例ですが、例えばすべての引数をバイナリ型で受け取ってしまえばいろんな仕様変更にも耐えられるよね、というような意見にどれだけの人が賛同できるでしょうか。

 前回ではプログラミング言語の進化は疎結合性・再利用性の追求だと述べました。実際、近年のプログラミング言語には、非常に強力な疎結合性を兼ね備えた言語仕様がいくつか見受けられます。これは疎結合性がプログラム品質の観点から十分に議論・分析が行われてきた結果であり、プログラム開発の目指すべき目標を考えれば言語仕様の正当な進化であるといえると思います。しかしその一方で明快な指標を持ちにくいソースコードの可読性については、幾分進歩が遅れているのではないかという危惧を持っています。例えばJavaScriptの動的にメンバを追加できる言語仕様は、可読性の観点から若干の問題を孕んでいるといえるかもしれません。

 疎結合性と可読性。

 このバランスこそが優れたプログラムを作成する上で極めて重要なポイントだと私は考えます。

 次回に続きます。

Comment(85)

コメント

もーちょい深いところでは、"粒度と一貫性"が出てきそうに思います。
「プロジェクトひとり」ならそうでもないのですが、複数の
設計者/実装者が多数になると粒度とスタイルがどうしてもバラつき、
結果的に疎結合性/可読性を落としがちになるかな、と。

玄米茶

おっしゃる通りだと思います。

本文では漠然とした話しかできませんでしたが、粒度の話は可読性の問題を具体的にあらわすことのできる非常に重要な観点だと思います。

次の回では「基底クラスなどの論理階層上の低位モジュールの設計は(本来)上流工程従事者がやるべき」というような話をしようと思ってるんですが、粒度の問題はまさにこれに当てはまりますね。
全体を見渡すことのできる上流工程従事者にしか粒度を考慮した設計はできないわけですから。

次の回でこの話を使わせてもらうかもしれません。
コメントありがとうございました。

基底クラスもそうだけど、ライブラリ/ユーティリティに相当するあたりもいい感じで制御しとかんとコード・クローンが蔓延したり。

最近では、逆に、メンバー変数へのアクセスをなくし、staticメソッドにすることが推奨されているそうです。私は読んだことはないですが、Readable Code という本には、

「クラスのメンバへのアクセスを制限するもう一つの方法は、メソッドを出来るだけstatic にすることだ」

と書かれているそうです。

http://sanematsu.wordpress.com/2012/08/13/readable-code/

ごめんなさい、わからんかった。↑の「逆に...」とは何に対して、ですか?

玄米茶さんはpublic staticはスコープを広げる、との意見であるが、その逆という意味です。ただし、玄米茶さんはpublic static変数のことを言っているのかpublic staticメソッドのことを言っているのか不明なので、なんともいえません。私も「逆に」を入れるべきか、入れないべきか迷いました。

もし玄米茶さんがスコープをせまくするのが良いプログラムだとすれば、static public変数を極力少なくするだけでなく、メンバー変数もクラス内に限定されたグローバル共有変数なので、極力少なくされたほうがよろしいです。

http://blogs.wankuma.com/rti/archive/2011/07/18/201127.aspx

その結果、ここに書かれているように、staticにしてもかまわないメソッドが多数できることになります。要するにライブラリ関数のようなメソッドばっかりになる。
そんなことができるのかと疑問に思うかもしれません。

非オブジェクト指向言語の場合は、引数が多くなったりポインタを使うのが面倒で、横着なことにグローバル変数を使ってしまうことがありましたが、オブジェクト指向言語はインスタンスをメソッドの引数やリターン値に使えるので、メソッド内部でグローバル的な変数やメンバー変数の利用を避けることができるので、意外にもstatic publicメソッドを使って、スコープが究極に狭いプログラムを作ることができるんです。

フレームワーク技術の観点からすると、public staticメソッドはポリモーフィズムによるオーバーライドができないので、アプリケーション・カスタマイズには不向きですね。

あー、なるほど。

オブジェクト指向が流行りはじめた頃、"software-IC"と例えられてました。
機能単位をオブジェクトの形でまとめると、その中でどんなにややこしいこと
やっていようが利用者は気にすることなく、ICのピンを電線で繋げばモノ作れる。
で、ピンを繋ぐのはstatic publicでええやんか。ってなノリですかしら。

インスタンス・メソッド(non-static)が定義できるからこそ、
クラス・メソッド(static)の繋ぎ合わせで書けるわけで、
「インスタンス・メソッドなんか要らないよ」にはならんですけどね。

...いかんいかん、わかった"つもり"になってた。

グローバル変数は:
誰でも/どこからでも/どんな順序ででもアクセスできる。
→ スコープ広すぎ。危険。
→ 極力減らそう。最終的にはなくしてしまおう。

てのは理解できるのですが、まったく同じロジックで:

public static メソッドは
誰でも/どこからでも/どんな順序ででも呼び出せる。
→ スコープ広すぎ。危険。
→ 極力減らそう。最終的にはmainひとつにしてしまおう。

と「ならない」のはナゼなんでしょか?

基本的には、staticであろうが、なかろうが、publicなら、どこからでも呼び出せるわけです。

スコープの狭いことに執着するプログラマーはpublc staticメソッドはメンバー関数にアクセスしない(確かできない)し、public static変数も読み出ししかしません。良いプログラマーはそうです。ダメプログラマーはスレッドセーフなどの原則がわからないので、public staticメソッドでpublic static変数に書き込みをしてしまいます。結果として不安定な動作をするプログラムができてしまいます。

staticでないpublic メソッドは、インスタンスごとに違う状態をメンバー変数に持ってますから、スレッドセーフの原則がわからないプログラマーでも安定した動作をするプログラムを作ることができます。

public staticはライブラリ関数と同じなので、作ったり使ったりすると便利なのです。たぶん日本のプログラマーは欧米と比べるとレベルが低すぎるのでスレッドセーフの原則もわからない人が多いからだと思います。public staticメソッドは欧米のサンプルコードでも多く見かけます。

それだけ、日本のIT業界はガラパゴス化している。日本の高校までの教育レベルは世界一だと思いますが、学校の勉強ができるからといって職業としてITをやっていけるかというと問題であるし、受験英語しかできない。グローバルコミュニケーションができない。

通りすがり

> staticでないpublic メソッドは、インスタンスごとに違う状態をメンバー変数に持ってますから、スレッドセーフの原則がわからないプログラマーでも安定した動作をするプログラムを作ることができます。

メンバ変数も、スレッドセーフの原則がわからないプログラマにかかると、とても危険なことになり得ます。

例えば、ここなんかを参照して下さい。
『[実装編]スレッドセーフにすることを忘れてはいけない』
http://itpro.nikkeibp.co.jp/article/COLUMN/20070820/279950/

επιστημη

> たぶん日本のプログラマーは欧米と比べるとレベルが低すぎるので
> スレッドセーフの原則もわからない人が多いからだと思います。

理由になってないんじゃないかなぁ。
シングルスレッドであってもスコープ云々には関係ないし。

僕が思うに、たくさんの処理がprivate/protectedに追い出されることにより、
残ったpublic staticが制御/管理可能な程度に少なく(かつシンプル)になり、
「スコープ広いけど危険は少ない」が実現できるってことじゃないかと。

>みながわけんじさん
>「クラスのメンバへのアクセスを制限するもう一つの方法は、メソッドを出来るだけstatic にすることだ」

static宣言により、static以外のメンバは使えなくなるので、利用範囲を制限しているってのは正しいのですがー
staticにすることとpublic staticにすることは違うわけで、別次元の話を1つにしているように見えますね。

>オブジェクト指向言語はインスタンスをメソッドの引数やリターン値に使えるので

地味に意味がわからないんだけど、それって、オブジェクト指向言語じゃなくても似たようなことはできるんじゃ?
そして、

>意外にもstatic publicメソッドを使って、スコープが究極に狭いプログラムを作ることができるんです。

いやいや、static publicなんて、スコープとしては一番広いというか、大きいじゃないですか。
おそらく・・・
「なんに対してのスコープなのか?」というレベルでスコープという言葉の使い方が違うんだと思うのですが(^^;

>επιστημηさん
>と「ならない」のはナゼなんでしょか?

これは、データをどこが管理しているか?っていう視点で考えるのではないかと。
オブジェクト指向的には、データは用意したクラスが管理し、メソッドで操作するので、
メソッドのスコープが広いことで、データが操作できてしまうのがリスクですよね?

じゃーデータをクラスに持たないと考えたらどうなるか?
みながわけんじさんの理想を追求すると、public staticなメソッドしかないクラス・・・
C#なら、static classとして宣言するようなものですね。
つまり、ライブラリなので、スコープが広くても、どんな順番で呼んでもOKになるわけです。

この辺は、データをどう扱うのがいいのか?ってな話で、なにが正しいってのはないはずなんですよねぇ。
多種多様なデータを同じものとして扱いたい・・・
つまりデータは違うけどやることは同じって場面が多いほど、オブジェクト指向にメリットがでるじゃないですか。
その逆で、データは同じだけど、やることが沢山あるってな場合だと、オブジェクト指向の恩恵も少ないのかなと。

私は、クラスの主役は、メンバ変数がもつデータだと思うんですよねぇ。
だから、みながわけんじさんの

>メンバー変数もクラス内に限定されたグローバル共有変数なので、極力少なくされたほうがよろしいです。

この考え方は、クラスとしては、発想が逆に感じるんですよ。
データが先にあって、その操作にメソッドを用意するわけで、少なく出来るってのは、
設計が間違ってるというかー無駄なデータがあっただけだと(^^;

データのことを一番知ってるのは、そのデータ自身なわけでー
じゃーそいつに全部まかせてしまえーってのが、オブジェクトの真髄だと思うのですよ。
自己完結しているから、他と組み合わせしやすく、取り替えやすい。
おまけに上位互換も簡単に作れるんだから、ただのライブラリとして使うのはもったいない。

まぁ、扱ってるデータにもよるし、過去の遺産との関係もあるからー
実際問題として、現状で問題ないなら、それがその会社や人にとってはベストなんだろうなと。
そして、自分のベストが他人のベストとは限らないという単純な話・・・かな。

επιστημη

>> メンバー変数もクラス内に限定されたグローバル共有変数なので、
>> 極力少なくされたほうがよろしいです。
>
> この考え方は、クラスとしては、発想が逆に感じるんですよ。
> データが先にあって、その操作にメソッドを用意するわけで、少なく出来るってのは、
> 設計が間違ってるというかー無駄なデータがあっただけだと(^^;

いや、僕はコレに関してはすんなり呑み込めました。
「関係の深い変数がいくつかあるならそいつらまとめてクラスにしよぉ」
ってことならば。

int x; int y; int z; なんてのを
Point3D point; にまとめて減らせる。ちゅーことかと。
あるいは int size; int capacity; string* data; を
vector strings; とか。
メンバが抱えるデータの総量は変わらんけども気にせんならんことは減るわな。

通りすがり

なんというか、mutable/immutable、const/非const、副作用あり/なしあたりの話と、スコープの話と、static/非static、public/非publicあたりがごっちゃになってる気がします。

玄米茶

>public static変数のことを言っているのかpublic staticメソッドのことを言っているのか不明なので

public static修飾子のついたすべての要素のことを言いました。

>メンバー変数もクラス内に限定されたグローバル共有変数なので、極力少なくされたほうがよろしいです

Sodaさんの意見に共感できます。
そもそもメンバのインスタンスにアクセスする必要のないメソッドがそのクラスに属していることにあまり意味はありません。staticメソッドが増えるということは、みながわさん自身がおっしゃるようにライブラリ関数のようなものばかりになりますが、このような使い方はいわゆるbetter Cというやつですね。オブジェクト指向である意味は薄れます。小さなライブラリ群を開発する場合はこういう使い方でも構わないと思いますが、コラム本文でも言っているようにできれば工学的な見地で議論をしたいと思ってコラムを書きました。

>スレッドセーフなどの原則がわからないので、public staticメソッドでpublic static変数に書き込みをしてしまいます

スレッドセーフの話は、staticうんぬんよりも排他制御の話ですよね。
staticであろうとなかろうと排他制御が必要なところには必要ですよね。
通りすがりさんのご指摘が当を得ていると思います。

玄米茶

>インスタンスをメソッドの引数やリターン値に使えるので

みながわさんの意見で共感できるところはここでしょうか。
オブジェクト指向言語の本質はオブジェクトの受け渡しだと思います。
カプセル化やポリモーフィズムを適切に使用することで再利用性・疎結合性に繋がっていくと。
私は第一義的にポリモーフィズム等を挙げるようなオブジェクト指向の説明・教育には疑問を持っています。

επιστημη

>>インスタンスをメソッドの引数やリターン値に使えるので
>
> みながわさんの意見で共感できるところはここでしょうか。
> オブジェクト指向言語の本質はオブジェクトの受け渡しだと思います。

同意。ただ、「データのまとまりを受け渡し」であれば構造体で実現できるので、
キモとなるのは「データのまとまりを"メソッドごと"受け渡し」できるてとこかな。
ここでいうメソッドはもちろんnon-static、だからデータが"自立"できる。

> 私は第一義的にポリモーフィズム等を挙げるようなオブジェクト指向の説明・教育には疑問を持っています。

これまた同意。社内で講師を頼まれることがちょくちょくあり、僕は「ADT(抽象データ型)」から入ります。継承もポリモーフィズムもADTを支える脇役的位置づけで。

>επιστημηさん
>Point3D point; にまとめて減らせる。ちゅーことかと。

その発想は無かった、そういう発想もありますねぇ。
でも、スコープの話だったからー
必要なデータとしてみると減ってないので、スコープ的な意味では、あまり関係なさそう。

>玄米茶さん
>オブジェクト指向言語の本質はオブジェクトの受け渡しだと思います。

受け渡しだけなら、オブジェクト指向言語じゃなくてもできると思うんですよ。
むしろ、

>カプセル化やポリモーフィズムを適切に使用することで再利用性・疎結合性に繋がっていくと。

こっちの仕組みが言語レベルで入ってるってのが、うまみかなと。

>私は第一義的にポリモーフィズム等を挙げるようなオブジェクト指向の説明・教育には疑問を持っています。

プログラムコードにしてしまうと、ポリモーフィズムが一番明確に差がでるから・・・かな?
カプセル化は、自己責任という言葉で無効化されやすいし・・・
データの振る舞いについては、ライブラリ的な関数を使う場合と何が違うのかがわかりにくい・・・
まぁ、自分の場合はそうだったってだけですがw
構造体に関数が付けれてなにがおいしいのか、わからなかったってのがクラスの第一印象w
ポリモーフィズムにであって、価値観というか、データのおき場所の違いに目が向いたんですよ。

PS.
あぁ、確認段階でεπιστημηさんのコメントがーw
なるほど、「インスタンス」から「オブジェクト」に変わってたのかー
でも、関数ポインタとかもあるから、やっぱり受け渡しだけなら、できそうな気が・・・
イメージしているのが、CとC++なのがイカンのか?w

επιστημη

> でも、関数ポインタとかもあるから、やっぱり受け渡しだけなら、できそうな気が・・・

できますし、いっぺんやりました。
C++コンパイラがやってくれてることをぜーんぶ自前でごりごりと。
どえらくたいへんなのよ。やれるけど、やってられない。

>Sodaさん

>staticにすることとpublic staticにすることは違うわけで、別次元の話を1つ>にしているように見えますね。

だから、玄米茶さんは、「public staticがスコープを広げる元凶」と書いています。この理由がわからないし、public static 変数なのか、メソッドなのか明確に書いていないからです。意図>オブジェクト指向言語はインスタンスをメソッドの引数やリターン値に使えるので的に別次元に話を一つにしているわけでもないのです。

>オブジェクト指向言語はインスタンスをメソッドの引数やリターン値に使えるので
>地味に意味がわからないんだけど、それって、オブジェクト指向言語じゃなくても>似たようなことはできるんじゃ?

オブジェクト指向の最大の特徴です。非オブジェクト指向言語では、数値、ポインタ、文字列、配列、構造体のようなものにデータとして扱う対象が限定されていましたが、自由にクラスを設定できるようになりまいた。「オブジェクト指向言語じゃなくても似たようなことができる」ならば例を示さなければ、無効な意見です。

>いやいや、static publicなんて、スコープとしては一番広いというか、大きい>じゃないですか。
>おそらく・・・
>「なんに対してのスコープなのか?」というレベルでスコープという言葉の使い方>が違うんだと思うのですが(^^;

あの~、何回も言いますが、static public っていう場合、変数なのか、メソッドなのか判別できるように書いてください。単に語彙だけで意見を判断するのは愚かなことです。
メンバーやpublic static 変数を含まない、public staticメソッドは、そのメソッド内でスコープが限定されているので、玄米茶さんの言うところの良いプログラムに分類されいるということです。staticがあるうが、なかろうがpublicはどこからでのアクセスできる、これはすでに私はコメントしましたよね。Sodaさん人の意見をよく理解してコメントしてくれませんか?

>私は、クラスの主役は、メンバ変数がもつデータだと思うんですよねぇ。

Visual Studio で、フォームを作った場合、自動的にクラスが生成され、GUIコンポーネントはメンバー変数と同じ動作をします。
GUIコンポーネント画面はもちろん、これでかまいません。

メンバー変数を状態変数として扱う場合、たとえばシステムハングなどでサーバーを再起動すれば、その状態変数の値が消えてしまします。状態はDBに登録しないと消えてしまうので、その意味で、メンバー変数を状態変数と扱うのは不十分です。
簡単なゲームには使えますが、クリアに何日もかかるゲームには、すでに不向きです。スクウェア・エニックスの社長が「わが社の公用語はC言語」と言ったのが名言です、ゲーム会社でも公用語は非オブジェクト指向言語なのですね。

>まぁ、扱ってるデータにもよるし、過去の遺産との関係もあるからー
>実際問題として、現状で問題ないなら、それがその会社や人にとってはベストなん>だろうなと。
>そして、自分のベストが他人のベストとは限らないという単純な話・・・かな。

そういうことです。Sodaさんも自分のブログをリンクしていますが、もっと自分の仕事の内容や技術的に勉強されたことをアップしたらいいと思います。

マジなディスカッションになると、サイトをリンクしてコメントしないと、その人のバックグラウンドや知識がわからいので、コメントで議論に参加しても、意見の裏付けがないので無視されがちです(根拠が薄いコメントは相手にしても時間の無駄)。

追伸、Sodaさん

>プログラムコードにしてしまうと、ポリモーフィズムが一番明確に差がでるか>ら・・・かな?

理由を述べてください。論理的な理由がないと、その意見は意味がないです。

>構造体に関数が付けれてなにがおいしいのか、わからなかったってのがクラスの第>一印象w
>ポリモーフィズムにであって、価値観というか、データのおき場所の違いに目が向>いたんですよ。

ポリモに出会って価値観がどうか変わったのか具体的に記述してください。

マジな議論をしているので、へたに雰囲気や印象のみの意見は、これ以上は無視させていただきます。時間の無駄ですので。

通りすがり

スクエニはC++もC#も使ってると思いますよ。ソースはスクエニの募集要項。

それと、データの永続化の話と、プログラム実行中に参照されるデータの話は別物ですね。どんなゲームでも言語が何であろうと永続化先が何であろうと、メモリにロードする必要があります。
OOでは、やはりインスタンスのメンバ変数が主役であることの間違いありません。

みながわさん:
> public static 変数を含まない、public staticメソッドは、そのメソッド内でスコープが限定されているので、玄米茶さんの言うところの良いプログラムに分類されいるということです。

ごめんわからんです。「1:public staticメソッドはどこからでも呼べる」はいいとして、「2:そのメソッド内でスコープが限定されている」は如何なる意味ですか?
public static メソッドの中では使えるクラス/インスタンスが限られる? そんなことないけど。

スコープが狭いメソッド、あるいはメソッド内でスコープが限定されているメソッドとは、メンバー変数にアクセスしないメソッドです。

つまりメンバー変数にアクセスしないことで、クラス内の他のメソッドとの干渉がないので、スコープはメソッド内。もしメンバー変数にアクセスしてしまったら、スコープはクラス全体ということになります(publicメソッドを呼び出す場合は、もちろんクラス全体から可能ですが、どこでそのメソッドが使われているか今の開発環境ならば、簡単に検索できるので問題視する必要なし)。

結果的にそのようなメソッドは状態変数が不要なので、インスタンスなしのstaticメソッドとなります。
ですから、staticメソッドを作る場合は、メンバー変数を使わない、使うとしたら引数に使うようにする。スレッドセーフにするためにpublic static変数に書き込みを行わない(読み込みはOK)、などの(Readableなコードを書くための)暗黙の原則があります。

>スクエニはC++もC#も使ってると思いますよ。ソースはスクエニの募集要項。

募集要項なんてあてになりませんよ。C++できたら、Cもできます。制約を厳しくして、優秀な人材を逃すのは適作ではないという判断からだと思います。

december28

横から失礼します。
みながわさん>

ちょっと質問させてください。

>この理由がわからないし、public static 変数なのか、メソッドなのか明確に書いていないからです。

なんて書いているのに、

>public staticはライブラリ関数と同じなので、作ったり使ったりすると便利なのです。

などと平気で書いていますが、これは変数のことですか?メソッドのことですか?
ライブラリ関数と書いているので、まあメソッドのことでしょうけど。

public static メソッドは確かに便利です。しかし、その便利さは状態を扱えないために限定的なものでしかないでしょう。
たとえば、一般的な業務アプリケーションは、ユーザがログインして使用を開始し、ログアウトすることで使用を終えます。
複数のユーザが別の画面を開けば、ユーザの数だけ「状態」がメモリ上に存在するわけです。

さて、みながわさんは、

>メンバー変数を状態変数として扱う場合、たとえばシステムハングなどでサーバーを再起動すれば、その状態変数の値が消えてしまします。状態はDBに登録しないと消えてしまうので、その意味で、メンバー変数を状態変数と扱うのは不十分です。
>簡単なゲームには使えますが、クリアに何日もかかるゲームには、すでに不向きです。

とおっしゃってますが、ひょっとして、状態を全てDBに登録しているのでしょうか?

ここで言う「状態」とは、次のようなものです。
たとえば、あるWebアプリケーションシステムに、伝票入力画面があるとします。
この画面は、[入力画面] → [確認画面] → [完了画面] と遷移する仕様になっているとします。
あるユーザが、この画面を開き、交通費などを入力して、確認ボタンを押します。
勘定科目などがチェックされて、合計金額が計算されて、確認画面が表示されます。

ここでユーザが入力した「交通費」が「状態」です。
普通ならこれを、static ではないメンバ変数にセットして、セッションに格納するなどして、維持すると思います。
もちろん、この時点でサーバが落ちれば、この「状態」は消えてしまうことになります。
みながわさんは、それを防ぐために、DBに格納する、と、そうおっしゃっているのでしょうか?

みながわさんが普段手がけていらっしゃるのが、みながわさんの言う「簡単なゲーム」のようなシステムならば納得ですが、
私が普段やっているような、複数のユーザが同時にログインして使用するWebシステムでは、
「状態をDBに登録する」なんてことは、ちょっと常識外なので、質問させていただきました。

通りすがり

エピステーメーさん(コピペできない環境なので、正しく表記できなくてごめんなさい)。

私が説明することでもありませんが、それはpublic staticメソッドがメソッド内にstatic変数をもたず、クラス変数も参照しないものであれば、そのメソッドはそこで閉じているので、その内部と外部は粗結合であり、玄米茶さんの言う良いコードに該当するということだと思います。

ちなみに私は、それってCの単なる独立した関数と意味的に変わらないよねと思いますが。

通りすがり

おっと、コメントを書いていたらみながわさんがコメントしてましたね。

みながわさん。
実際にスクエニの募集要項を見ましたか?
FFやドラクエのプログラマー募集要項には、「ていねなクラス設計ができる方」なんて書かれてます。
また、検索すると、ドラクエXのゲーム部分はC++で書かれているという情報がヒットします。

通りすがり

連投すみません。

みながわさん、スレッドセーフにするには書き込み側だけ意識すれば良いというのは違います。書き込み処理の前の読み込みにはロックを掛ける必要がある場合があります。連続する二行の読み込み処理の場合も、ロックを掛ける必要がある場合がありますよ。

επιστημη

> ちなみに私は、それってCの単なる独立した関数と意味的に変わらないよねと思いますが。

うん。まさしくそのとおり。
スコープの広い変数にアクセスしない「行儀のよいCプログラム」と大差ないすね。

>みながわけんじさん
>だから、玄米茶さんは、「public staticがスコープを広げる元凶」と書いています。

たぶん、根本的なとこで話がかみ合ってないと思うのですがー
privateな変数や関数をstatic指定するのと、public static指定するのではスコープが違うでしょ?って話ですよ。
玄米茶さんはpublic staticというもっともスコープが広いものを例として出しただけですよね?
おそらくというか、publicの有無が一番大きな話でstaticが付くとさらに広くなるというだけじゃないかなぁ。

で、スコープってのは、どこから見えるのかって話なわけでー

>スコープが狭いメソッド、あるいはメソッド内でスコープが限定されているメソッドとは、メンバー変数にアクセスしないメソッドです。

たぶんというか、他の方が使われているスコープってのは、そのような限定されたものではなく、
クラスの内外も含めた話だと思うのですよ。

>「オブジェクト指向言語じゃなくても似たようなことができる」ならば例を示さなければ、無効な意見です。

例って、ただの構造体を引数にしたり、戻り値にしたりすることはできますよね?
それで十分じゃないですか?
構造体の中に関数ポインタを含めれば、クラスに近いでしょ?
受け渡すだけなら、できるんですよ。
インスタンスなんて、auto変数と大差ないわけでー少なくとも関数に対しての引数や戻り値は、
オブジェクト指向言語だろうが、非オブジェクト指向言語だろうが、大差ないってのが私の主張。
逆に何を特別だと思っているのかってのが疑問なわけです。

>あの~、何回も言いますが、static public っていう場合、変数なのか、メソッドなのか判別できるように書いてください。

書く必要がないんですよ、どっちもなんだから。

>staticがあるうが、なかろうがpublicはどこからでのアクセスできる、これはすでに私はコメントしましたよね。

そう書いてあるから、なおさらわからないって話ですよ。

「どこからでもアクセスできる=スコープが広い」

おそらく、スコープという言葉は、そのように解釈されるのが一般的であり、
玄米茶さんもそのような意図と使われていると読めます。
でも、みながわさんの使うスコープは、上で説明されている通り、他の人と違う意味で使われているから話が合わないって思うのですよ。
つまり、

みながわさんのスコープ = クラスから見ているクラス内の範囲
他の人のスコープ    = クラスの外から見える範囲

というように、見ている場所と方向が違うのではないか?ということです。
スコープという言葉の意味が同じでも、適応している場所が違えば、話が合わないってことです。

>Sodaさんも自分のブログをリンクしていますが、もっと自分の仕事の内容や技術的に勉強されたことをアップしたらいいと思います。

んー、私自身の話はあまり関係ないと思うんですよねぇ。
外部リンクで説明しないといけないほどの内容は無いと思いますし・・・
どんな人がコメントしているか?よりも、コメント自体に目を向けて欲しいなと思うわけでー
自分にとって特殊だと思える内容であれば、そう書きますし、そうでないなら、それは、私にとっては特殊ではないってことです。

>>プログラムコードにしてしまうと、ポリモーフィズムが一番明確に差がでるか>ら・・・かな?
>理由を述べてください。論理的な理由がないと、その意見は意味がないです。

ん?続けて書いてありますよね?消去法の結果ですよ。
非オブジェクト指向言語で記載した場合、一番類似したコードが書きにくいし、書けたとしてオブジェクト指向言語ほどスッキリ
かけないでしょ?

>ポリモに出会って価値観がどうか変わったのか具体的に記述してください。

本編とは関係ないし、ここで書くのはどうかと思うので、(^^;
http://d.hatena.ne.jp/Deppi/20121215
ただ、価値観って人によって違うから、具体的に書いてもあまりやくにたたないんじゃないかなーと思うのですが(^^;

>C++できたら、Cもできます。

それは成り立たない場合がありますよ。
少なくともクラスを使ってた人がC言語で同じようなコード書けるか?といえば相当疑問。
逆はYesなんですけどねぇ、どこの世界でも下位互換ってのは難しいんじゃないかと。

december28

>ここでユーザが入力した「交通費」が「状態」です。
>普通ならこれを、static ではないメンバ変数にセットして、セッションに格納す>るなどして、維持すると思います。

私の会社および取引がある業者では、これは普通でないです。あなたは当然、これが普通だと思っているので仕方がないですね。話がかみあわなくて当然です。

Sodaさん

関数ポインタなど、そでにコメントで出た話なので、あなたのコメントは、あくまでも世間一般的に言われていることを言っているだけなので、時間が無駄なので読む気がしません。

私の考えているスコープについてのコメントもよく読んでいませんね。

C++ができてCができない人のC++プログラムなんて、まったく信用できません。そういう人は、スキルがあると評価してはいけないでしょう。

あたたのブログを見てると炎上ブログの話ばっかりで、技術的なものが感じられません。そのような人コメントは、警戒されますよ。

通りすがりさん

http://www.jp.square-enix.com/recruit/fresh/recruit/information.html

スクエア・エニックスの 募集要項は具体的な言語などは記載されてませんでした。
プログラミングは下請け、フリーエンジニアに任せるということでしょうか?
実際にゲームアプリの開発をやっている人に意見を聞きたいところですが、そのような人は、@ITなんて読んでませんし、コメントなんて書かないでしょうね。誰かゲーム会社で働いている知り合いしってませんか?

keeper

興味深くROMってましたが、びっくりしたことがあったので質問です。

december28さん>
>ここでユーザが入力した「交通費」が「状態」です。
>普通ならこれを、static ではないメンバ変数にセットして、セッションに格納するなどして、維持すると思います。
>もちろん、この時点でサーバが落ちれば、この「状態」は消えてしまうことになります。
>みながわさんは、それを防ぐために、DBに格納する、と、そうおっしゃっているのでしょうか?

みながわさん>
>私の会社および取引がある業者では、これは普通でないです。
>あなたは当然、これが普通だと思っているので仕方がないですね。
>話がかみあわなくて当然です。


びっくりしたというのはここです。
確認ですが、みながわさんの会社や取引のある業者さんでは、ユーザが入力した「交通費」のような「状態」を、DBに格納しているんですか?
最終的にDBに登録するのはもちろんですが、その前の確定しない「状態」を、いちいちDBに登録しているということでしょうか?
ブラウザからリクエストを投げた情報は、そのままメモリ上で操作するのが普通なのではないでしょうか?
それをいちいちDBに格納するとおっしゃってるんでしょうか?

話がかみあわなくて、と言っているのは、何か勘違いされているのではないかと思います。
通りすがりさんが言っているように、「データの永続化の話と、プログラム実行中に参照されるデータの話は別物」なのを、混同されているのではないでしょうか?

>みながわけんじさん
>関数ポインタなど、そでにコメントで出た話なので、あなたのコメントは、あくまでも世間一般的に言われていることを言っているだけなので、時間が無駄なので読む気がしません。

いや・・・なんていうか・・・例を出せって言ったのは、みながわけんじさんですよね?
私は、特別なことを言ってるわけじゃないので、そういわれても困るのですが・・・

その・・・よくわからないのですが、例として適切ではないという意見でしょうか?
つーか、「世間一般的に言われている」ことと認識しているなら、なんで例を求められたのかわからんのですが・・・
それとも別の話と混ざってます?

>私の考えているスコープについてのコメントもよく読んでいませんね。

ここもよくわからないのですが・・・私がなにか誤読しているという主張ですよね?
元がpublicのメンバ変数や関数をpublic staticにすると、インスタンス以外からもアクセスできるようになってしまいスコープが広がるじゃないですか。
staticでスコープが狭くなるものと広くなるのもがあるって話ですよ?

>C++ができてCができない人のC++プログラムなんて、まったく信用できません。

最近じゃーC言語を覚えてからC++って流れじゃなくて、最初からC++って流れのほうが多いと思うのですよ。
C++固有の機能に慣れた人は、固有機能の代替ができないと、C言語でプログラムが書けないわけです。
代替ってのは、近似したコードであったり、根本的な構造の考え方の変化だったりイロイロですがー
似て非なる言語なので、主は根本的な構造の考え方の変化でしょう。
だから、C++では作れても、Cでは作れないってのは、結構あるパターンだと思うわけです。
C++で作れるスキルと、C言語で作れるスキルは別物って主張ですよ。

>あたたのブログを見てると炎上ブログの話ばっかりで、技術的なものが感じられません。

特に技術的なブログを目指してないので、当たり前ですが(^^;
それ、なにか関係あるんですか?
誰が書いているか?よりも(コメントが)何て書いてあるか?ってほうに重点を置いてみて欲しいなぁと思うのですが・・・

keeper さん

あなたの主張や書き方がごもっともです。
「最終的にDBに登録するのはもちろん」
と書いてあれば、私ももちろんOKです。

december28さん>
>[入力画面] → [確認画面] → [完了画面]
この書き方ではDBに登録しなで完了(終了)と思ってしまいます。

december28

みながわさん>
表面的な語彙だけではなく、内容をしっかり読んでいただけないでしょうか。
私が質問したかったのは、途中の状態をいちいちDBに登録しているのかどうかであって、最終的にDBに登録するのかどうかは、この際、関係ないのです。
もう一度、質問にお答えいただけないでしょうか?

BT

PS VITAはC#やVisualStudio
XboxやWindowsゲーム用はC++
PS3もC++
ゲーム用途はオブジェクト指向ですね。
Cも全く使ってない訳ではないみたいですが。

玄米茶

みながわさんの文章がどうも関数型言語のことを言っているように見えたので、その観点からの私の見解を述べますね。

関数型言語の関数は副作用を持たないという原則がありますが、純粋に副作用を持たないモジュールのみで開発が完結できるソフトウェアの領域というのはきわめて少ないと思います。
実際、関数型言語の中には副作用を許容する仕組みを取り入れたものもいくつかありますし、今はやり(?)のscalaは関数型言語の特徴ばかりが取りざたされますが、結局のところあれはオブジェクト指向言語であり、必ずしもscalaで作られたプログラムが関数だけで成り立っているわけではありません。

関数型言語の利点は、簡潔にコードが書けるということと副作用がないことの明示化であり、主に可読性の向上にあると思います。
プログラム中の副作用を必要としない部分を注意深く切り取り、モジュール化し、その部分のみを関数で記述する、という使われ方ではないかと私は推測しています。

また関数型言語の関数も(オブジェクト指向のインスタンスのように)きちんと変数にとるなどして、持ちまわることが基本になると思います。
そうすることで関数に施せる高度な仕組みを利用するからです。
public staticメソッドは、関数型言語の関数のように変数にとったり引数にとったりすることができませんし、カリー化などの高度な仕組みも持っていません。
単純な呼び出しの関係でしかないpublic staticメソッドは、実装依存という観点、またメソッド自身のスコープの広さから言って、結合度はそれほど低くはないと言えると思います。
結局のところ、副作用がないことの明示化という意味においてユーティリティや末端の処理などにしか活用できないと思います。
そしてこれは、コラム本文で「可読性向上のための用途がある」と述べた部分です。
決してpublic staticメソッドが疎結合性の観点から良いプログラムに分類されるとは思えません。

通りすがり

このコラムを初めて読んでから一週間ほどになりますが、「スコープ拡大の元凶であるpublic staticは」の意味がわからず依然としてモヤモヤしています。

「低い結合度」「高い凝集度」が良いのはほぼ自明だと思いますが、「public static」は結合度のどの部分に影響するものなんでしょうか?

クラス対クラス(あるいはインスタンス対インスタンス)が密に結合しているのは良くないし、同一クラスあるいは同一メソッド内に異なるレイヤーの処理が混在しているのも良くないというのはわかります。
それを解消するのに、OOのインターフェイスに対する実装とか、抽象と具象という概念が役立つというのもわかっています。

その上で質問したいんですが、public staticが結合度を阻害することを説明する例はありますか?

public static は他の public static をお構いなしに呼べるのでモジュールどうしの"糊"になり、
そのことが結合度を大きくしてしまいがちなんじゃないかと考えます。

http://ja.wikipedia.org/wiki/%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%97

によると、

「プログラミングでは、予期しない誤作動を避けるためにも、それぞれの作業段階で必要のない名前はできるだけ参照されないようにすることが望ましい(大域変数の危険性)」

ですから、スコープで危険性のあるのは、public static変数やメンバー変数の多用濫用であり、メソッドがpublicであるかは、それほど問題視されないというのが持論です。

メソッドは共有変数により干渉し複雑化する危険性があるという意味で、メソッド間の「糊」となるのは、共有変数です。

メソッド間の「糊」となるのはメソッドではありません。

そうでなければ、何故ライブラリ関数など安心して使えるのですか?

もちろん、すべて public staticで作れという意味ではありません。最近の開発ツールではフォームをつくるとクラスが自動的に生成されGUI画面のプログラムが簡単にできます。
あくまでも、スコープや「糊」とは何か?考えてみた自論です。

> メソッド間の「糊」となるのはメソッドではありません。

なぜ?

public class ModuleA {
public static void f() {
ModuleB.g();
ModuleC.h();
ModuleD.i();
}
}

ModuleA.f() を使うには ModuleB/C/D を連れてこにゃならんです。
これが「糊」じゃなかったら何なんでしょ。

通りすがり

επιστημηさん。

なるほど。私は、public staticと聞くと、SingletonのgetInstance()や、完全に独立したUtility Classを思い浮かべて、それ自身にはそれほど問題はないのではと思っていたんですが、確かに
> public class ModuleA
のようなコードがあちこちに散らばっている状況を考えると、これは確かに萎えますね。

通りすがり

みながわさん。

> 最近の開発ツールではフォームをつくるとクラスが自動的に生成されGUI画面のプログラムが簡単にできます。

この話が何度か出てきましたが、本コラムとどういう関連性があるんでしょう?

επιστημη

こっち↓についても僕の見解を述べておきます。

> メソッド間の「糊」となるのはメソッドではありません。
> そうでなければ、何故ライブラリ関数など安心して使えるのですか?

public class ModuleA {
public static void f() {
 Lib.g();
 Lib.h();
 Lib.i();
}
}

いくら呼んでも連れてくるのは Lib ひとつ。
しかも Lib が ModuleA/B/C/D を呼んではいない。
統制が取れてるのよね。

>通りすがりさん
>> public class ModuleA
>のようなコードがあちこちに散らばっている状況を考えると、これは確かに萎えますね。

ん?通りすがりさんが言ってるように、完全に独立したクラスというかー
ライブラリ関数のようなクラスなら、ある程度結合度が高くても、いいんじゃないかなぁ?
上のεπιστημηさんの例も、

>ModuleA.f() を使うには ModuleB/C/D を連れてこにゃならんです。

機能別に分類したライブラリモジュールならOKじゃない?
この場合、クラス名はライブラリ名程度の違いしかないと思うのですよ。
だから2つの例に明確な差があるかというと、無いようにも見えるんですよねぇ。

ModuleBの中でModuleAを使うなど、相互に呼び出しているような結合度だと、駄目だとは思いますが(^^;
public staticだと大抵のことはできちゃうからなぁ(^^;
仕様変更とかあった場合に、モジュールとして差し替えにくいってのもイマイチかな?
まぁ、差し替えするようなものをpublic staticにするのがNGだと思うから、その心配はないのでしょうが(^^;

>επιστημη 2012年12月18日 (火) 16:37
>> メソッド間の「糊」となるのはメソッドではありません。
>なぜ?
>public class ModuleA {
>public static void f() {
>ModuleB.g();
>ModuleC.h();
>ModuleD.i();
>}
>}
>ModuleA.f() を使うには ModuleB/C/D を連れてこにゃならんです。
>これが「糊」じゃなかったら何なんでしょ。

public static void f()
でなくても
staticでないメソッド public void f()
クラスないのローカルメソッド void f()
でも、まったく同様のことが言えるので、何故public static メソッドがスコープを広げる元凶となるのか?という議題についての理由になっていません。

↑わけわからん。

public class ModuleA {
 // static でないメソッド
 public void f() {
  ModuleA.g();
  ModuleB.h();
  ModuleC.i();
 }
}

そう、まったく同様にstaticでないメソッドからも
public static メソッドが呼べてしまいます。
public static メソッドを定義するからこんなことが起こるんです。

public class ModuleA {
 // static でないメソッド
 public void f() {
  this.g();
  this.h();
  this.i();
 }
...
}

これならModuleAで閉じてるもの。

> ModuleBの中でModuleAを使うなど、相互に呼び出しているような結合度だと、駄目だとは思いますが(^^;

僕が「おっかない」と感じているのはまさにソレ。
綺麗なDAGになってりゃいいけどね。

public static メソッド、僕は否定しません。
「スコープ広いから注意しようね/乱用は禁物よ」ってだけのこと。

ナッシュビル


みながわさん>
ちゃんと日本語読んでますか?

「プログラミングでは、予期しない誤作動を避けるためにも、それぞれの作業段階で必要のない名前はできるだけ参照されないようにすることが望ましい(大域変数の危険性)」

要するに、スコープはできるだけ狭くしろ、ということです。
理由がなければ、public は使うな、という意味ですよ。

>メソッドがpublicであるかは、それほど問題視されないというのが持論です。

みながわさんの持論ですね、あくまでも。
それは間違いです。
意味もなくメソッドをpublic にするのは大問題ですよ。

>メソッド間の「糊」となるのは、共有変数です。

つまり、static変数ということですか?
メソッド間でデータを受け渡しするのに、static変数を使うんでしょうか?
それは、ものすごく危険ですね。

>最近の開発ツールではフォームをつくるとクラスが自動的に生成されGUI画面のプログラムが簡単にできます。

それは、このコラムの話題と何の関係があるんでしょうか?そんなことを得意げにひけらかされても理解に苦しむのですが。

あと、december28さんの質問をスルーしているのは、答えに詰まっているからですか?

↑わけわからん

>そう、まったく同様にstaticでないメソッドからも
>public static メソッドが呼べてしまいます。

そうですが・・・

>public static メソッドを定義するからこんなことが起こるんです。

ここで、論理の飛躍があります。staticでないメソッドからもstaticでないpublicメソッドが呼び出せます。

public void f()
は、他のクラスからメソッドとしてよりだせますよね。

>これならModuleAで閉じてるもの。

閉じてないですよ。

ナッシュビル

みながわさん>
それから、みながわさんの投稿は誤字脱字、主語が何なのかはっきりしないものが多く、意味がつかみにくいことが多いです。そのことも混乱の一因となっていますね。
文章を書き慣れていないうちは、何度か読み返してみましょう。
社会人としての忠告です。

追伸

私の2012年12月19日 (水) 08:58投稿は、επιστημη さんの自論に対しての感想です。

他の方が割り込んできたので、「↑わけわからん」は誤解を受けます。

ナッシュビルさん
人のコメントに対しての回答義務は、コメント者やコラム筆者にはありませんので、ご了承ください。ノーギャラですので・・・

december28さんの課題については、よくあるプログラムの例なので、巷の本やインターネットの情報を調べれば、利用している開発環境での、推奨方法がいくつか乗っているので回答する必要はないと私は思っています。また玄米茶さんの本コラムの議題「スコープ」とは、あまり関係がありません。

1> 問題はスコープです。
そのとおり。

2> スコープの範囲を極力せばめるのが基本です。
まさしくそのとおり。

3> 型に属するのか、インスタンスに属するのかで考えるのが基本です。
型に属する(public static)メソッドはどこからでも呼べる
インスタンスに属するメソッドはその呼び出しにインスタンスを必要とする。
したがって型に属するメソッドはスコープがより広い。
上記[2]により、public staticメソッドが増えるのは望ましくない。

...という"自然な主張"なんだが、どこかにアナがありますかしら?

december28

みながわさん>
>december28さんの課題については、よくあるプログラムの例なので、
>巷の本やインターネットの情報を調べれば、利用している開発環境での、
>推奨方法がいくつか乗っているので回答する必要はないと私は思っています。

私が聞きたいのは、まさにそれが理由です。巷の本やインターネットの情報や
社内や仕事をしたことのあるプログラマーたちの書くどんなコードを見ても
途中の状態をいちいちDBに登録するなどというトンデモな書き方をしている
人はいなかったので、びっくりしてしまったのです。
何かの釣りかと思ったのですが、いたって真面目に書かれているようなので、
ますます混乱してしまいました。

もう一度書きます。私の想定は以下のようなものです。Webアプリケーションです。

1.ユーザがブラウザから、何かの値を入力してrequestを投げます。
2.サーバ側プログラム(たとえばServlet)は、そのrequestに含まれる値を読んで、
 static以外の変数に格納します。
3.この先、この値がどうなるかは、この段階ではどうでもいいです。
 DBに格納するのかもしれないし、ログに書き出して終わりかもしれないし、
 何かの計算ロジックに渡すのかもしれません。
4.もちろん、この段階でサーバが落ちたら、ユーザが入力した値は消えます。

さて、みながわさんの

>みながわけんじ 2012年12月15日 (土) 13:40
>メンバー変数を状態変数として扱う場合、たとえばシステムハングなどでサーバー
>を再起動すれば、その状態変数の値が消えてしまします。状態はDBに登録しないと
>消えてしまうので、その意味で、メンバー変数を状態変数と扱うのは不十分です。

を素直に解釈すると、上記2. で、

変数ではなくDBに格納する

と読めますが、これは本当ですか?

みながわさんの会社の業種が何なのかしりませんが、全状態が一瞬たりとも消えてしまっては
困るような業務システムを扱っていらっしゃるのでしょうか?

回答の義務がないのはわかっていますが、あまりにも理解を超えているので、
若輩者に教えを授けるという意味で、お答えいただければと思います。

december28さん

>2.サーバ側プログラム(たとえばServlet)は、そのrequestに含まれる値を読んで、
> static以外の変数に格納します。

メンバー変数に格納する理由がわかりません。
ラベルコントロールに格納して、ユーザーに値を確認させる。ASP.NET ならばこれができます。

Error401(通りすがり)

Aさん。

> @ITを読んでいると、よくεπιστημηさんが仲間を引き連れて(プロバイダを複数契約しているのかな)暴言や自画自賛を交えて、他の読者を攻撃している光景が向けられます。普通に一人で議論できないのでしょうか?不快です。

ひょっとして私がその仲間だと思われてたりするんでしょうか。
通りすがるつもりだったんですが、長引いたので別の名前にしときます。

> 実装面ではstaticの有無には差がほとんどありません。暗黙のthisポインタがあるのかないのかだけです。そこまで気にすることではありません。些細なプログラミングテクニックでなく、設計面で考えるべきです。

私は逆に設計面で考えたときに、public staticは些細なプログラミングテクニックではないと考えます。

public staticは、それが有効である場面に限って使うべきもので、それ以外の場面では使えても使ってはならないものだと考えます。

例えば、人口に膾炙したテクニック(SingletonパターンのgetInstance()など)や、完全に独立した不変でモジュールとして強固な機能を集めたUtility Classのメソッドでは、public staticが有効です。
もう一つの例は、クラス変数を操作するクラスメソッドの場合です。
以上の例は、確固とした意思表明としてのpublic staticであり、設計の意図を反映したものです。

他方、そのようにできるからという理由や、そうすればインスタンス化が不要という理由だけでpublic staticをあちこちのクラスに作ってしまうと、それを使用するコードと特定クラスが強固に結合してしまいます。

クラスが多数表現されているクラス図を想像して下さい。public staticがあちこちのクラスに多数存在し、それをまたあちこちで使用していてそれを「関連」として線で結んで表現すると、これはもう絶望的になると思うんですが。

december28

みながわさん>
>メンバー変数に格納する理由がわかりません。
>ラベルコントロールに格納して、ユーザーに値を確認させる。ASP.NET ならばこれができます。

すみませんが、ちょっと聞きたいことがずれています。
ラベルコントロールに格納しただけでは、サーバが落ちた場合、ユーザが入力した値は消えてしまいますよね。
みながわさんは、そのような事態を防ぐためにDBに登録すると発言されていますが、その点をうかがいたいのです。

Error401(通りすがり)さん

>クラスが多数表現されているクラス図を想像して下さい。
>public staticがあちこちのクラスに多数存在し、
>それをまたあちこちで使用していて
>それを「関連」として線で結んで表現すると、
>これはもう絶望的になると思うんですが。

じゃあ、public staticメソッドを public メソッドに変えてみたらいかがでしょうか?それはもっと絶望的な状況です。

スレッドセーフなpublic static なんてライブラリ関数と同じ扱いでかまわないので、線で結ぶなんてナンセンスですね。

december28さん

玄米茶さんは、このコラムでスコープについて語りたかったのでテーマがずれています。

>若輩者に教えを授けるという意味で、お答えいただければと思います。

本当にそう思っているならば、私のサイトにメールアドレスがあるので、メールを送ってください。
返信して回答を差し上げます。
よろしくお願いします。

Error401(通りすがり)

みながわさん。

> じゃあ、public staticメソッドを public メソッドに変えてみたらいかがでしょうか?それはもっと絶望的な状況です。

トートロジーになるんですが、もちろん通常のpublicメソッドを絶望的な状況なまでにスパゲッティに絡ませてしまうと、それはもう絶望的です。public staticが多数あると、その絶望的な状況に陥る確立が上がるのではという認識です。

なおクラス図を書くときは、◇→や◆→や単なる→でクラス間の関連は明示しますよ。ただし、完全に独立したUtility Classは線を引きません。

> スレッドセーフなpublic static なんてライブラリ関数と同じ扱いでかまわないので、線で結ぶなんてナンセンスですね。

それには同意します。

今までの流れの中で言う「駄目なpublic static」を使ってしまうと、絶望的な状況に陥るかもよという感じです。

Error401(通りすがり)

あ、ちょっと待って下さい。

> > スレッドセーフなpublic static なんてライブラリ関数と同じ扱いでかまわないので、線で結ぶなんてナンセンスですね。
>
> それには同意します。

class Fooに10個のメソッドがあって、その中に一つ二つスレッドセーフなpublic staticメソッドがある、という状況の場合は別ですよ。

class全体がpublic staticな性質の場合は線を引かないという意味です。

december28

みながわさん>
>玄米茶さんは、このコラムでスコープについて語りたかったのでテーマがずれています。

最初に状態をDBに登録するという話を出したのはみながわさんです。
ご自分で持ち出した話題に対して質問されて、テーマがずれているのでここでは答えない、というのは
公平ではないのではないでしょうか。
ここで始めた議論はここで終わらせましょうよ。
回答をお待ちしています。

december28さん

繰り返しますが、ノーギャラなので、回答の義務はないです。

みながわwrote:
>メンバー変数を状態変数として扱う場合、たとえばシステムハングなどでサーバー
>を再起動すれば、その状態変数の値が消えてしまします。状態はDBに登録しないと
>消えてしまうので、その意味で、メンバー変数を状態変数と扱うのは不十分です。

december28さん wrote:
>みながわさんの会社の業種が何なのかしりませんが、全状態が一瞬たりとも>消えてしまっては
>困るような業務システムを扱っていらっしゃるのでしょうか?

私は全状態が一瞬たりとも消えてはいけないと書いてはいないので、答えは 「NO」 です。

みながわwrote:
>メンバー変数に格納する理由がわかりません。
>ラベルコントロールに格納して、ユーザーに値を確認させる。ASP.NET ならばこれができます。

december28さん wrote:
>すみませんが、ちょっと聞きたいことがずれています。
>ラベルコントロールに格納しただけでは、サーバが落ちた場合、ユーザが入力した値は消えてしまいますよね。
>みながわさんは、そのような事態を防ぐためにDBに登録すると発言されていますが、その点をうかがいたいのです。

前の答えがNOならば、この場合もサーバーが落ちたら、ユーザーが入力した値も消えます。
ただし、私の質問、
>メンバー変数に格納する理由がわかりません。
について、december28 さんが回答してくれないのは公平ではありませんし、本コラムのテーマである、「スコープ」に関係ないので、今後も回答しなかったり、メールでの連絡を求めたりすることはあります。

december28

みながわさん>
それ、質問だったのですか?
「~がわかりません」と言うのは、一般的に質問とは呼べないでしょう。
ひょっとして、普段からあまり人と対話することがないのでしょうか。

とにかく、あくまでも話題から外れると言い張るのであれば、これ以上質問をするのは止めておきます。
ここでの投稿を見ている限り、みながわさんの主張には、どうも一貫性がないようですので、
筋の通った答えが得られるとも思えませんし。
社会人何年生なのかは知りませんが、もう少し勉強されることをお勧めします。
マイクロソフトのツールの知識だけでは、そのうち困るシーンが出てきますよ。

Error401(通りすがり)

Aさん。

このコラムのコメント欄の「通りすがり」は、全て私が書きました。

> ただ、staticに拘る問題ではなく設計の問題だと指摘しているだけです。

私に指摘をしているのですか?
なぜ私が指摘されるのか良くわかりません…。

>επιστημηさん
>↑わけわからん。

επιστημηさんはModuleB、ModuleC、ModuleDでpublic staticの例を出したのに、
みながわさんは、void f()のほうにpublic staticがあるので、void f()に着目した・・・ってことかな。
だから次の例でも、public void f()の中身がModuleA「で」閉じているという説明に対して、
public void f() に目がいっているから、ModuleA「が」閉じてないってかえってくるのかなと。
たぶん・・・private void f()ならよかったのではないかと(^^;;;;;;;

>メンバー変数に格納する理由がわかりません。
>ラベルコントロールに格納して、ユーザーに値を確認させる。ASP.NET ならばこれができます。

ラベルコントロール自体がメンバー変数だと思うんだけど、違うの?
それともメンバー変数って別の意味なんだろうか?
それ以前に状態変数ってどういう意味でつかってるのかってとこまでさかのぼらないと理解するのが難しそう。
december28さんは、状態変数を「一時的な変数」として扱っているように見えるし、だからこそ、
DBに登録するような表現に違和感を感じているように見えるんだけどなぁ。

最終的に、みながわさんのいう「状態変数」ってなんだったんだ?ってのが謎として残ったような印象。

私がASP.NETに詳しくないからかもしれませんが、ASP.NETの概要みると、VB.NET以前のVBに近い印象ですねぇ。
主データは全てデータベース、コントロール貼り付けて、それを操作するわけでー
簡単なものはそれだけでOKってな感じですかね?

>それでは全部ローカルメソッドのコピペになってしまうでしょ。

どうしてそういう発想になるのか、わからなかったけど、同じ処理を関数にまとめて処理するってな
VB初期の構造化プログラミングの流れで考えるとー
public staticなメソッドを用意したクラスならそういうことができるけど、それが使えないってことは、
関数をまとめられないので、同じ関数があちこちに用意されてしまうってな発想なのかなと。

こー、みながわさんの世界というか、理想は、C++をbetter Cとして使う方法に近いイメージで
ASP.NETをbetter VBとして使うようなものだろうか?
頭にハテナマークが浮かんだ人も、そう考えると見え方がかわりそうな気がする・・・

PS.
中傷には、イロイロ突っ込みたいとこありますがー
できれば、「あきらかにおかしい」と思う発言は引用しないほうが、会話が成立するコメント欄になると思います。
イロイロな意味でです。

玄米茶

>Error401さん

ありがとうございます。
そう言われるとモチベーションが上がりますね!

皆さんするどくていらっしゃるので私の主張したい話をいくつかコメント欄で先回りされ、なんかもうかなり満足してしまっていたんですが、頑張ろうかなって気になりました。
コメント欄については、私は非常に満足しております。
皆さんコメントありがとうございます。

取り急ぎ、お礼のコメントでした。

> 理想は、C++をbetter Cとして使う方法に近いイメージで

僕はけっこーそんなコード書きます。コマンドライン・アプリは特に。
小さな部品となるクラスが揃ったらあとはべたーっと関数並べて。
大きな流れが縦一列に並ぶので可読性は良好です。
# みながわさんのイメージに近いのかも

...そっか,Java/C#の類はクラスに属さない関数を定義できないから
public static メソッドで何らかのクラスに貼り付けにゃならんのか。
そうすっと手続きがあちこちにバラけがちになるのかな。

public class 一連の流れ {
 static void あーする() { ... }
 static void こーする() { ... }
 static void そーする() { ... }
 public static void Main() { あーする(); こーする(); そーする(); }
}

ってやりゃいぃじゃん。

>επιστημηさん
> public static void Main() { あーする(); こーする(); そーする(); }

GUIを使うものを、Visual Studioで作ろうとするとMainに該当するようなとこが勝手に作られるわけでー
んでもって、GUI部分をデザイナの貼り付けで作ると、コードと分離してメンバ変数化されるじゃないですか。
仕組みとして、うまく隠してくれているから、気にすることはなくなるのかなと・・・
気にしないだけじゃなくて、特別なものに見えやすいのかなと・・・

でもって、データベース操作が主目的だと、データ用のオブジェクトを作るまでもなく、各画面ごとに

GUI入力->データベースに反映

ってな流れで完結してしまい、「オブジェクト指向?なにそれおいしいの?」となっても不思議ではないかなーと。
この流れで、入力に対する共通処理をライブラリ関数にまとめてしまうのも自然かなぁと。

ここで、画面の基本クラスをつくって、派生させれば、共通処理をまとめれるわけですが・・・
先のようにVisual Studioが自動で作っている部分に手を加えることになるので、
そういう発想には到達しにくいし、到達しても手間だと感じてしまえば、そこで終わりかなと。
自動化されているところに手をいれるリスクを考えると、ライブラリのほうが安全なのは確かなんだけど、
こんどは仕様変更が面倒になる罠がまってるのよね(^^;

このあたりの内容は開発環境依存だから、Javaとかだとまた違いそうですね。

私の場合、一人で作る場合が多いので、好き勝手できるけど、IT系である多人数開発だと、
安全の考え方からして違うんだろうなーと思ってみてるんですよねぇ。
スコープの問題もそう、一人で作ってるなら自己責任でどうとでもなるけど、多人数開発だと、
見えてしまうことが問題になるんでしょうね。
全ての開発者が同じレベルではないということは、見えて、使えるという状況でなにをされるか
わからないというリスクを背負うってことになるでしょうし(^^;;;;;;;

PS.
C#でWindowsフォームベースのものを多言語化すると、Formの派生でデザイナが意図しない動作を
してしまう時があるので、画面の派生ってのは鬼門なのかなーと思ったり思わなかったりw
まぁ、拡張コントロール使わなければ大丈夫なんでしょうけど、VS2008でハマったんだよなぁ(^^;

玄米茶

staticメソッドについては、正直私もあまり目くじらをたてるものでもないと思いますが、

>スコープの問題もそう、一人で作ってるなら自己責任でどうとでもなるけど、多人数開発だと、見えてしまうことが問題になるんでしょうね。

Sodaさんのこのコメントがまさにそうで、プログラミングの議論をする場合には、より工学的に話を進めないと議論にならなくなっちゃうんですよね。
なので、より大規模でより大人数でより長期的な開発にも当てはまるように想定して話をしないといけないんです。
ちっちゃなプログラムなんて、どう作ったってたいした問題は出てこないんですから。

私はメソッド間の「糊」となるのは引数だと思っていて、staticメソッドが増えれば増えるほど、そのメソッドが必要とするデータ(引数)を持ちまわる量も増える可能性が出てきます。
本来ならばこれらのデータはメンバ変数としてカプセル化(データ隠蔽)され、当該メソッドとともにインスタンス化されることで、安全な状態となって持ち運びされるべきです。
各々のデータは各々の適切な箇所でのみ意識され設定され、またデータの仕様変更にも影響を受けにくくなります。
これは単純に、データの構造化とかカプセル化の辺りの話をしているだけです。
適切にクラス設計されカプセル化されたプログラムというのは、データの持ちまわりが基本となるC言語のような手続き型言語と比較して、全体のスコープの総量を小さくできるはずなんです。

このようなオブジェクト指向における極めて重要な設計思想は、ポリモーフィズムによる抽象化の話の影に隠れてないがしろにされがちです。
私が「オブジェクト指向言語の本質はオブジェクトの受け渡し」だと言ったのは、この辺りの話がしたかったからと、抽象化の仕組みはオブジェクトの受け渡しが土台としてあるからこそ実現できるものである、というそもそも論的な意味合いも込めての発言でした。

A

玄米茶さんへ

>私はメソッド間の「糊」となるのは引数だと思っていて、staticメソッドが増えれば増えるほど、そのメソッドが必要とするデータ(引数)を持ちまわる量も増える可能性が出てきます。
それはならないと思います。
メソッドはあくまでも型に所属するものであり、そうなった場合はstatic有無に関係なく型の設計が間違っているだけです。
インタフェースの問題は設計で解決されるべき問題です。
玄米茶さんはしないと思いますが、この書き方だとstaticさえつけなければいいと短絡的な誤解をする人が出る恐れがあります。

>このようなオブジェクト指向における極めて重要な設計思想は、ポリモーフィズムによる抽象化の話の影に隠れてないがしろにされがちです。
それは知りませんでした。
オブジェクト指向は全てがそろって始めて有効な技術であり、偏った説明がなされているとは意外でした。
そんな状態は駄目ですね。納得です。
それだけにstaticだけに話が流れたのは残念です。
応援しています。


他の人へ
staticおじさんなどとレッテル貼りをして、staticの使い方をよく考えないと本質は見えてこないと思います。
staticがいらない子ならば、そもそもコンパイラに用意しません。
頭ごなしに拒否するのでもなく、かといって多用もせず、冷静に考えるべきです。
レッテル貼りで思考停止せず、言語仕様の全てを有効利用できて初めてプロといえます。
自分が知らない事を否定していては知識が広がりません。
ましてや議論を拒否し、誹謗中傷に徹する様は愚かさの極みです。
レッテル貼りした時点で技術論ができないといっているのと同じであり、した人が負けです。
今後、子供じみた行為が横行しないことを祈っています。

Z

論点がぼやけてしまって誤読してるかもしれないが、モジュール設計上での広域Staticの存在を許容するか、否定するかの二元論になってませんか?
OOP派の人々が広域変数・メソッドを悪玉にしたがっているのはわかる。
goto文が諸悪の根源だ・・・・的な不毛論にしか映らない。
言語構文として staticが存在するのだから、言語特性として使用制限は無理。
コーディングマナーとしてどうよ。の問題だよね。
C言語の longJump のように、副作用の強い劇薬兼毒薬もある。使う局面は少ないし、一般的に非推奨だが、使う局面も少ないがある。
goto文も使ったほうが見通しがよくなる場合もある。
結局、
 カッコいいソースだが、動作が緩慢である.
 不細工さがあるが、キビキビ動く。保守が楽。
何時もの、水掛論になっていそうな感。

Staticを否定する人に聞きたいのだが、MS.VB のモジュールクラスをどのように認識しているのか?
java/c#等のoop派の人たちから、諸悪の根源のように言われているが、世のms.netアプリの大きな部分にモジュールクラスが使われてる現実をどう解釈するのか。
業務アプリは確実に動作するのが第一で、ソースのスタイルは重要視されない現実を否定するのか?
開発者の一部は、ソーススタイルに興味がない。広域変数メソッドがもたらす不具合よりも、手早く作れるほうがメリットが大きい。
この手の論争でいつも思う。
理想をいうのは簡単だか、現場に浸透させる手段が提示されないの評論家でしかない。開発者であるならば、打開策を提示すべきだ。
Aさんの主張が受け入れられないのも同根だと。

Jairo

Zさん、何か勘違いしてませんか。

玄米茶さんはユーティリティや一意性を示すのにはstaticは有用だと、コラムの中でちゃんと書いています。そのようなもの以外のstaticを使うと、粗結合性を阻害したりカプセル化を破壊したりする場合があるというのが、玄米茶さんの主張です。

それから、業務アプリの場合でも、ソースのスタイルは重要視されてます。

Z

>ソースのスタイルは重要視されてます。
業務開発のとき、ソーススタイルに費やす工数が取れんのです。
スタイルレビューなどに裂く時間があれば、開発に回す。
業務仕様の動きが確認されれば、バグは0個として、次の工程に進む。
こういう、自転車操業的な現場しか経験したことがない。多分他でも類似であろう。
そういう現場で、ソースの品質云々をいっても埒が明かない。
余裕があればこそソース品質向上云々が言えるのであって、余裕のない現場では、対岸の話題でしかない。

Jairo

Zさん、ここはプロセスの改善策を話し合う場ではありません。
そのような話がしたいのであれば、しかるべき場所で行ってください。

A

Zさんへ
>論点がぼやけてしまって誤読してるかもしれないが、モジュール設計上での広域Staticの存在を許容するか、否定するかの二元論になってませんか?
OOP派の人々が広域変数・メソッドを悪玉にしたがっているのはわかる。

そうなんです。短絡的かつ使用法を知らない人がstaticは悪だと騒いでいます。
彼らはどちらかというと、にわかOOP派で他者を見下したいという欲求の元、みながわさんに対する誹謗中傷をしているという状態です。
技術的に考えているというよりも、誹謗中傷をしたいから理由をつけてやっています。
ですから、彼らに技術論を言っても無駄です。
目的が誹謗中傷の人たちだから見ての通り会話になりません。
技術的に言うのであれば、型のアクセス範囲の問題になります。
staticにしたからと言って、必ずパブリッククラスにする必要はありません。
さらにいえば、関数だけを公開する必要もありません。
ちゃんと戦略的に設計をしていれば問題がありません。

ソースコードについては重視しています。
重視しているからこそ短絡的にstaticを拒否しません。
短絡的にstaticを拒否すると選択肢が狭くなります。
オブジェクト指向をするからこそ、staticが必要な局面もあります。
型に属するのか、インスタンスに属するのかは重要な設計要因です。
そうでなければ、言語設計者はstaticを導入しません。
きれいなソースコードについては、時間を割くというよりも、文化を育てる方向でいけば上手くいきます。
自分の職場は自然とチェックする体制になっています。
プロ意識を高めるのが一番の解決法です。

玄米茶さん

>私はメソッド間の「糊」となるのは引数だと思っていて、staticメソッドが増えれば増えるほど、
>そのメソッドが必要とするデータ(引数)を持ちまわる量も増える可能性が出てきます。
本来ならばこれらのデータはメンバ変数としてカプセル化(データ隠蔽)され、
>当該メソッドとともにインスタンス化されることで、安全な状態となって持ち運びされるべきです。

staticメソッドが増えるほど引数が増えるという論理は間違っています。
メソッドをなるべく再利用されるように、汎用化するほど、パラメータの数は増えるので引数の個数が増えます。

引数の個数が増えれば、プログラミングしにくいので、メソッド関数ではなくクラス化して、デフォルトパラメータを設定すれば、プログラミングしやすくなる、という当たり前の論理です。

シドニア

みながわさん>
>staticメソッドが増えるほど引数が増えるという論理は間違っています。
>メソッドをなるべく再利用されるように、汎用化するほど、パラメータの数は増えるので引数の個数が増えます。

オーバーロードという仕組みをご存知ないのでしょうか?

public void foo();
public void foo(String arg1);
public void foo(int arg1);
public void foo(String[] arg1);

機能を増やす=引数を増やす
という思い込みを捨ててください。
あなたが自分の会社の中でお山の大将やっている間にも、世の中は進歩しているんですから。

玄米茶

太郎冠者さんはじめ、フォローしてくださった方々にお礼申し上げます。
ありがとうございました。
最後にAさんとみながわさんに返答させてください。

>Aさん

捨てハンで煽ってくるような人達は基本的に無視しています。
相手のしようがありません。
これ以上増えるようであれば、@IT様に相談しようと思っています。
Aさんはハンドルを固定してコメントしておられるようなので、話ができるのではと思い、問いかけをしました。

Jairoさんがおっしゃるように、誹謗中傷があると思うのであればコメント欄で応酬せず、@IT様へ連絡してください。
私にはコメント欄の削除権も凍結権もありません。
コラム投稿後に私にできることは、一利用者とあんまり変わりがありません。
(相談権があるかな?)

>みながわさん

私の言うデータの持ち回りというのは引数が増えることではなくて、複数の関数間で同じデータをバケツリレーのようにホイホイと渡していくようなイメージですね。
staticメソッドがstaticメソッドを呼び、さらにそれがstaticメソッドを呼ぶ…、なんてことが起きる場合の話です。

特に大規模なソフトウェアをC言語なんかで開発する場合に良く起きることなんですが、関数の細分化が進みすぎると、例えば重要な構造体とかがあっちでもこっちでも必要となって、気がつくと周りの関数全部が同じ構造体を引数にしているなんてことが良くあります。
場合によっては呼びたい関数が必要とするからという理由だけでその構造体を中継するなどという関数も出てきます。

適切なデータの構造化がなされず、巨大な構造体なんかを作ってしまうと、誰でもかれでもその中身を触れてしまって危なくてしょうがないんですね。
構造体の値がおかしい!なんていう障害が発生したら、あっちも見てこっちも見てと大変です。
不具合の発生箇所と原因箇所がコード上離れてしまってる場合が結構あるんです。

オブジェクトのインスタンスであればデータにはアクセス権がつけられますし、データにアクセスするメソッドもクラスに閉じているので割と安心です。
ポリモーフィズムも適切に使えば、不要なメソッドを見せなくてすむかもしれません。
まぁ結局はAさんの言うように「設計が重要だ」に行き着いてしまうかもしれませんが、より危うい方向にいきやすいと言えるかなと思いました。
我ながら細かい話だなーと思うので、これ以上主張するのはやめときますね。

あとコメント見て思ったのは、JavaScriptのところに反論がなかったのが意外でしたね。
「おまえは何もわかっとらん」とか批判を覚悟でちらっと書いてみたのですが、あまりささる話でもなかったんですかね。

@IT自分戦略研究所編集部

いつもご利用ありがとうございます。@IT自分戦略研究所編集部です。
このたび、コラムニストの希望により、新規コメントの受け付けを終了しました。

不適切な内容については、追って対応予定です。
ご理解とご協力のほど、何卒よろしくお願いいたします。

@IT自分戦略研究所編集部

本日21時過ぎ、「コラムと関係のないコメント」及び「事実と異なる内容のコメント」にあたると編集部が判断したコメントを削除いたしました。

▼対象コメント
2012年12月14日 (金) 19:32
2012年12月19日 (水) 09:36
2012年12月19日 (水) 15:04
2012年12月19日 (水) 16:48
2012年12月19日 (水) 16:59
2012年12月19日 (水) 17:10
2012年12月19日 (水) 17:30
2012年12月19日 (水) 18:38
2012年12月19日 (水) 19:10
2012年12月19日 (水) 19:45
2012年12月19日 (水) 21:33
2012年12月19日 (水) 21:56
2012年12月20日 (木) 07:21
2012年12月20日 (木) 08:06
2012年12月20日 (木) 08:42
2012年12月20日 (木) 09:25
2012年12月20日 (木) 19:31
2012年12月20日 (木) 20:20
2012年12月23日 (日) 17:15
2012年12月23日 (日) 20:36
2012年12月24日 (月) 08:33
2012年12月24日 (月) 10:30
2012年12月25日 (火) 09:42
2012年12月25日 (火) 11:48
2012年12月25日 (火) 12:15
2012年12月25日 (火) 13:32
2012年12月25日 (火) 20:14
2012年12月25日 (火) 23:37
2012年12月26日 (水) 00:06
2012年12月26日 (水) 01:03
2012年12月26日 (水) 08:28
2012年12月26日 (水) 12:49

@IT自分戦略研究所編集部

本日16時過ぎ、「コラムと関係のないコメント」及び「事実と異なる内容のコメント」にあたると編集部が判断したコメントを削除いたしました。

▼対象コメント
2012年12月19日 (水) 14:18

コメントを投稿する