コンテンツにスキップ

英文维基 | 中文维基 | 日文维基 | 草榴社区

利用者:紅い目の女の子/スパゲティプログラム

{{複数の問題 |出典の明記=2009年6月 |言葉を濁さない=2014年2月 | }} スパゲティプログラム(Spaghetti code)またはスパゲティコードとは、プログラムソースコードがそれを制作したプログラマ以外にとって解読困難である事を表す俗語。名称の由来は、皿に盛られたスパゲッティのようにロジックが絡み合っていることから。パスタとも呼ばれる。

プログラミングがワイヤラッピングで行われていた時代のスパゲティコード(1977年)

概要

[編集]
スパゲティプログラムは、第三者に対する見栄えを欠き、また当座の実用性を満たしても、後にバグが出現する可能性が高い

実務において、特にクライアントからの受注や業務委託に求められるコーディングは他者が解読可能な形態、すなわちコードの整序やリファクタリングされたプログラムコードを求められる。しかし、コード共有範囲の狭いプロジェクトや趣味で行うプログラミングの場合、第三者に対する可読性を追求しない場合がある。

プログラムのテストを行う際、プログラムの実行順序をトレース(追跡)するのに苦労する。それゆえ、バグが発生してもなかなか原因を見つけ出せなくなる。

スパゲティプログラムは保守・機能追加を妨げるため、修正されることが望ましい。しかし、「動作しているプログラムは修正しない」ことを習慣とする文化では、修正は避けられる傾向にあった。これは、見通しの悪いスパゲッティ状態のプログラムを変更することにより予期せぬバグを追加してしまったり、「寝たバグを起こす」可能性が高かったからである。時間や予算・人材が許す場合でも、この傾向は見られた。

後にテストファーストの方法論が確立され、プログラム本体の完成と同時期にテストプログラムも作成されるようになると、プログラム変更の危険性は相対的に低くなり、このような状態のプログラムは積極的に修正することが奨励されるようになった(リファクタリング)。

なおウェブサイトからダウンロードしたJavascriptファイルでスペースや改行なしで変数名も極端に短く、まるで可読性を欠いたようなコードが配布されていることがあるが、これはダウンロードの高速化を目的としたミニファイ(圧縮)処理やソースコードを解析しづらくする難読化を行うプログラムからの生成ファイルであり、元のソースコードがそのようになっているわけではないのでこれをスパゲティコードと呼ぶことはない。

スパゲティプログラムの特徴

[編集]
ソースが複雑に絡み合ったスパゲティ・ナポリタン

goto文の濫用

[編集]

スパゲッティプログラムを作りだす原因としてよく挙げられるのが、goto文を多用することである[1]BASICなどの言語にあるgoto文は、無条件に指定したラベルの位置までジャンプする。これは関数ループなどの制御構文を利用した制御に比べ、処理の流れを追いにくく、ソースコード可読性およびメンテナンス性を低下させてしまう。近代的なプログラミング言語では、例外処理や多重ループを抜けるための制御文が用意されているため、ほとんどgoto文は使われない。

しかしgoto文や分岐先の直接指定が適切、または行わざるを得ない場合もある。

  • マシン語などによる制御系プログラミング
  • 1980年代のBASICにおけるgoto文にはラベル機能が無く、行番号しか指定できない。
  • C言語等でエラーを処理する場合、goto文を使うことでエラー処理を書きやすくなることがある。

多重継承の濫用

[編集]

オブジェクト指向を取り入れたプログラミング言語において、多重継承を機能追加のためにむやみに濫用し、クラス間の関係が複雑になりすぎてしまうことでスパゲティ化が起こることがある。このため(メンバの名前の衝突や菱形継承を避けるなどの目的もあり)、DelphiJavaC#などの言語では多重継承が禁止され、機能の追加にはオブジェクトコンポジションを利用することが推奨されている。

スコープの拡大

[編集]

グローバル変数の濫用によりスコープが拡大され、可読性が悪くなることがある。プログラミング言語によってはグローバル変数が言語仕様に無いものも存在する。

技術不足

[編集]

コーディング技術の不足による要因もある。同一関数内に処理を延々と書き連ねたり、一貫性のない記述を繰り返したりすることにより可読性が低くなることがある。

スパゲティプログラムの奨励事例

[編集]
小容量なパッケージに無理やり詰め込むことで、半ば必然的にスパゲティと化す

通常スパゲティプログラムが奨励される事は無い。しかし、小容量なパッケージに無理やり詰め込んだり、芸術点を稼ぐなどの理由で、プログラムのスパゲティ化が推奨されるような環境もある。

「奨励」と言うほどではなくとも、特に2000年代以前のシステムでは、限られたコンピュータ資源を最大限利用するために、スパゲティ化を「許容」することがしばしばあった。コンピュータ資源が豊富な現代においては、スパゲティ化することで速度や容量が多少稼げるとしても、第一に可読性が優先される。

例えば変数名を省略するなど、「無駄を省く」ことで、プログラマーの技術の有無にかかわらず、プログラムは半ば必然的にスパゲティと化す。わずか数バイト、あるいは数msの無駄を省く目的で、スパゲティプログラムを奨励する形になっていないか、自ら良く問いただすべきである。

小容量化

[編集]

同じ処理を行うのに、複数のコーディング方法が存在する場合がある。例えば、Javaにおいてboolean型変数「hoge」の真偽判定を行う場合、「if (hoge)」と書いても、「if (hoge == true)」と書いても、動作は全く同じである。前者の書き方は無駄を省いて容量が削減できるが、後者の書き方は可読性に優れるため、どちらが推奨されているということはなく、両方の書き方ができるようになっている。人によってはどちらかを推奨している場合があるが、上にあげた例のように、それほど大した違いが無い場合は、個人の好みの問題が大きい。とはいえ、少なくとも計算機資源が豊富な環境においては、可読性の著しく低い手法を推奨する人はいない。

ただし、メモリの容量や、通信量の制限が厳しいような環境においては、ソースの可読性よりもプログラム全体の容量削減を重視したコーディングスタイルが推奨される場合がある。数バイトでも削減したいような環境においては、小容量なパッケージに無理やり詰め込むため、可読性は無視され、スパゲティと化す。

ある程度は可読性の高いコードで書いた後、プリプロセッサに通して容量の少ないプログラムに変換する(例えば変数名を1文字に変換するなど)という手法もある。

なお、2010年代以降において、わずか数バイトをケチりたいようなシステムにおいてJavaを使うことはあまりないが、2000年代の携帯電話アプリ業界ではこれが普通であり、例えばドコモのiアプリにおけるJARファイルのファイルサイズは10Kb以下(2002年当時)と言う制限があった。プリプロセッサによる多少の支援を考慮に入れても、キャラグラも含めてファミコンの初代『スーパーマリオブラザーズ』の1/4と言う厳しい容量制限でプログラムを動かしており、必然的にソースの可読性は無視された。

高速化

[編集]

プログラムの高速化のため、CPUのバグを突くようなトリッキーなプログラミングが推奨されるような環境があり、これがプログラムのスパゲティ化を招く。

例えばファミコンApple IIなどのゲームにおいては、65C02の仕様書に記載されていない未定義命令(Undocumented instruction、本来使ってはいけない)を利用することが常套化していた。特にファミコン後期においては、競合機や次世代機の登場によってファミコンのゲームに対する性能への要求が強くなり、またキャラグラの増大によってプログラムの小容量化に対する要求も強くなって、本来してはいけないはずのコーディングが必然的に奨励され、プログラムのスパゲティ化を招いた。

ファミコン用ゲーム『ファイナルファンタジーIII』の飛行艇の高速移動のプログラミングに関して、既に退職した当時のメインプログラマのナーシャ・ジベリ以外誰も理解できず、そのため当時の人気ゲームでありながら後継機への移植が難航したという逸話がある。

芸術

[編集]

例えば、五・七・五・七・七の三十一バイト(みそひとバイト) でプログラムを記述する「アセンブラ短歌」など、実用性よりも芸術性が重視されるようなプログラムが存在する。スパゲティプログラムは、一般的には「美しいプログラム」とはみなされないが、一方で「スパゲティである方が美しい」「小容量なパッケージに無理やり詰め込んだ方が芸術的」という価値観も存在する。

例えば、雑誌MSX・FANの投稿プログラムコーナー『ファンダム』で事実上のスパゲティプログラム奨励が行われていた。特に「1画面プログラム」すなわち行番号を含めた40×24文字のショートプログラムを多数採用・掲載しており、可読性よりソースの圧縮性を評価していた。

よく行われていた可読性の逸脱行為には以下がある。

  • 1行の文字数を40の倍数以内に近い数に収める
  • コメント、コメント行、(アポストロフィのみの)空行を書かない
  • 空白文字やタブを書かない (MSX-BASICが逐次解釈である事を利用している)
  • 行内の分岐に関わるIF文の使用は極力避ける (if文を終了するには行を終了するしか方法が無かった為)
  • NEXTの対象変数を書かない

例として、BASICにおけるトリガー入力待機ルーチンは通常

10 IF STRIG(0)=0 THEN GOTO 10

と記述するのに対し、1画面プログラム採用作品では

10 FORI=0TO1:I=-STRIG(0):NEXT

という記述が用いられていた。

中には「マシン語ソースをアスキーコード制御文字列を含まない範囲の8ビット値にシフトさせ、そのキャラクターコード群とデコーダのみ記述する」という可読性が全く無いプログラムが採用される事もあった。

その他の例

[編集]

「*」をコントローラーで左右に動かすプログラムを一般的な記法で書いたもの。
条件分岐させ水平座標、Xが 0未満、28を超えないように加減算する。

10 CLS
20 LET X=14
30 LOCATE X,10:PRINT " ";
40 LET S=STICK(0)
50 IF S=3 THEN X=X+1: IF X>28 THEN X=28
60 IF S=7 THEN X=X-1: IF X<0 THEN X=0
70 LOCATE X,10:PRINT "*";
80 GOTO 30

以下はよく用いられていた圧縮方法で書かれたソース。

1 CLS:X=14
2 LOCATE X,10:PRINT" ";:S=STICK(0):X=X-(S=3)*(X<28)+(S=7)*(X>0):LOCATE X,10:PRINT"*";:GOTO 2

メイン部分を1行にまとめるために、条件分岐を省いている。
水平座標の変数 Xのはみ出しをチェックする関係式の評価を数値として扱い、1個の算術代入文としている。
しかしMSX BASICでは式の評価が 真ならば-1、偽ならば0 が返る為、一見すると変数 Xへの加算と減算が逆に見えてしまう。

脚注

[編集]
  1. ^ 小林 修 (1993-06-15). “本格派スパゲッティ・プログラムの勧め”. 情報処理 34 (6): 795-798. http://id.nii.ac.jp/1001/00004275/ 2021年3月29日閲覧。. 

関連項目

[編集]

{{デフォルトソート:すはけていふろくらむ}} [[Category:プログラミング]] [[Category:プログラミング言語の構文]] [[Category:ソフトウェア工学のフォークロア]]