スパゲティプログラム

出典: フリー百科事典『ウィキペディア(Wikipedia)』

: spaghetti program: spaghetti code使[1]


(1977)

[]


辿1

[ 1]

1980[?]BASIC使

[ 2][ 3]使

OSAPI

[2]

[?]


[]




JavaScript

[]

goto[]


gotoBASICgoto

BASICBASICgotoifgoto使returnGOSUBgotoBASICBASICGOTOBASICBASICVisual Basic for Applications (VBA) Visual Basic .NET (VB.NET) 2023使

PascalCgotoBASIC使[ 4]

C++Javabreakgotogoto使C#gotogotoreturn[3]

goto使使

Cgoto使[ 5]

使使

使[]


使使寿[6]

使JavaC#C/C++

Singleton 使

C/C++1[7]

使使

[8][9]使

[]


C++使DelphiJavaC#使goto[10]Effective C++Effective Java

[]


使1調使[11]

 (structured concurrency) [12]

[]


GUII/O[13]Future async/await

[]


使使[14]C++

しかたなくスパゲティプログラムにしてしまった事例[編集]

現在ではスパゲティプログラムは絶対に書いてはいけないとされている。現在ではメモリ量はふんだんにあり、可読性が重要で、メンテナンスやコードの改変が容易で、システムが安定して動作することが重要だということが理解されているからである。

だが特に1980年代ころまでの、まだ構造化されていないプログラミング言語が横行していていた時代で、構造化前のプログラミング言語しか動かないマシンで限られたメモリにプログラムをおさめなければならなかった状況下では、しかたなくGoto文が使われることがあった。数バイトでも削減したいような、追い詰められた開発環境では、無理やり詰め込むため、現代から見れば「あり得ない」「許容されない」ような、粗野なプログラミング手法ですら使われた。

一部のシステムでは、CPUのバグを突くようなトリッキーなプログラミング手法がしかたなく選ばれていたものがあり、ソースコードやアセンブリコードを普通の読み方で読んだだけではとても理解できないようなジャンプが起き、まさにプログラムのスパゲティ化を招いた。例えばファミコンApple IIなどのゲームにおいては、65C02の仕様書に記載されていない未定義命令(Undocumented instruction、本来使ってはいけない)を利用することが常套化していた。特にファミコン後期においては、競合機や次世代機の登場によってファミコンのゲームに対する性能への要求が強くなり、また視覚的キャラクタに割り当てられるデータ量・メモリ量の増大によってプログラムのほうに割り当てられるバイト数が減ってしまい、本来してはいけないはずのコーディング手法が、ますます横行するようになってしまった。たとえばファミコン用ゲーム『ファイナルファンタジーIII』の飛行艇の高速移動のプログラミングに関しては、既に退職した当時のメインプログラマのナーシャ・ジベリ以外誰も理解できず、そのため当時の人気ゲームでありながら後継機への移植が難航したという逸話がある。[要出典]

スパゲティプログラムが許容、放置された具体例[編集]

1980年代のメモリ量が限られていた時代には、プログラムのバイト数を圧縮する技を競い合う誌上コンテンストなどが開催されていて、そこではスパゲティプログラムが許容された。例えば、雑誌MSX・FANの投稿プログラムコーナー『ファンダム』で事実上のスパゲティプログラムが許容されてしまっていた。特に「1画面プログラム」すなわち行番号を含めた40×24文字のショートプログラムを多数採用・掲載しており、行から行へと、標準的ではない手法でジャンプをさせるようなコードが横行した。

たとえば次のようなものである。

  • 行内の分岐に関わる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への加算と減算が逆に見えてしまう。

その他

また、五・七・五・七・七の三十一バイト(みそひとバイト) でプログラムを記述するという曲芸的ジャンル「アセンブラ短歌」を考案した人も昔はおり、こうした作品でも31バイトの中でプログラムを完結させるためにスパゲティ状のジャンプを内部で行うものが多々あった。

スパゲティプログラムを修正する方法[編集]

スパゲティプログラムは保守や機能追加を妨げるので、できることなら修正することが望ましい。しかし実務で使われているシステムは「スパゲティプログラムを修正した場合のメリットとデメリット」「修正せず、そのまま放置する場合のメリットとデメリット」を天秤にかけて、「とりあえずうまく動作しているプログラムは、滅多なことでは修正しない」ということが広く行われている。

各種の汎用オペレーティングシステム、ソフトウェア開発ツール、金融機関の基幹システム、産業用機械の制御ソフトウェア、業務用アプリケーションなど、実務で日々使用されているシステムでは安定性が非常に重要であり、不用意にコードを修正してうっかりシステムの安定性を損ねるとシステムに依存した業務が停止してしまい、ユーザーに多大な迷惑をかけるだけでなく、不具合によって発生した金銭的な損失に対して補償しなければならなかったり、果てはユーザーを失なったりする事態に陥るからである。

またスパゲティプログラムを修正するとしても、十分な解析やテストをせずにうかつに修正してしまうと、かえって既存機能や動作の互換性を損なったり、別のバグを追加してしまったり、修正されていたはずのバグを復活[注釈 6]させてしまったりする可能性が高いからである。時間や予算・人材が許す場合でも、この傾向は見られた。スパゲティプログラムを修正するとしても、コードが入り組んでいて解読や分割・分離が難しいことから、しばしば小手先の作業だけでは困難で、相当に大掛かりな作業になることが多い。

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

なおあまりに酷い状態に陥っているスパゲティプログラムは、修正するのではなく、思い切って放棄してしまって、新たにゼロから整然と構造化したプログラムを書いたほうがよほど早い、ということもある。

脚注[編集]

注釈[編集]

  1. ^ 1命令や1行だけを実行させ、命令ごとあるいは行ごとの状態が正常かどうかひとつひとつ確認すること。
  2. ^ 大規模プロジェクトでは命名規則がコーディング規約で整備されていることが多いが、その命名規則に従っていない一貫性のないコードは可読性の低いプログラムになりやすい。そもそも命名規則自体が現代的なコーディングスタイルに則しておらず不適切であることもある。
  3. ^ 1つの変数に複数の意味・役割を持たせて使いまわしすると、変数名も不適切・あいまいになりやすく、コードの可読性やメンテナンス性が低下する。
  4. ^ ただしCには大域ジャンプを可能とするsetjmp()longjmp()も用意されていた。
  5. ^ C++やObject Pascalにはデストラクタがあり、C#やJavaではusing文[4]やtry-finally文やtry-with-resources文[5]が使えるため、確実なリソース解放のためにgoto文やラベル付きbreak文などを使用する必要はない。
  6. ^ 「寝たバグを起こす」「寝ているバグを起こす」とも形容される。

出典[編集]

関連項目[編集]