移動: トップ > ラボ > プログラミングのアイディア > 関連するコードを色付けして表示
履歴:
統計:
目次:
このページの最終更新日: 2012/05/29
提案日: 2005/12/27

関連するコードを色付けして表示

概要

概要文章
○ 短い背景+をするかの短い説明(どちらにも一番分かりやすく短い具体例を使う、まとめではない)
○ 「~するといったアイディアです。」の形で終わる
○ これによって現在よりもよくなること

ある1つの機能を果たすコードが、飛び飛びに分散してしまうと、後からコードを見たときに、コードの意味が分かりにくくなってしまいます。そこで、飛び飛びの場所に挿入したコードに対して、を付けたりコメントを付加することで、分散したコードそれぞれにマークを残しておき、その関連性をコードに記録しておく、といったアイディアです。

コードに色を付けると、コードの間にある関連性がひと目で分かるようになります。その機能に関連する処理がくっきり浮き上がって見え、コードが理解しやすくなります。もし、機能を拡張しようとしたときも、スムーズに拡張を始められます。

背景

飛び飛びの場所にコードを挿入することがある

背景文章
○ これが出てきた過程、これを思いつくまでの経緯、世の中の必要性などを書く。
○ 「そこで、~する、といったアイディアに至りました。」の形で終わる。
○簡単な言葉で書ける必要なことだけを短く書く。詳細な背景は後で書く。

プログラムを書いてソフトを作る中で、ある1つの機能を追加するために、よく飛び飛びの場所コードを挿入しなくてはならないことがあります。例えば、動作のログを保存する機能を追加する場合、いろいろなタイミングでいろいろなデータを保存するために、それらを取得するコードを飛び飛びの場所に挿入しなくてはいけません。

このように、同じ目的で追加するコードが、飛び飛びに分散してしまうと、後からコードを見たときに、コードの意味が分かりにくくなってしまいます。そこで、飛び飛びの場所に挿入したコードに対して、を付けたりコメントを付加することで、分散したコードそれぞれにマークを残しておき、その関連性をコードに記録しておく、といったアイディアに至りました。

機能を追加/拡張するときに、よく飛び飛びの場所にコードを挿入する

どんなもの?

分散したコードに色を付ける

方法文章
○「背景」からの流れを使わず、ここから読んでも分かるように新しく文章を書き始める。
○はじめに筆者が想像するその具体的な場面状況をまず読者に伝える。「~ときに/~の場合を考えます」のようなフレーズを使う。
○状況設定の後、そのアイディアがどんなものなのかを一言(最も短い例)で書く。 「~といったアイディアです。」のようなフレーズを使う。限定しすぎたり不足があっても後の文章で修正できる。
○一言説明の後、アイディアの説明を1つの例に限定してそのシナリオ全部を書く。
取り残した部分、利点性質後で書く。

プログラムを書く中で、ある1つの機能を追加するために、飛び飛びの場所コードを挿入することがあります。このような飛び飛びの場所に分散したコードに対して、を付けたりコメントを付加することで、分散したコードそれぞれにマークを残しておき、その関連性をコードに記録しておく、といったアイディアです。

例えば、動作のログを保存する機能を追加する場合を考えます。いろいろなタイミングでいろいろなデータを保存するために、飛び飛びの場所にコードを挿入しなくてはいけません。コードの例を次に挙げます。コードは、単に、ログ機能のコードが分散するイメージを表すもので、処理内容に意味はありません。

    void Func1(Class1 a, Class2 b) {

        var log = "";
        for (var i = 0; i < 10; i++) {
            var x = a.GetSomething(i);
            var y = b.GetSomething(i);
            if (x > y) {
                a.SetObject(b);
                log += string.Format("Set b to a in {0}th.\n", i);
            }
            log += string.Format("{0}, {1} obtained in {2}th.\n", x, y, i);
        }
        this.Log += log;
    }
ログ機能のコードが分散するイメージ (C#)

ログ機能のコードが分散して、メインで行う処理の中に埋もれています。このように、2つの機能が交じり合ったコードでは、コードが読みにくくなります。そこで、ログ機能のコードに色を付けると、次のようになります。

    void Func1(Class1 a, Class2 b) {

        var log = "";
        for (var i = 0; i < 10; i++) {
            var x = a.GetSomething(i);
            var y = b.GetSomething(i);
            if (x > y) {
                a.SetObject(b);
                log += string.Format("Set b to a in {0}th.\n", i);
            }
            log += string.Format("{0}, {1} obtained in {2}th.\n", x, y, i);
        }
        this.Log += log;
    }
分散したログ機能のコードに色を付ける

このようにコードに色を付けると、コードの間にある関連性がひと目で分かるようになります。ログ機能の処理がくっきり浮き上がって見え、コードが理解しやすくなります。もし、ログ機能を拡張しようとしたときも、どの行がログ機能なのかがすぐ分かり、スムーズに拡張を始められます。

エディタソフトが色を付ける機能を持つ

コードを書くときに使うエディタソフトは、コードに色付けする機能を新たに持つ必要があります。エディタソフトがコードに任意の色を付けられるように対応して、どの行に何色を色付けしたのかを含むデータもソースファイルに保存します。

色付けしたコードに、常に色が付いていると目立ちすぎる場合があります。前述の例では、ログ機能のコードが常に黄色で表示されますが、ログ機能はメインの処理ではないので、あまり目立ってほしくありません。そこで、下の図のように、色付けした行にカーソルが来たときだけ色を付けて、カーソルがないときは色を付けないように表示する機能を、エディタソフトに追加します。このとき、どの行が色付けされているかが分かるように、色付け状態を行番号に灰色で表示します。色付けされている行は行番号を灰色にします。

色付けした行にカーソルが来たときだけ色を付けるエディタソフトのイメージ [全体]

さらに、色付けしたコードを表示せずに隠したい場合があります。前述の例で言えば、ログ機能のコードは隠して、メイン処理のコードに集中したい場合です。そこで、下の図のように、色付けしたコードを1行に折りたたんだり、元に展開する機能を、エディタソフトに追加します。よくある関数の折りたたみ/展開表示のように、色付けコード部分も1かたまりとして折りたたみ/展開できるようにします。このとき、同じ色で色付けされたコードは、そのすべての行を同時に折りたたみ/展開して、関連のあるコードを一気に表示/非表示と切り替えます。

同じ色で色付けされたコード部分を折りたたみ/展開するエディタソフトのイメージ [全体]

背景で挙げたログの例を使って具体的に様子を見せる。
常に色が付いていると目立つなら、その行にカーソルが来たときだけ動的に色を付ける。変更履歴の色線にもう1線追加して、色づけされているかどうかを灰色で表示。関数の折りたたみ/展開のように、色づけブロックも非表示にできて、同じ色のブロックは同時に全て折りたたみ/展開する。コードを書くときには、色づけされた上に書くと、そのアスペクトに関連するコードとして保存。

色付け機能のないエディタで代用するには

前述のような任意のコードに色を付けられるエディタソフトは、世の中にほとんどありません。そこで、通常のテキストエディタで代用するには、色付けしたいコードの末尾に、コメントで「// [Aspect=A1]」のようにマークを付けます。前述のログ機能のコード例に対して、色を付ける代わりにコメントでマークを付けると、次のようになります。

    void Func1(Class1 a, Class2 b) {

        var log = ""; // [Aspect=A1]
        for (var i = 0; i < 10; i++) {
            var x = a.GetSomething(i);
            var y = b.GetSomething(i);
            if (x > y) {
                a.SetObject(b);
                log += string.Format("Set b to a in {0}th.\n", i); // [Aspect=A1]
            }
            log += string.Format("{0}, {1} obtained in {2}th.\n", x, y, i); // [Aspect=A1]
        }
        this.Log += log; // [Aspect=A1]
    }
色付けしたいコードの末尾に、コメントで「// [Aspect=A1]」のようにマークを付ける

同じログ機能のコードには、[Aspect=A1]のように同じ識別名A1を付けるようにします。この識別名A1は、何色でコードを色付けするかと同じ役割を果たします。

もしエディタソフトに、フォルダ内のソースファイル全てに対して、文字列を検索できる機能があれば、文字列「[Aspect=A1]」を全て検索して、結果をリストに列挙します。すると、分散したコードがリスト一覧で見られるので、どこにどんなコードが分散しているのかを一箇所で見ることが出来ます。

Visual Studioには、下の図のような「フォルダを指定して検索」する機能があります。これを使って[Aspect=A1]を検索することで、分散したコードをリスト一覧で見られます。また、リストの項目をダブルクリックすれば、該当するコード行が表示されます。

Visual Studioにある全ソースファイル検索機能 [拡大]

通常のテキストエディタで代用するには、コメントで// [Aspect=A1]のようにマークを付ける。IDEの全検索機能で[Aspect=A1]を検索、全て列挙させることで分散したコード行が一覧で見れる。A1が識別子になる。

同じ色を付けた部分は1つのアスペクトになる

本記事で紹介したアイディアは、同じ機能のコードが各行へ分散してしまう場合は、それらのコード行に同じ色を付けて、その関連性をコードに記録しておく、といったものでした。このように色付けしたくなるのは、そこに自分が気付いている何らかの同一の関連性があって、それらが各場所へ分散してしまうからです。たった今は気付いているのに、少し経てば完全に忘れてしまうのを恐れているからです。

これは、アスペクト指向で言われるアスペクト(横断的関心事)と同じ発想に結びつきます。関連性をコードに記録する目的で、コードに1種類の色を付けることは、そこに1つのアスペクトがあると言えます。複数の色を使ってコードに色を付けたなら、そこには色の数だけ複数のアスペクトがあることを示しています。

本記事で提案するアイディアは、同じアスペクトのコードブロックに色を付けて、そのアスペクトをコードに記録するもの、と言うことが出来ます。

このような色づけ部分は1つのアスペクトになる。同じアスペクトのコードブロック同士を色付けして表示。コードに色が付けられて、これを編集でき、色付け情報を保存できるエディタソフト。

色を付けると効果のある例

コードに色を付けて関連性を記録しておくと効果のあるケースを以下に挙げます。

動作のログを保存する機能
  前述のログ機能の例のように、動作のログを保存するコードは、メイン処理の色々な場所に分散しやすい性質があります。そのため、ログ機能のコードに色を付けて関連性を記録しておきます。もしくは、「// [Aspect=LOG]」のコメントを付けておきます。これによって、ログ機能とメイン処理のコードが混ざり合うのを防いで、メイン処理の開発に集中できるようになります。
デバッグのために一時的に追加するコード
  デバッグのために、今だけ一時的に追加するデバッグコードは、メイン処理の色々な場所に分散しやすい性質があります。そのため、デバッグコードに色を付けて関連性を記録しておきます。もしくは、「// [Debug]」のコメントを付けておきます。これによって、デバッグコードがメイン処理に埋もれてしまうのを防いで、後からデバッグコードを忘れず除去できるようになります。
進捗(%)を表示する機能
  処理が何%進んだかという進捗を表示する機能は、次の3つ、変数定義のコードと、表示タイミングをカウントして進捗表示のイベントを発行するコードと、処理が完了して100%進捗表示のイベントを発行するコード、に分散する性質があります。そのため、進捗表示機能のコードに色を付けて関連性を記録しておきます。もしくは、「// [Aspect=PROG]」のコメントを付けておきます。これによって、進捗表示機能とメイン処理のコードが混ざり合うのを防いで、メイン処理の開発に集中できるようになります。
モード遷移に関わるコード
  モード遷移に関わるコードは、何かと分散しやすい性質があります。例えば、あるモードで値を初期化させたい場合、次の3つ、変数定義のコードと、そのモードに遷移した直後の初期値を代入するコードと、モード終了時に値を解放するコード、にコードが分散する性質があります。そのため、初期データの保持に関わるコードに色を付けて関連性を記録しておきます。もしくは、「// [Aspect=INIT1]」のようなコメントを付けておきます。これによって、初期化を行う機能の範囲がはっきりして、後からコードを拡張しやすくなります。
キャッシュの機能
  一度計算した値を保存しておいて、2度目からは保存値を使う、といったキャッシュの機能は、次の3つ、変数定義と、2度目から保存値を使うキャッシュ動作本体のコードと、保存値をクリアして再計算するコード、の3つにコードが分散する性質があります。そのため、キャッシュ機能のコードに色を付けて関連性を記録しておきます。もしくは、「// [Aspect=CACHE1]」のようなコメントを付けておきます。これによって、これによって、キャッシュ機能とメイン処理のコードが混ざり合うのを防いで、メイン処理の開発に集中できるようになります。

アスペクト=横断的関心事の類に入るようなコードの分散例であれば、本記事で提案するコードに色を付けるアイディアが効果を発揮します。また、機能を追加/拡張するときに、よく飛び飛びの場所にコードを挿入するので、このような場面でコードの色付けが便利です。

利用して効果があった例を併載。
+ 背景で挙げたログの例→// [Aspect=LOG]でマーク。
+ デバッグのために今だけ追加したデバッグコードが散乱する→// [Debug=A1]でマーク。

+ 進捗%を表示する例→変数定義、表示タイミングのカウントと進捗表示イベント発行、処理完了イベント発行、の3つにコードが分散→// [Aspect=PROG]でマーク。
+ モード遷移で、あるモードでは初期データを保持したい場合→変数定義と、モード遷移直後の代入と、モード終了時の解放、の3つにコードが分散→// [Aspect=M1]でマーク。
+ 連続した描画処理の末尾にRedrawを1回だけ呼ぶ例→自動で最後の判定は難しい
+ キャッシュの例→変数宣言、キャッシュの動作本体、キャッシュの更新通知、の3つにコードが分散
一般に言われる横断的アスペクトの類に入るものに言える。機能を追加/拡張するときに、よく飛び飛びの場所にコードを挿入するので、このときに便利。追加/拡張したという意味がコードに残る。

色付け部分はリファクタリングで消える

本記事のアイディアでは、コードに色を付けることで、その関連性を記録しました。しかし、コードに色を付けるということは、逆に言えば、その周りでコードが分散している、ということを表しています。色を付けた部分には、2つ以上の機能が混在しています。

このような傾向は、プログラムの観点から見ると、決して良いものではありません。2つ以上の機能が混ざっていると、コードが煩雑で読みにくくなり、あまり良い状態ではありません

このように、色が付いたコードの周りは、複数の機能が混在して、あまり良い状態ではないので、このコードをリファクタリングします。すると、混在していた複数の機能がきれいに分離されて、色を付けて見分ける必要がなくなり、色が消えてしまうことが多くあります。リファクタリングによって、混在していた複数の機能が、クラスなどに分離されてまとまり、コードの分散がなくなって、色を付けなくてよくなります。

そのため、コードの色付けは、次に行うリファクタリングまでのつなぎとして考えます。機能が分散したコードには一時的に色を付けて、簡易的にその関係性を記録しておき、次のリファクタリングのときに、記録した色に基づいてコードを分離して整理します。リファクタリングはそれほど頻繁に行わないので、それまでの間コードに色を付けることで、分散した機能の関連性を記録する役割を果たします。コードの色付けは、次に行うリファクタリングのための道しるべとなります。

このようなマークを付けるということは、即ち、その部分に2つ以上の機能が存在する/混在している、ということを表している。2つ以上の機能が混ざっていると、コードが煩雑になるので、マークを付けた部分は、いずれのリファクタリングによって、消えてしまう(別のクラスなどへ分離してまとまる、コード自体が分離されてマークしなくてよくなる)ことが多い。一時的にマークを付けて関係を覚えさせておき、後の構成整理のときに、1つにまとまってマークする必要がなくなることが多い。

コードに実行順序の制約があるためコードが分散する

本記事では、分散したコードに色を付けてその関連性を記録しましたが、コードが分散しなければ、色付けする必要はありません。しかし、実際には、コードが分散することが多くあります。そこで、なぜコードが分散するのか、その理由について考えます。

なぜ1つの機能がコードのあちこちに分散するのかを考えると、その機能が動作するには、どうしてもこの行にこのコードを入れないといけない、ということの繰り返しでコードが分散します。これは、その行のタイミングで何かを実行させたい、という実行順序が存在するために発生します。機能に実行順序の制約があるために、その制約に適したタイミングの場所がコード全体の中で飛び飛びに分布して、それらの場所にコードを挿入するので、コードが分散します。

なぜ1つにまとまらずにコードのあちこちに分散するのか?→その行にコードを入れないといけないのは、そのタイミングで実行させたいという実行順序があるから。コードに実行順序の制約があるから、飛び飛びにコードを追加する。→コードの他に実行順序制約を定義できれば、1つの場所にまとめて書ける。

2011/04/01現在、世の中に似た機能はない。

 

更新履歴
2011/03/22
  • 書き始め
2011/04/25 v1
  • 初版作成

※ご意見、ご感想、改善点、その他の情報などがありましたら、メールにてお知らせ願います。

Copyright (C) 2002 - 2019 Simon.P.G. All Rights Reserved. Top | Simon.P.G. とは | 使用条件 | ご意見
inserted by FC2 system