スパゲティプログラム
スパゲティプログラム(英: spaghetti program)またはスパゲティコード(英: spaghetti code)とは、コンピュータプログラムの状態を指すための表現(俗語)であり、命令の実行順が複雑に入り組んでいたり、遠く離れた関連性の薄そうなコード間で共通の変数が使われていたりするなど、処理の流れや構造が把握しにくい見通しの悪い状態になっているプログラムのことである[1]。スパゲッティプログラム、スパゲッティコードとも表記される。
概要
編集スパゲティプログラムというのは、規律のない不用意なジャンプの多用によって命令実行の順番が複雑に入り組んでいたり、プログラムの状態を管理するための変数が遠く離れた場所(プログラム全体のあちこち)で読み書きされていたりと、まさにスパゲティがこんがらがったような状態になったプログラムのことである。
スパゲティプログラムは、プログラムのテストを実施したり、内部動作解析やデバッグのためにプログラムをステップ実行によりトレースしたりすることが困難になる。結果として、プログラムのスムーズな開発や完成を妨げる。またソフトウェアを改良したり拡張したりすることも困難にする。
サブルーチンやクラスを最初に実装したときは、整然とした読みやすいコードになっていたとしても、機能追加や仕様変更に対応するためにコードを修正し、状態を管理・保持するための変数や条件分岐などが増えていくにつれて、徐々に読みやすさが失なわれ、設計も陳腐化していき、気付いたときにはスパゲティコードになっていることもある。
スパゲティプログラムの要因
編集goto文の濫用
編集スパゲティプログラムを作り出す原因としてよく挙げられるのが、goto文の濫用である。(構造化以前の)BASICなどの言語にあるgoto文は、機械語やアセンブリ言語のアドレス指定ジャンプ命令に近い特性を持ち、無条件に指定した行番号の位置までジャンプする。これはサブルーチン(関数)やループなどの制御構文を利用した制御に比べ、処理の(素直な、上から下への)流れを混乱させる。
グローバル変数の安直な使用
編集現代のプログラミングでは、できる限りグローバル変数の使用は控え、ローカル変数を優先的に使用するべきだとされている。グローバル変数はサブルーチンを超えてアクセス可能であり、公開宣言すればプログラムのどこからでもアクセスできるようになり、また寿命も長い(プロセスと同じ生存期間を持つ)ため、大規模で複雑なプログラムになるにつれて管理が難しくなり、事故を起こす可能性が高くなる。グローバル変数は、変数の定義位置と、変数が実際に読み書きされる箇所が遠く離れがちであり、気付かないうちに内容が書き換えられてしまう可能性もある。また、グローバル変数はプロセス内の複数のスレッドで共有される資源であり、複数のスレッドから同時にアクセスされる可能性がある場合は、アトミック操作や排他制御を適切に記述しなければならない。グローバル変数をむやみに多用すると、容易にスパゲティコードとなる[2]。
継承の濫用
編集オブジェクト指向を取り入れたプログラミング言語においても、継承を機能追加のために濫用し、クラス間の関係が複雑になりすぎてしまうことでスパゲティ化が起こることがある。継承はgoto文と同じくらいプログラムを分かりにくくする要因であるという意見もある[3]。『Effective C++』や『Effective Java』のような書籍では、機能の追加には継承よりもオブジェクトコンポジション(合成)を利用することがベストプラクティスとして推奨されている。
不適切なマルチスレッドプログラミング
編集並行処理や並列処理のために複数のスレッドを使用するマルチスレッドプログラミングでは、処理の流れが1つではなく、同時に並行動作する複数のスレッドが互いに協調し合う必要があり、不用意にスレッドを使うとスパゲティ化を招きやすい。マルチスレッドによる非同期処理は、従来とはまったく別の意味でのスパゲティコードをもたらす[4]。
マルチスレッドプログラミングを簡略化し、コードの信頼性を向上するために、構造化並行性 (structured concurrency) と呼ばれる概念についても議論されている[5]。
コールバックの多用
編集コールバックを多用するプログラム、特にイベント駆動型プログラミングもスパゲティコードを招きやすい。例えばGUIアプリケーションは常にユーザー操作に対する応答ができるようになっていることが重要であり、イベントループを持ちユーザー応答をつかさどるメインスレッドでネットワーク通信やストレージI/Oなどの長時間かかる可能性のある処理をその場で同期的に実行するとアプリケーションが応答停止(フリーズ)することがある。そのため、いったん他のスレッドやプロセスに実際の処理を任せるようにリクエストを発行した後、処理結果をコールバック関数の引数などの形で非同期的に受け取るようなイベント通知スタイルを採用する必要があるが、その結果を受けて次に実行する処理をさらにコールバック関数で記述して……といったように、非同期処理のコードは一連の流れを把握しづらいスパゲティスタックとなりやすい[6]。この問題を緩和するために、Future パターンに対応したライブラリや、それを発展させたasync/await構文をサポートする言語なども登場している。
動的結合の濫用
編集プログラムのカスタマイズポイントを提供するコールバック関数、オブジェクト指向のポリモーフィズム(多態性)を実現する仮想関数、ダック・タイピングに使われるリフレクションのような動的結合(動的ディスパッチまたは動的バインディング)は、アルゴリズムの再利用性向上のために有用な機能だが、実行時でなければ実体の特定(名前解決)ができず、統合開発環境の機能を使っても呼び出し構造や依存関係を直接追跡できないため、濫用するとスパゲティコードを招きやすい[7]。関数オーバーロードや演算子オーバーロード、C++のテンプレートのような静的結合であっても、テンプレートメタプログラミングなどで濫用するとスパゲティコードを招きやすくなることもある。
脚注
編集注釈
編集出典
編集- ^ スパゲッティコード(スパゲッティプログラム)とは - 意味をわかりやすく - IT用語辞典 e-Words
- ^ アンチパターンってなに? | Think IT(シンクイット)
- ^ Opinion -- 川俣 晶:ソフト開発を成功させる1つの方法 - @IT
- ^ Lecture 4: IPC & Threads / CSE 120: Principles of Operating Systems | Alex C. Snoeren, カリフォルニア大学サンディエゴ校
- ^ JEP 428: javaマルチスレッドプログラミングを容易にする構造化並行性
- ^ Windows with C++ - The Pursuit of Efficient and Composable Asynchronous Systems | Microsoft Learn
- ^ まずコードの可読性を最適化しよう | POSTD