Programing」タグアーカイブ

やってることの分だけコードがある、のはフツーのこと。

WEB+DB PRESS vol.127」(以下、wdp127)にリファクタリングの話があったので読んでみたら良かった。

https://gihyo.jp/magazine/wdpress/archive/2022/vol127

知らない単語が出てきたのでメモしておく。

□ 凝集度(cohesion)
□ 結合度(coupling)

調べてみると、語源は1979年あたりにヨードンが提唱していたらしい。

□ DRY(Don’t Repeat Yourself)

コードの最適化、なんでもDRYというルールがあるそうだ。

プログラミングしていると、同じようなコードを何度も書くとき、共通化できるのでは?と考える。あちこちに書いておくと、あとでそこに修正が生じた場合にあちこち直さなくてはならないため不便だし、処理が同じならモジュール化すべきだという考えだ。

wdp127では凝集度について、論理的凝集は避ける、ようなことが書いてある。要はフラグを使った関数内分岐を避けよ、という意味と理解した。ウチでは同じような意味で「多機能関数はダメ」というルールがある。

結合度については値は引数で渡す、ようなことが書いてある。要はグローバル変数は避けよ、という意味と理解した。ウチでは「関数の中はローカル変数だけを使う」と「グローバル的な変数にはゲッターセッターを用意して関数を介して取得する」というルールがある。ただし例外として、画面に一覧を表示する際のフォームオブジェクトはグローバル参照をしていいというルールもある。画面に表示する情報についてゲッターセッターをすべて用意するのはあまりにも面倒だ。フォームオブジェクトは名前付のルールでわかるようにしている。

ウチでは上記のほかに、

・モジュール内の構造は原則2つまで

forやifの構造が3つ以上出現する場合は、さらなるモジュール化を検討せよ、という意味だ。

・処理の分だけコードはある

仕様がケース分岐しているのであればコードも分岐している、のがフツーだ。入り口から分岐させて、中に共通処理を見つけたら共通モジュール化する、のが良い。

・デバッグしやすいコード

できるだけ1行で書く、というコードはダメ。if文の中に関数を記述したり、引数に関数を記述するのはわかりやすいNGだ。デバッグする時にたとえばforループが何回回るのかは重要な要素だ。上手くいかないときは0回の場合が多い。これをデバッグするには繰り返し回数を見れば良いのだが、大体はなんかの関数の戻り値である。これをfor($i;1;myFunc())のように書いてしまうとmyFunc()の戻り値を見たくなる。$count:=myFunc()とか記述して$countの値を見ることになる。それなら最初からそういうコードにしておけば良い。

・プログラマに必要な機能はおそらくユーザにも必要

上記はソース内部の記述だが、表面的な仕様でもデバッグに便利な機能を実装しよう。あるテーブルの中身が見たいような機能は言われなくても作ってしまう。デバッグでよく使うツールであればプログラマは便利になる。そのままリリースして、ユーザに「削除して」と言われてから「隠す」ようにすればよい。

まだ語り尽くせないので、また思いついたら続きを書く。。。

4Dアプリ開発ガイドをv18対応に改訂、キーフィールドが必須に

4Dアプリ開発ガイドを改訂。v15対応(以下、旧ガイドと呼ぶ)をv18対応(以下、新ガイドと呼ぶ)にした。

4Dの仕様変更に従って一部内容を変えた。当時の4DはSQLと同様に「レコードのキーフィールドはなくてもいい」という仕様だった。Display selectionとModify recordでレコードを操作する場合はそれでよかった。しかし旧ガイドは、ハンズオンの目標としてリストボックスをダブルクリックするアプリを作る都合上「キーフィールドは必須」だ。そのためキーフィールドに格納するユニークなレコードIDを生成するためにシリアル番号テーブルを実装していた。SQLではよくやる手だ。

v18ではキーとなるIDフィールドの実装が必須になった。SQLとは異なる仕様だ。新ガイドではSQL的な実装をやめて4Dの機能を使うようにした。IDフィールドには、重複不可の属性をつけて自動入力属性でユニークIDを入れるという実装だ。これによりシリアル番号テーブルは不要になった。旧ガイドで実装していたのはシリアル番号テーブルとログインユーザテーブルの2つ。シリアル番号テーブルがなくなると、実装するのは「ログインユーザ」テーブル1つだけになる。これではガイドとして内容が薄いということで、新ガイドではログイン履歴を記録することにした。履歴の保存先としてアクションテーブルを実装。ログインユーザを表示すると、ユーザ名やパスワードとログイン履歴一覧を表示するアプリとした。

この結果として、新ガイドでは「ログインユーザ1件についてログイン履歴がN件あるリレーショナルデータベース」を実装することになり、1対Nのリレーショナル構造を実装するためのテーブル定義と画面表示について説明することになった。

ハンズオンのステップの多くは旧ガイドから流用した。誤字脱字は訂正し、デスマス調をデアル調に変えた。よく使うキーボードショートカットキーを示したり、コラム欄を追加して、なぜそのような実装にしているかの説明を増やした。附録のデバッグの章には、4Dのデバッガでよく使う機能についての説明を追加した。

VectorScriptのインクルード その2

VectorWorksプログラミング実行環境で使える言語「VectorScript」の話。インクルードしたいファイル名を絶対パスで指定するか、相対パスで指定するか、という話(VectorScriptのインクルード その1)の続きです。

普通C/C++などのコンパイル型言語では、コンパイル時にコンパイラーが理解できる相対パスでインクルードするのが一般的。ビルドしてしまえばあとは一体化しているのでプログラムを動かす時はインクルードパスを意識する必要はない。

VectorScriptでも、絶対パスでインクルードファイルを指定するのは無しだと思う。相対パスじゃないと別のマシンで読み込めない。ではインクルードファイルを参照可能な場所とはどこか。

ここではVectorWorksフォルダ(Vectorworksアプリのあるフォルダ)にしてみた。図面ファイルの横に置くと、図面を別の場所に保存するとき、VectorScriptのインクルードするソースファイルもコピーしなくてはならない。Vectorworksフォルダならすべての図面ファイルから参照可能。

メインとなるソースはたとえば次のようになる。

{$INCLUDE xxx}で4つのソースファイルをインクルード

{$INCLUDE xxx}で4つのソースファイルをインクルード。次の図のようにVectorWorksフォルダに「JCLVS」フォルダを置き、ソースファイルとテキストファイルを置いておく。

Vectorworks 2015フォルダに「JCLVS」フォルダ

たとえばダイアログを表示する「My_dlg_2Edit1Cbx.pas」の中身は次。

2つの入力フィールドと1つのチェックボックスを指定するためのダイアログボックスを表示するためのプログラムだ。メインのソースの、{$INCLUDE JCLVS\My_dlg_2Edit1Cbx.pas}のところに「My_dlg_2Edit1Cbx.pas」の中身(231行)が展開されて、構文チェックされる。メインプログラムは飛躍的に読みやすくなった。

メインプログラムの13行目、filenameに「JCLVS\default.txt」を代入してダイアログの関数に渡しいてる。外部ファイル「My_dlg_2Edit1Cbx.pas」のソースから引数でファイル名を渡された「JCLVS\default.txt」を読み込むことができていることにも注目してほしい。

このようにVectorWorksフォルダにソースファイルの一部を置いておくことでメインプログラムの可読性が高まる。メインプログラムを作っている時にうっかりサブプログラムに影響を与えることが減って生産性が高まる。変数のスコープ(有効範囲)を意識して、変数名・関数名にネーミングルールを設けた方がいい。これはソースを分割しない場合でも。

ソースが展開されるだけなので、サブプログラムからメインプログラムの変数は参照できるし、インクルードしているサブファイルが2つ以上あって、両方に同じ関数が定義されているとエラーになるので注意が必要だ。C言語の分割ソースとは異なる。

上記の場合で、たとえばMy_Lib.pasの中で共通ライブラリ「JCL_str.pas」の文字列操作関数を使いたいとする。My_Lib.pasに{$INCLUDE JCLVS\JCL_str.pas}を記述すれば、JCL_strを使えるようになる。が、メインプログラムでも{$INCLUDE JCLVS\JCL_str.pas}が記述されていて二重インクルードのエラーになる。この問題についてはまた次回。