2020/07/03週末付近

ほぼいつもの週末ルーチン、コロナ第二波の予感があるので遠出はしない。病院予定なし。ごく近場で買い出しとケーキ。日曜はごそごそやる。レシート、ログ調査等

名付け

Lombokを使うとき、変数numOfPieを外から読むにはgetNumOfPie()を呼び出すことになる。getXXXという名前は固定の形式でツール側で自動生成される。多量のメンバー変数があるとき、書くのも面倒だしソースもむやみに長くなって見通しが悪くなるので助かるのだが、一方で「今まで自分で決めていた変数や関数の名前を、仕組みの側に一方的に決められる」というのもレガシープログラマにとってはつまずいてしまうところだ。

近代のフレームワークは名付けをいろいろ強制する。時期としてJavaのLombok, JPA(findByXXXAndXXX…()),Spring, PHPフレームワークのCakePHPあたり以降のフレームワークには多かれ少なかれあるんじゃないだろうか。知ってる範囲/覚えている範囲で

  • Lombok: getter setterを書かずに済む data→getData(),setData()
  • JPA: 関数を書かなくてもDBのモデルだけから解析して findByData() と書けばDataの検索関数が呼べる(頻繁に利用されているかどうかは知らない。少なくとも案件で使うことになったことはある)
  • CakePHP モデルの名付けで英単語の複数形で書くとリストとして処理され、単数形で書くと単一と見なされるとか。詳細は忘れた。

不便そうに見えてこれは非常に便利な機能である。設計フェーズならまだしもコーディングしているときにいちいち名前を考えるのは実は面倒である。一時的な変数は a とか i とか 使って、後になってなんだっけと思ったりする。取り出すならgetXXX(),設定するならsetXXX()と決め事をしてあればそこを悩まずに済む。

余談だが名前は英文的に長く付けることが多くなった(キャメルケースかスネークケースかは処理系の慣例次第)。もちろんそういう長い変数名/関数名を使えるようになったのはハードがリッチになったことも大きい。若い頃に使ったアセンブラ,FORTRAN,BASIC,初期のプリプロセッサ型C(Introl Cとかも)なども長さは8文字までとか今ではまったく考えられない制約があった(そこでハンガリアン記法なども使っていたな)。

昭和プログラマからすると、このフレームワーク側の名付けの強制については、そういう強制があるということを頭の隅に置いておかないと「この関数/変数はどこにあるのだ」「自分で付けた名前のメソッドがなぜか呼び出されない」「なぜか原因不明のコンパイルエラーが出る。変数名を変えただけで起きたり直ったりする」など理不尽な理由で悩み続けることになる(CakePHPとかも結構悩んだ)。

名付けが強制される(ツールやフレームワーク側で合成される)ということは、一つ書いた名前 がいろんなところで合成されて使い回されるということだ。例えば あるクラスのメンバーに cupOfTea という名前のメンバー変数を作れば、それは getCupOfTeaとかsetCupOfTea とか findByCupOfTea とかソース全体に波及していく。つまり最初の名前に直感的にわかりにくい/区別しにくい名前を付けると、それが後々まで響いてしまう。変数名にうかつにfindNameとか付けてしまうとgetFindNameとなったり、a1,a2 とつけたらfindByA1()となったり、後で悩みまくることになる。今はリファクタリング機能があるIDEが多いので後で直すことも出来ないではないが、仕様書で仕切る多メンバープロジェクトでは一度付けた名前を軽々と直すことも出来ない。公開APIなどもだ(最近のオープンソース系のプロジェクトでは、メジャーバージョンアップのたびにまずいところを根っこから直すところも多いので、そういうところはdeprecatedとか付けて名付けを直すことあるのだろう。直された側は悩むのだが)。

今時の設計者にはソフト開発向けの名付けのセンスがいるのかもしれない(慣れだとは思うが)。ただシステムが巨大になれば名付けも含めて膨れ上がって矛盾し出すのは仕方がないことだ。破綻を遅らせるようなうまい名付けセンスが望まれるとともに、名付けが破綻するくらいに使われ続けたシステムならばそれはそれで大往生とも思える。

最初の名付けの後々の影響が大きいため、近年のプログラムでは誤字(typo)は非常に気を遣うようになっている。上記の名付けロジックでつまらないバグになったり、長く続くプロジェクトで初期の誤字でニヤニヤされたり。IDEの補完入力でもtypoがあるとうまく働かなかったりする。結果、IDEはスペルチェッカーが入っているものがほとんどになってしまった。

スペルチェッカーというとワープロの専売機能(スペルチェッカーライブラリのアプリ組み込み案件とかもやったな)だったのに、ソフト開発に英語のスペルチェッカーが必須になってしまったのは昭和のプログラマには笑い話にも思えてしまう。

Microsoft Office xp

Microsoft Office xpの製品版CDだと思う。表面加工されすぎていてよく見えない。背後の黒いのは撮影用の古いスマホなのでデザインではない。

INFO MAC VII

INFO Mac VII フリーソフト集。今を考えれば隔世の念を抱くもの。

インターネットにつなぐにも相応の課金がかかり時間がかかる時代に、データをインターネットからダウンロードしてまとめてCDに焼いたものを売っていた。今考えると相応にはおいしいビジネスだったのかもしれぬ。

Mac OS X Xcode Tools

珍しいCDばかり出すと普通のCDの写真がはけないので、わりと普通のXcode ToolsのCD.

Appleのハードは買うことがなくても、OSは仕事上いるときがあるので買わざるを得ないし、買ったら付いてくる。ラベルによると10.3の頃らしい。でもこの頃のCDにMacの顔アイコンが付いているとは思わなかった。

lombok

lombok https://projectlombok.org/ の一番分かりやすい機能は @Getter @Setter だろう。

Setter,Getterの話は私はC#の勉強時に知って、ああなるほどと思った。

クラス内のメンバー変数はクラス外で読み書き不要なら他人の処理に壊されないようにprivateにすれば安心である。しかし外から書かれると困るが外から読みたいことは多々ある。そういう変数はprivateに設定した上で読み出し専用のメソッドがあればよいという話。C#はそれを言語の基本レベルで持っている。Javaも意図的にgetData()的に書けば書いていけるが、フィールド数が山ほどになればそれを書くだけで疲れてしまう。

lombokはEclipseに組み込めばアノテーションで@Getterと書くだけで、ソースには見えないgetterのコードが埋め込まれて、別の処理から例えばgetData()と書けば中身を読める。変数そのものはprivateなので外からは壊されない。

@Service
public class AdderService {
  @Getter
  private String data;
}
↓出力classファイルをデコンパイル
@Service
public class AdderService {
  private String data;

  public AdderService() {
  }

// 見えないままに自動生成
  public String getData() {
    return this.data;
  }
}

エディタからは直に見えないgetData()というメソッドを他の場所の処理で書ける。ソースを全文検索してもgetDataは見つからないのに処理が通って、プライベート変数が外から読めて、コンパイラも怒らない。昭和プログラマからすればこれは言われなければ分からない。lombokの設定準備が少し特殊(たしかlombok jarファイルを直接起動してEclipse exeを加工するような操作?)な点も含めて、何回も設定しなおしで悩むことになった。

実現手法としては、コンパイル時に処理に割り込んでアノテーションを検索して処理を追加するみたいな感じなのだろう。細かいところは知らない。Javaの仕様に書かれているのだろう。かなりハックめいた手法である。でも考えてみれば自分のLexyも似た観点でOSをハックして英語メッセージを日本語化している。最初に考えた人もわくわくしながら書いたのだろう。

これが便利なのは確かだった。特にorマッパーなどで多量のフィールドを持つデータを格納するクラスの手間や間違い、ソース量を減らし、保守性をかなり上げる。

ただこの時期の新技術は手間を減らす部分が主ではないと思う(後で思った)。React系の直前までの新技術は「プログラムソースはビジネスロジック(仕様書)の事だけを書く」という進化だったのだと思う。

Autowired

Autowiredによる依存性の注入(昭和のプログラマにはこの言葉がピンとこない)は、ぶっちゃけ

「アプリフレームワークの初期化のときやクラス(=Bean)の初期化のときに、jarの中の@Autowiredのラベルのついている変数を探して、存在したら、nullかどうかチェックして、nullだったら対象のシングルトンインスタンスが作られているかどうかを探して、作られていなかったらそれを先に初期化して、対象インスタンスのポインタをjarの実行中のバイトコードの変数に直に上書きしてしまう」

という話。JavaBeansは初期化時の規則が定まっているので、そういう初期化プロセスまでフレームワーク側ががっつりハックできるという話なのだが、実行時のバイトコード内の変数の位置を実行時に探索して、後付けで値を入れてしまうというのは自分らの世代では「OS以外やっちゃいかんこと」という先入観があるのでどうしても「え? なんで」となってしまう。

実行時にスタックポインタに直書きしてプログラムの飛び先を変える古いウイルスのような挙動とか、デバッガで実行中のアプリを一時停止してメモリを書き換えてプログラムの飛び先を変えるとか、アプリではそれやると反則だよね(そんなコード書いたら袋だたきだよね)というイメージが古いプログラマにはある。でもそれをJavaアノテーション仕様とBeanの仕組みでルール範囲でハックして実現して、現実として「便利だよね」という状況を作ったら認めざるを得ない。すべては「ソースコードをビジネスロジックの仕様記述のみにする」という今風の哲学によるものだからだ。

「nullにいつのまにか値が入っていても、便利だからいいじゃないか。業務ロジックを書いているときに初期化なんて考えたくないし、何より目に邪魔だから見たくない(見えないところでやってくれ)」というコンセプト。

あとは、細部まで追った訳ではないが簡単に調べた部分だけ。

Spring Boot サンプルコード

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class DemoApplication {
  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }

  @Autowired
  private AdderService autowiredAdderService;

  @GetMapping("/hello")
  public String hello(@RequestParam(value = "name", defaultValue = "World") String name) {
    return String.format("Hello %s!", name);
  }
}

デバッグポインタをAdderServiceのコンストラクタに設定してスタックを追う。

DemoApplication 初期化時にAutowiredを探索
autowiredする要素(inject)を登録する
inject要素を作る
存在していなかったらシングルトンインスタンスを作っちゃう

※ @Autowiredによる紐付けは最近は推奨されないそうだ。 http://pppurple.hatenablog.com/entry/2016/12/29/233141 言ってることはわかるので今度Springを使うときには参考にしよう。

MPW Pro

あまりに地味なCDが続いたのでコンテナを取ってきて珍しい目なディスクをとってきた。これはMPW Pro。 個人購入分のはず。

Macintosh Programmers Workbench 旧Mac用の統合開発環境IDEです。今でいうXcode相当。結構味のある開発環境です。DOSやUnixのようなコマンドライン系開発環境なので好き嫌いは結構あるかもですが今考えるとよく出来ている開発環境だったと思います。