第16回 G*ワークショップ(Groovy勉強会)に参加してきました。

大震災の影響もあるのか、前回(JGGUGの勉強会(G*ワークショップ)に初めて参加してきました - 達人プログラマーを目指して)と少し間が開いていますが、昨日、JGGUGさん主催のG*ワークショップというGroovy勉強会に参加してきました。場所は、東京の勉強会ではおなじみの外苑前のオラクル青山センターです。
やはり、今回の注目ポイントは4月に正式リリースされたGroovy1.8とそれに対応した待望の日本語書籍プログラミングGROOVYの出版が間近に迫っているということがあげられますね。
なお、詳しいレポートは、既に、以下でまとめられています。
第16回 G*ワークショップ+JGGUG総会に参加してきた - Diary of absj31
第16回 G*ワークショップ+JGGUG総会に行ってみた - 日々常々
mike、mikeなるままに…: 第16回 G*ワークショップ+JGGUG総会に行ってきました。
また、勉強会中のTwitterのつぶやきのまとめも以下にあります。
2011/06/17_第16回 G*ワークショップ+JGGUG総会 - Togetter
今回の勉強会に参加して、非常によかったと感じたのは、
株式会社メソッドのMANIC活用法-2chや業界での評判まとめ
のサービスを利用して、参加者全員にTwitterのアイコンとIDの入った名刺が配布されたことですね。普段、Twitter上でお話をしている相手でも、初対面で顔がわからないとなかなか話しかけにくいものですが、これがあれば隣に座った人などが一目で識別できます。主催者の方々のお心配りに感謝いたします。今回はこの名刺のおかげで、ミケネコさん(@mike_neck)、きょんさん(id:kyon_mm、@kyon_mm)、irofさん(@irof、id:irof)キムコウさん(@kimukou_26)、スズキさん(id:suzukij、@suzukij)、ちゅうきさん(id:tyuki39、@tyuki39)など、普段Twitterでフォローさせていただいている方々を簡単に見つけることができ、ご挨拶させていただくことができました。今回は時間の関係で参加者同士で自由に歓談する時間が少なかったところはありますが、これは非常によい試みだと思います。(私もそうですが、初対面の人に話しかけるのが苦手な人にとっては助かるアイテムですね。)

2010年度JGGUG総会(山田正樹氏)

まず、最初に山田正樹氏(@yamadamasaki)の司会でJGGUGの総会が滞りなく執り行われました。詳細はid:absj31さんのレポートに詳しく書かれていますが、私としては特に以下に興味がありましたね。

  • 吉田健太郎/ふも(id:fumokmm)さんが新たに執行役人に就任
  • 活動計画として日本Springユーザ会や日本Javaユーザ会と連携を予定
  • 月1回程度のチャットミーティングが必須のサポートスタッフを募集中
  • 個人サポータ制度として任意の金額をJGGUGに寄付できる。(詳しくはsupporters@jggug.orgまで)

特に、他のJava関連のユーザグループとの連携については期待していますし、私も仕事上Java関連で開発をしているという関係上、今後何らかの形で貢献できたらと思いました。Groovyはプログラミング言語そのものの研究というだけでなく、実用技術として、既存のプラットフォームやフレームワークとの親和性を生かしてこそ力を発揮するというところがあると思います。

「レッツゴーデベロッパー2011」レポート(Takuma Watabiki氏)

続いて、 Watabiki氏(id:bikisuke、@bikisuke)より、仙台で先月開催された「レッツゴーデベロッパー2011」というクロスコミュニティイベントの活動報告がありました。クロスコミュニティイベントとは言え、全セッションのうち以下の二つがGroovy関連という、Groovyファンにとっては実に楽しいイベントだったようですね。

これは、もともと計画されていた「G*ワークショップ in 仙台」が、あの大震災の影響で実施できない状態になっていたという事情があったようですね。それにしても、震災であのような大きな被害を受けた東北で多くの困難がありながらも敢えてイベントを計画し、また、そのイベントに多くの開発者が集まって成功裏に終了するとは、本当に熱いものを感じました。自分も日ごろから業界や会社の仕事環境がよくないとか不平を言っているだけではだめですね。こうした活動の話を聞いて、自分もできるところから、少しずつでも行動を起こしていかなくてはいけないとあらためて思いました。

「今日からはじめるGPars」(吉田健太郎/ふも氏)

正直なところ、今回G*ワークショップに参加する最大の目的が、実はリアルで一度ふもさん(id:fumokmm、@fumokmm)にお会いしたいということがあったのですが、今回初めて達成されました。今回はこの勉強会のために名古屋から東京にお越しいただきました。ふもさんは、はてなのスターフレンドとして、GroovyやClojure関連でお世話になっております。残念ながら個人的にじっくりとお話する時間がなかったのですが、想像していたとおり、大変気さくで話しかけやすく、また研究熱心なプログラマーという印象の方でした。
今回は、4月に正式版がリリースされたGroovy1.8に正式に含まれるようになったGParsという並列処理ライブラリーについてのお話でした。GPars(ジーバーズ)の名前はそのまま、Groovy Parallel Systemsの略のようです。*1

The GPars project offers Java developers intuitive and safe ways to handle Java or Groovy tasks concurrently. Leveraging the enormous flexibility of the Groovy programing language and building on proven Java technologies, we aim to make concurrent programming for multi-core hardware intuitive, robust and enjoyable.

GParsプロジェクトはJava開発者に直感的で安全な方法でJavaやGroovyのタスクを並行処理する方法を提供します。Groovy言語の途方もない柔軟性と実績のあるJavaの技術を活用することで、マルチコアハードウェアに対する並行プログラミングを直感的で信頼性があり、そして、楽しいものにします。

実際、講義ではふもさんお得意の「ハロワ」*2の実演を交えながら、

  • 非同期コレクション(並列コレクション)
  • アクター
  • エージェント
  • データフロー

を使ったプログラミングについて説明されました。確かに、概念的にこれらの並行処理の概念をきちんと理解する必要があるのですが、実演で示されるサンプルプログラムは、ハロワの名前にふさわしく、どれも一画面に収まってしまうくらい非常に短いもので、あっけにとられる程簡単なものばかりでした。しかも、1.8からは標準添付なので、別途ライブラリーを追加する手間もいらないのです。
従来、Java言語でこれらの処理を実現しようとしたら、Java並行処理プログラミング ―その「基盤」と「最新API」を究める―などで説明されているjava.util.concurrentなどのライブラリーを駆使してかなりの分量のコードを記述する必要があり、ただでさえ敷居が高い並列処理がますます難しくなるという印象だったのですが、GParsを使えば少なくともコード自体は非常に簡単に記述できるのですね。たとえば、

import static groovyx.gpars.GParsPool.*

def numbers = [1, 2, 3, 4, 5, 6]
def squares = [1, 4, 9, 16, 25, 36]

withPool { 
    assert squares == numbers.collectParallel { it * it }
}

この記述だと、実際あまりにも簡単すぎて何が行われているのかわからない感じですが、CPUのコア数に応じてスレッドのプールが自動的に割り当てられ*3クロージャーで示される各計算処理(上記の例だと二乗)が複数のスレッドで同時に処理されるようになるとのことです。また、これはJSR166、JSR166yなどのJavaのライブラリーに対するDSLとなっているそうですが、信頼性の高いJava言語の基盤の上に使いやすいGroovyのAPIをかぶせたという感じでしょうか。Java言語やJavaのライブラリーをいったん完全に否定するところからスタートするのではなく、伝統的なJavaの資産を活用しつつ、その上で使いやすさを提供するという、もともとのGroovyの思想を並列処理でも利用できるようになるということですね。
並列コレクション以外に、ScalaErlangで有名なアクターを使ったプログラミングやClojureのようなエージェントを使った可変な状態の同期、データフローを使った依存関係による計算順序の並列化の話などがありました。
もちろん、記述は簡単ですが、適切に利用するためには背景となる並列処理に関する知識が必要になると思われます。上記の例でもcollectParallelに渡すクロージャーが純粋な関数となっておらず、副作用があるとおかしな動作になってしまいますし、そもそも、どういった場合に並列化すると高速になるかといったことを実際には考慮する必要があると思います。
従来、並列処理は難しいものとして敬遠していたところがあったのですが、このような使い勝手のよいライブラリーが利用できるのであれば、今後、業務でも真剣に応用することを考えていきたいと思いました。複数のリクエストを同時に複数スレッドで処理するオンラインのアプリケーションとは異なり、バッチプログラムやテストプログラムの高速化などに特に有用かもしれません。
なお、ふもさんを中心として、現在GParsのユーザーガイドの翻訳中とのことでした。(翻訳協力者募集中@fumokmmまで)
講義スライドは以下に上がっています。

「Groovy 1.8の新機能 〜ま、まさか、今までのGroovyが予告だったとでも言うのか…〜」(上原潤二氏)

講義の後半はプログラミングGroovyの著者の一人でもあり、また、groovyservの開発者としても知られる上原さん(id:uehaj、@uehaj)から、たくさんあるGroovy1.8の新機能のうち特に重要なものについての紹介がありました。

  • 文法の拡張
  • AST変換強化
  • クロージャまわりの強化(メモ化による高速化など)
  • 効率向上(int型の最適化など)
  • ライブラリー強化
  • その他

タイトルにあるように、いままでのGroovyの存在が単なる予告編だったのかと思わせるくらい、魅力的な数多くの新機能が実現されており、今回のリリースは「Groovy史上、十指に入る良い出来」で、全機能を説明したら1日がかりになるということをおっしゃっていました。
講義スライドは以下。

これは、LTコーナーの最後にid:nobeans氏によって行われたデモの内容とも重なるのですが、私として、もっとも興味深かった内容はAST(抽象構文木)変換によるソースコードのさらなる簡易化の話ですね。AST変換などと聞くととても難しそうに響くのですが、Groovyのソースコードを解析した結果を途中で加工してしまうテクニックで、実用上はプリプロセッサによるマクロ展開に近い感じで使えるようです。それで、1.8では数多くのAST変換を行うアノテーションが標準で利用可能なのですが、それらを

  • ボイラープレート割り
  • 巨人の肩
  • 言語機能の補修・拡張

といった観点から分類し、非常に上手に説明されていました。ボイラープレート(boilerplate)コードというのは洋書を読んでいると時々出くわす表現ですが、お決まりの鋳型コードを指す用語です。Javaでいえばgetter、setterやtry-catch-finallyなどいたるところで見られます。もともとGroovyではこれらのボイラープレートはJavaに比べてはるかに少なかったのですが、AST変換の活用でさらに削除可能ということですね。巨人の肩は@WithReadLockと@WithWriteLockなどのようにコーディングイディオムを実現するための定型コードを生成します。最後の分類では、もともとJava言語のキーワードとして足りていないような機能を言語機能自体を変更することなく拡張する手段として使えるということを示しています。なお、AST変換については、1.8からGroovyConsoleに標準搭載されているASTブラウザの機能を利用することで、簡単に変換結果を表示できるということも便利ですね。
その他、クロージャーの呼び出し結果をキャッシュして再利用するメモ化やint型による大幅な高速化などGParsとの関連すると思いますが、関数型ライクなプログラミングを強化する機能がいくつか追加されているようです。

その他

その他、今回はオラクルでJavaエバンジェリストをされている、寺田佳央さん(@yoshioterada)が、今後全国各地で開催される予定のJava SE7のLaunchイベントの紹介ということで飛び入り参加されており、個人的にご挨拶する機会がありました。また、懇親会兼LT大会では10人の方々から発表がありました。
平日の夕方の限られた時間でしたが、全体的に非常に密度の濃い充実した勉強会で、有意義な時間を過ごさせていただきました。
他の新しい言語と違い、Groovy言語については、JavaプラットフォームやJava言語を作り直すのではなく、より使いやすく自然に拡張するというところがあると思います。Javaプログラマーにとってはいったん過去の知識を忘れてゼロから出直すという必要がないというところに優しさと親しみを感じますね。ですから、私のようにSI開発の現場でJavaで開発している場合でも、ツールやテストケース作成など、段階的に便利な機能から活用してくことが十分に可能だと思います。自分の会社の周りでは、いまいち認知度や利用実績が低いようですが、
日本でGroovyの知名度がいまいちな理由 - Togetter
もうすぐ、オリジナルの日本語書籍も出版されますし、自分の周りでもファンを徐々に増やしていければと思いました。

*1:一般的にはconcurrent=並行、parallel=並列と訳されるようです。両者は厳密に区別される場合もあるようですが、混同して使われているケースもあるようです。並列と並行の違い - 氷雪の備忘録

*2:もちろん、この場合ハローワークでなくてHello Worldの略。新しい言語やライブラリの実行環境を確認するための最短のプログラムのこと。

*3:デフォルトではコア数+1。もちろん、明示的にプールサイズの指定も可能。

EclipseでJava EE6を使った開発をするときは型フィルター機能が便利

CDIなどJava EE6の新機能を使うときに、ちょっと厄介と感じるのは、複数のパッケージに同名のアノテーションが存在していることです。
大混乱に陥っているJavaEE 6のアノテーションに関する使い分けについて - 達人プログラマーを目指して
うっかり間違えてimportしてしまうと、正しく動作せず混乱のもとになります。
eclipseを使う場合、型フィルターという機能を利用することで、型検索の対象から外すことができ、また、「Ctrl+Shift+O」による自動インポート編成の際にも対象から除外されるため、知っていると便利かもしれません。

この機能はStringUtilsなどクラスパス上に同名のクラスが多数あり、規約で特定のライブラリーのクラスに限定するような場合にも使えると思います。

ConQATを利用してソースコードの品質をチェックする

ある程度プログラマーとして経験を積めば、ソースコードを読んだときに、そのソースコードの良し悪しというものは、嗅覚を使って直感的に嗅ぎ分けることができるものです。実際、そのように体の感覚を使ってこのコードは不吉だと感じるところは実際大いにあり、コードの臭い(code smell)として知られています。
コードの臭い - リファクタリングの必要性を示す兆候
これはファウラーの名著

リファクタリング―プログラムの体質改善テクニック (Object Technology Series)

リファクタリング―プログラムの体質改善テクニック (Object Technology Series)

  • 作者: マーチンファウラー,Martin Fowler,児玉公信,平澤章,友野晶夫,梅沢真史
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2000/05
  • メディア: 単行本
  • 購入: 94人 クリック: 3,091回
  • この商品を含むブログ (312件) を見る
でも紹介されており、こういった不吉な部分を適切に嗅ぎ分けて、リファクタリングしていくスキルというのは、プログラマーにとって欠かすことのできない大切なスキルだと思います。
しかしながら、SI業界の多くの開発組織では、技術者としてPGのスキルや意見を尊重してくれるところは少ないのが現実ですし、ソースコードの品質が感覚的によくないといってもなかなか信じてもらえないところもあると思います。そういうときこそ、客観的、定量的にソースの品質を計測するメトリクスツールの導入を検討するのがよいのではないでしょうか。
メトリクスとは何か | Think IT(シンクイット)
Eclipseで使えるメトリクス計測ツール (4/5):Eclipseで使えるテストツールカタログ(3) - @IT
こうしたツールには本格的な商用のツールから、フリーのものまで最近ではさまざまなものが出回っているようです。フリーのもので、私が今まで利用してきたものを挙げると

などがあります。これらの解析はビルド時に自動的に行って定期的にレポートを生成させることもできます。
ただし、もともとのソースコードの品質があまりにもひどい状態の場合、以上のメトリクスを計測してもあまり面白いデータが採れないということもありますね。恐ろしいことですが、放射線量があまりにも多くてガイガーカウンターの針が振り切れてしまい計測不能という危険な状態のコードもたくさん存在するのです。そういった状態のレガシーコードを相手にするときには、問題の存在を視覚的に表現してくれるようなツールがあると便利です。そういうことで、何かよいツールはないかなと探していたのですが、先日
http://conqat.in.tum.de/index.php/ConQAT
という非常に面白いツールを発見しました。Apache2のオープンソースライセンスで提供されているのですが、

  • コード行数など基本的なメトリクスの計測
  • パッケージ間の依存関係などアーキテクチャの整合性の確認
  • クローン検出(いわゆるコピペコードの検出、ABAP、ADA、C#C/C++CobolJavaVisual Basic、PL1、PL/SQLに対応。それ以外は検出精度が下がるが、テキストとして検出可能。)
  • メトリクス計測結果の視覚化
  • FindBugsなど他のツールとの連携
  • Hudson、Jenkinsなどとの連携

などが行えるだけでなく、計測する処理フローを独自の言語とフローエディタを使って編集したり、新しい処理を独自に開発して追加したり、非常に柔軟性が高く高度なフレームワークとして作成されているようです。Eclipseの拡張として作成されているため、基本的には直感的に操作することができます。以下は、解析フローを編集する画面で、ライブラリーとして提供されているさまざまな処理を組み合わせて、全体の解析処理を組み立てることができます。

このツールの中でも、特にクローン検出の機能は非常に強力です。実際、昨日紹介したサーティファイの認定試験(改良後のもの)Javaプログラミング能力認定試験の問題がかなり改善されていました - 達人プログラマーを目指してに対して、クローン検出の機能を実行すると、以下のようなマップが得られます。

この図で長方形の面積はソースコードのステップ数に比例しており、赤い色の濃いところはクローンペアとして検出された行数の比率を示しています。濃い赤色のところほどコピペされた部分の割合が高いことを示しています。さらに、各クローンペアについては、Eclipseソースコードエディタ上で実際の箇所を確認することができます。

まったく同一の部分だけでなく、似たような部分もクローンとしてきちんと検出されます。
こういうツールは自動ビルドツールと連携させて、定期的にコードレビューを自動化する目的で活用すれば、非常に有用だと思います。

Javaプログラミング能力認定試験の問題がかなり改善されていました

以前、本ブログでJavaプログラミング能力認定試験の1級のサンプルプログラムがあまりにも旧態依然とした設計でひどいという指摘をさせていただきました。
SI業界(日本)のJavaプログラマーにはオブジェクト指向より忍耐力が求められている? - 達人プログラマーを目指して
実際、例題のプログラムがあまりにも理解しにくかったので、そこでは、

日本のSI業界でJava PGとして仕事をするためには、オブジェクト指向的にきれいなあるべき姿でコーディングできるスキルではなく、このようにオブジェクト指向をまったく理解していない上流のSEが作成した異常な設計書に忠実にしたがってコードを書き、また、その複雑なスパゲッティコードを長期にわたってメンテナンスする根性と忍耐が最重要のスキルとして試験で試されているということなのかと私は理解しました。

と書いたのですが、冗談は抜きにして、実際、この例題のようなひどいプログラムが大量に作成されて長いこと保守されているということは、少なくとも私の経験上本当にまぎれもない事実なのです。必ずしも常に最新の技術を知っていることが最重要ではないとは思いますが、最低限オブジェクト指向プログラミングのような基本的な技術は、SI開発の現場で活用し、品質や生産性の向上に役立てていかなくてはならないと思うのです。
プログラマーの成長を考えないSIerの仮説は間違っている - 達人プログラマーを目指して
それで、先日この試験の話題が再びTwitterで少し話題となったため、今どうなっているのかなと確認してみたのですが、素晴らしいことに、いつのまにか全面的にリファクタリングされて書き直されていました。
サンプル問題 - 試験を知る - 個人の方 - Java™プログラミング能力認定試験│資格検定のサーティファイ│あなたのスキルアップを応援します|

  • インターフェースの使い方
  • UIとロジックの分離(ConsoleStatusという表示状態を抽象化した型を継承して、各処理を実装しているところはよくない。表示とロジックの関心事が混在して、変更の発散コードスメルがあります。)
  • 総称型の使い方(RecordListは総称化した方がよい)
  • コピペの存在(DisplayPersonsByNameStatus と DisplayPersonsByTypeStatusなど。ちなみにPersonsの英語は誤りで正しくはPeopleとすべき?)

などにおいて、今後まだまだ、改善の余地はありますが、何よりもまず、わかりやすい英語のクラス名が与えられていますし、可読性の上でも以前とは比較にならないほど向上して、パッと見にはずっとJavaらしい設計になっていました。
以前のバージョンは、

という状態で、何人もの勇者がこの怪物に対して果敢にリファクタリングに挑戦しても、まったく手におえる状態ではなく、あっけなくやられてしまうという状態だったのですが、ここからなら、TDDでリファクタリングすることも容易であると思います。基本的には以前とほぼ同じユースケースを実装しているようですが、コード記述量は圧倒的に改善されています。以前のひどいコードをご覧になった方は比較してみると、プログラムの設計が保守性に与える影響の大きさを知ることができるのではないでしょうか。
私のブログの影響なのかわかりませんが、これほどの短期間で対応していただいた関係者の方々に感謝いたします。

こだわりのある職人プログラマーほど、無駄なコードを少なくしたいものという事実を理解してほしい

ちょっと興味深いエントリが目に留まりました。「プログラミングへのこだわり」を方向づける: 設計者の発言基本的に、この方自身もプログラマーや開発者をされているようですし、他のエントリを読んでも「プログラマーの地位向上をすべき」ということで、私にとっても非常に共感することをおっしゃっているのです。それでも、ちょっとこのエントリの内容については疑問に思うところがあったので、勝手ながら私の意見を書かせていただきたいと思います。

業務システムの生産性や保守性を高めるための基本は「コードを1行でも減らす」である。なぜなら、コーディングとこれにともなうテスティングこそが、開発作業の中でもっとも人手のかかる作業だからだ。個別案件においては、良いコードだろうが悪いコードだろうが少なければ少ないほどよい。

これは、まさにおっしゃる通りですね。もちろん、可読性ということもあるため、厳密には最少のコードが最良というのは言い過ぎですが、基本的には重複がなくロジックが共有化されており、無駄なコードも存在しないというのがよいコードの条件です。このブログで既に紹介してきたように、世界の達人プログラマーと呼ばれる人の書いたどんな書籍を読んでも、基本的にこの考え方は一致しています。誰も保守できない独自のコードを書くべきなどと主張する人はいません。

達人プログラマー―システム開発の職人から名匠への道

達人プログラマー―システム開発の職人から名匠への道

  • 作者: アンドリューハント,デビッドトーマス,Andrew Hunt,David Thomas,村上雅章
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2000/11
  • メディア: 単行本
  • 購入: 42人 クリック: 1,099回
  • この商品を含むブログ (347件) を見る
プログラマが知るべき97のこと

プログラマが知るべき97のこと

リファクタリング―プログラムの体質改善テクニック (Object Technology Series)

リファクタリング―プログラムの体質改善テクニック (Object Technology Series)

  • 作者: マーチンファウラー,Martin Fowler,児玉公信,平澤章,友野晶夫,梅沢真史
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2000/05
  • メディア: 単行本
  • 購入: 94人 クリック: 3,091回
  • この商品を含むブログ (312件) を見る
Clean Code アジャイルソフトウェア達人の技

Clean Code アジャイルソフトウェア達人の技

それで、疑問なのは次の部分です。

いっぽう、そんな方針と明らかにそぐわない人たちがいる。彼らはプログラミングそのものに強いこだわりを持っていて、より良いコードをより多く生み出すことに生きがいを感じる人たちだ。チャーミングな彼らの姿を個別案件の開発現場で見るたびに、私はキャスティングの失敗事例を見る思いがして切なくなる。

優秀な開発者であればより良いコードを多く生み出すのではなく、より良いコードをより効率的に書く、つまり、より少ないコードで価値の高いことを実現することにこだわりを持っているものです。XPで有名なケントベックでもリファクタリングの中の一節で、同一のプログラムの無駄をなくすために何十年もかけて同じプログラムの改善をやっているようなことを書いていたと思いますが、新しい機能を作り出すことよりも、むしろ、同じ機能をいかに簡単に保守しやすい状態に維持するのかという点に最大の情熱を注ぐプログラマーも存在するということを忘れてはならないのではないでしょうか。そして、極力無駄をなくすというこだわりはコードの重複を除去するといったミクロなレベルから、ドメインモデルやサービス、OSSやパッケージの再利用というところにも及びます。*1私自身もどちらかというとクリエイターというより、リファクタリングや再利用、テスト自動化による効率化の方に面白味を覚える方ですね。私がプログラミングで最も楽しいと思える作業 = リファクタリング - 達人プログラマーを目指して

ゆえに私は、プログラマを個別案件に引き留めておこうとする開発手法や技術を嫌悪する。個別案件において生み出すことが求められているものは、「こだわりの手作りプログラム」ではなく「使いやすく保守しやすいシステム」である。

たしかに、特定プロジェクトによらずに横断的に使える基盤やツールを開発することはコピーが容易というソフトウェアの性質を考えれば正しいことです。しかしながら、現在は、インターネットから無料で便利なOSSのツールやフレームワークが入手でき、クラウド上で優秀な業務アプリケーションが調達できる時代です。本当に汎用的に使えるソフトウェアであれば、わざわざ自社の閉じた環境で内製しなくても、外部から優れた製品を簡単に入手できます。たとえば、今時O/Rマッピングや画面を30行単位でページングする仕掛けをわざわざ社内独自のフレームワークとして開発するような時代ではもはやありません。
そうであれば、

  • 汎用的に実行可能なCRUD処理などは汎用パッケージ、OSS FW、クラウドを流用する
  • その会社で本当にコアになる業務ドメインに優秀な開発者を集中させる
  • 既存レガシーシステムの有効活用も含めて、企業全体として情報資産を最大限に活用可能なアーキテクチャや改善プロセスを構築する

のような方向にシフトしていくのが合理的であるように思います。つまり、エンタープライズレベルや個別システムレベルなど様々な階層でアーキテクトという役割*2をもっと重視し、既製品を流用する範囲と独自開発すべき部分を正しく選定し、そのうえでコアとなる業務ドメインに対しては徹底的にむしろこだわりを持ったハイスキルのプログラマーを投入して長期間拡張可能なシステムを構築するという方向に持っていくべきではないでしょうか。そのあたりの話は、以下にも書かれています。

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

つまり、汎用的に幅広く使える便利ツールや共通部品の開発に優秀なプログラマーを投入すべきというのは一般に正しいことではありますが、一方で

  • 本当に汎用的に使える部品なら内製しなくてもOSSやパッケージで既に広まっているはずでは
  • 汎用部品の流用だけで全システム開発をまかなえるということは幻想では(使い捨てシステムなどの例外を除き、一般的な案件では案件固有の問題が残るのでは)

という疑問が湧いてくるのです。
SI業界で私がかかわったお客様の状況を考えると

  • 技術にこだわりを持った技術者を個別案件とは別の基盤チームなどに配置する
  • OSSで入手できるような汎用のツールをわざわざ車輪の再発明のように開発し標準化させる
  • 個別の要件の違いを考慮せず、汎用の標準FWを押し付ける
  • 個別案件は低スキルのプログラマーを安い単価で外注する
  • 結果としてコピペが横行し、リファクタリングや単体試験のない保守不能なシステムができる
  • 大量のコストをかけてダメなシステムを長期間保守する
  • システムに手を加えられないからいつまでも古いバージョンを使い続ける*3

といった悪循環に陥っているケースが本当に多いのではないかと思います。今後は、既製品の流用でなるべく開発量を抑えつつ、独自開発する領域は良いプログラムへのこだわりのある少数精鋭のチームで取り組むという方向にシフトしていくべきなのではないでしょうか。つまり、そういう個別のカスタム案件にこそ優秀な開発者を配置し、無駄なコードのない保守しやすいシステムを構築すべきなのではないかと思います。
この点については、以下もご参照ください。
頭数要員だけでこなせる仕事が今後もずっと取れると考えているSIerの仮説は間違っている - 達人プログラマーを目指して
プログラマーの成長を考えないSIerの仮説は間違っている - 達人プログラマーを目指して
Java EEや.NETはCOBOLやVB6よりも本当に生産性が高いか? - 達人プログラマーを目指して

*1:職人プログラマーというとなんでも自作しないと気が済まない人をイメージするかもしれませんが、少なくともオブジェクト指向の世界では再利用と怠惰は最大のプログラマーの美徳の一つとされています。デザインパターンフレームワーク化といったところはそういうこだわりの表れなのです。

*2:海外だと上級プログラマーがアーキテクトのロールを兼ねるということも多いと考えられるため、両者の境界があいまいなのですが、日本のSI業界だとプログラマーはコードを書き下すPGの意味で使われることが多く、期待される役割がかなり違いますかね。だから、「こだわりのプログラマー」といったとき翻訳書で描かれる海外の達人プログラマーのイメージと、実際に業界の現場で意味する対象が大きく異なる可能性は大いにある。日本でこだわるというと細かいところにこだわって、全体的な効率化を考えない人という意味もあるかもしれないですね。

*3:いまだに新規開発でもJDK1.4などの古い書き方をするシステムが主流という悲しい現実もあるようです。

Clojure言語基本の「き」(その2)

前回Clojure言語基本の「き」 - 達人プログラマーを目指してに引き続き、Clojure言語を新しく覚える際にポイントとなる事柄をまとめていきたいと思います。

逐次実行

(do 式1 式2 ... 式n)の形式を使うことで、処理を逐次実行させることができます。最終的に一番右側の式の結果が全体的な式の結果となります。この方法は副作用の存在を前提としているため、純粋な関数型のプログラムに対する手続き型プログラミングへの抜け道となる危険性がありますが、ClojureからJavaを呼び出す場合などに便利な場合があります。

user=>
(do
  (println "Hello")
  (println "World")
  (+ 1 2))
Hello
World
3

また、doと似た構造は、(後から説明するように)letやwhenなどClojureプログラムのさまざまな場面で暗黙的に表れるという点も重要です。

letによるローカル束縛

前回の記事でdefマクロを使うことで変数が定義できると説明しました。この変数は関数の外部でも参照できるため、グローバル変数のようなものです。一方、特定のスコープの中だけで参照可能なローカル変数的な変数を定義する(ローカル束縛)にはlet特殊形式が使えます。これは(let [バインディング*] 式*)という形になります。

user=>
(let [radius 5, pi 3.141592, area (* pi (* radius radius))]
  (println "半径 =" radius)
  (println "面積 =" area)
  area)

半径 = 5
面積 = 78.5398
78.5398
user=> radius
java.lang.Exception: Unable to resolve symbol: radius in this context (NO_SOURCE_FILE:0)

letの後のベクターの部分で順次radius、pi、areaというローカル束縛を作成することで、円の面積を計算しています。なお、letの最後の式の部分は、doと同様に複数の式を記述でき、最後の式の値がlet式の値となります。なお、letの外側ではローカル束縛を参照することができないため、エラーとなっています。

条件分岐

もちろん、いわゆるif文やswitch文に相当する機能もあります。
まず(if test then else?)の形のif特殊形式があります。以下の例では、式(< number 100)を評価することでパラメーターnumberが100未満なら"yes"、それ以外なら"no"を返す関数を定義しています。(一般的に、booleanを返す述語関数の名前の最後に?をつける慣習に注意してください。)

user=>
(defn is-small? [number]
  (if (< number 100) "yes" "no"))

#'user/is-small?

user=> (is-small? 50)
"yes"
user=> (is-small? 200)
"no"

Javaの場合と比較してif文というよりは3項演算子の方が概念的に近いかもしれません。

public String isSmall(int number) {
    return (number < 100) ? "yes" : "no";
}

javaのif-else文のようにthenやelseの部分で複数の処理を実行させたい場合は明示的にdoを入れ子にして利用する必要があります。
次に、ifと似たものとしてwhenマクロがあります。これは(when test 式*)の形式となります。

user=>
(defn is-small? [number]
  (when (< number 100) (println "number = " number) "yes"))

#'user/is-small?
user=> (is-small? 30)
number =  30
"yes"
user=> (is-small? 100)
nil

ifと違って、こちらはelseが記述できない代わりに、最後の部分が暗黙doによって複数の式が実行できることになります。この他、判定条件が逆転したif-notやwhen-notも時として便利です。
最後に、switchに相当するものとしてcondマクロが使えます。

user=> 
(defn number-size [number]
(cond 
  (< number 50) "small"
  (and (<= 50 number) (< number 100)) "midium"
  :else "large"))

#'user/number-size
user=> (number-size 30)
"small"
user=> (number-size 50)
"midium"
user=> (number-size 100)
"large"

コレクションに対する関数

逐次、分岐の説明が終われば、普通の手続き型の言語ではループを使った繰り返し処理の説明になるのですが、Clojureの場合は強力なコレクション関数が使えるため、低レベルのループ(実際には再帰アルゴリズムを記述するケースは非常にまれになります。中でも、以下の関数が代表的です。

map関数

map関数を使うとコレクションの各要素をパラメータとして適用した結果を各要素とする新しいシーケンスが生成されます。たとえば、与えられた引数を2倍にする以下の関数を考えます。

user=>
(defn double-num [n] (* 2 n))
#'user/double-num
user=> (double-num 3)
6

そして、ベクター[1 2 3 4 5]の各要素を2倍にするには以下のように記述することができます。

user=>(map double-num [1 2 3 4 5])
(2 4 6 8 10)

ここで、map関数は関数型言語において高階関数と呼ばれるものの例になっていて、他の関数自体をパラメーターとして取得して処理している点に注意してください。それから、先の例ではベクターリテラルで数列を渡しましたがこのように連続した数値の列はrange関数を使って生成するのが便利です。以上の例は以下のようにも書けます。

user=>(map double-num (range 1 6))
(2 4 6 8 10)

以上の例ではわざわざdouble-num関数を別途defnで定義して実行していたのですが、JavaScriptと同様にfn特殊形式を使って無名関数を利用することもできます。今回のように単に値を倍にするだけなら以下のように書けます。

user=>(map (fn [n] (* 2 n)) (range 1 6))
(2 4 6 8 10)

(fn [n] (* 2 n))の部分が無名関数となっています。さらに、これはもっと短縮して以下のようにも記述できます。

user=> (map #(* 2 %) (range 1 6))
(2 4 6 8 10)

実際には#(* 2 %)の部分が(fn [n] (* 2 n))に展開されることになります。

reduce関数

mapと並んで重要な関数としてreduce関数があります。これはmapと比べて最初動作が理解しにくいのですが、2つのパラメーターを取る関数fに対して(reduce f coll)とした場合、collの最初の2つの要素に対してfを適用し、次にその結果とcollの3番目の要素にfを適用という順番で順次計算した結果を返します。結果はスカラー値になります。これを理解するには例を見るのが一番で、1から10までの数値を足し算するには以下のように記述できます。

user=> (range 1 11)
(1 2 3 4 5 6 7 8 9 10)
user=>(reduce + (range 1 11))
55
apply関数

前回のエントリでも説明したように実際には+関数は可変長の任意の数値を取ることができるので、実際にはreduceを使って2つずつ順番に足し合わせなくても、任意の個数の数値の合計を算出することができます。

user=> (+ 1 2 3 4 5 6 7 8 9 10)
55

でも、以下のようにするとエラーになってしまいます。

user=> (+ (range 1 11))
java.lang.ClassCastException (NO_SOURCE_FILE:0)

+関数は複数の数値をパラメーターとして取ることはできてもコレクションを取ることはできないからです。この場合、apply関数を使うと、コレクションの各要素を可変長パラメータとして取り出して渡すことができます。

user=> (apply + (range 1 11))
55
filerとremove

filer関数を使うと述語関数として指定した条件を満たす要素のみから構成されるシーケンス*1を作成できます。逆にremove関数を使うと、条件を満たさないシーケンスが生成されます。

user=>(def col (range 1 11))
#'user/col
user=> col
(1 2 3 4 5 6 7 8 9 10)
user=> (filter #(> % 5) col)
(6 7 8 9 10)
user=> (remove #(> % 5) col)
(1 2 3 4 5)

recurとloopによる再帰

前節で説明したコレクションに対する高階関数をうまく使うことで、難しい再帰アルゴリズムを書くケースは減るはずですが、再帰こそがLispの一種であるClojureらしいところでもあります。再帰の詳しい説明は別の機会にすることにしてここではごく基本的な例のみ紹介します。
単純にパラメーターで与えられた数値まで自然数を足し合わせる処理を再帰によって実装するには以下のような関数が定義できます。

user=>
(defn sum-down-from [sum x]
  (if (pos? x)
    (recur (+ sum x) (dec x))
    sum))

#'user/sum-down-from
user=> (sum-down-from 0 10)
55

recur特殊形式を使うと自分の関数を再帰的に呼び出すことができます。以上の例では、xの値が正の間(pos?関数がtrueの間)再帰的にxを一つ減らした値で呼び出され、最後にsumが返されるため、結果として合計が計算されます。なお、素直に考えると以下でも同じような気がしますし、実際これでも同じ結果になります。

user=>
(defn sum-down-from2 [sum x]
  (if (pos? x)
    (sum-down-from2 (+ sum x) (dec x))
    sum))

#'user/sum-down-from2
user=> (sum-down-from2 0 10)
55

以上の例ではrecur特殊形式を使う代わりに普通に自分自身を再帰的に呼び出しています。しかし、内部的な動作は両者で異なり、recurを使った場合は、末尾再帰最適化されるため、大量のスタック領域を使うことを避けられるという違いがあります。

user=> (sum-down-from 0 10000)
50005000
user=> (sum-down-from2 0 10000)
java.lang.StackOverflowError (NO_SOURCE_FILE:0)

なお、再帰する際に関数の先頭に戻りたくない場合、ループする範囲をloop特殊形式で指定することができます。loopはrecurの対象となる点を除くとletとまったく同じ形をしています。以下は階乗を計算しています。

user=>
(defn factorial [n0]
  (loop [fact 1, n n0]
    (if (pos? n)
      (recur (* fact n) (dec n))
      fact )))

#'user/fact
user=> (factorial 3)
6
user=> (factorial 5)
120

loopを使うことで呼び出し側には公開したくないローカルな変数が使えます。

クオート

一般的にはClojureの式は直ちに評価されます。特にシンボルはvarに対する束縛があればvarの値になるし、そうでなければエラーになります。また、(a b c)の形のリストは今での例で見てきたように(特殊形やマクロを除いて)関数呼び出しとして解釈されます。

user=> (def a "test")
#'user/a
user=> a
"test"
user=> b
java.lang.Exception: Unable to resolve symbol: b in this context (NO_SOURCE_FILE:0)
user=> (1 2 3)
java.lang.ClassCastException: java.lang.Integer cannot be cast to clojure.lang.IFn (NO_SOURCE_FILE:0)

ここで、最初のaは束縛されているvarの値"test"として評価されていますが、bは束縛が存在しないためエラーとなっています。最後のエラーは奇妙ですが、1が関数名として解釈されてしまった結果、これが関数型にキャストできないためエラーになっています。このような式の評価を抑制させるためにはquote特殊形式を使うことができます。

user=> (quote a)
a
user=> (quote b)
b
user=> (quote (1 2 3))
(1 2 3)

なお、リーダーマクロという仕組みにより一重引用符「'」を先頭につけるとquoteに展開されるため、同じ意味になります。

user=> 'a
a
user=> 'b
b
user=> '(1 2 3)
(1 2 3)

なお、似て非なる概念としてバックティック記号「`」を使った構文クオートという仕組みがありますがここでは説明しません。

varと名前空間

今までdefマクロを使うことで「変数」が定義できると説明してきました。

user=> (def a 100)
#'user/a

これをもう少し詳しく説明すると、defにより、値100を保持するvarがデフォルトの名前空間であるuserに生成され、それがシンボルaによって束縛されたということになります。*2この場合シンボルaはvarの単純名のように、現在の名前空間にしたがって一旦user/aとして解決されてから評価されます。ですから、以下の結果は同じになります。

user=> a
100
user=> user/a
100

名前空間Javaのパッケージのような感じで、同じ単純名のvarを別々の名前空間に生成できるので、大規模なプログラムを作成する際に名前の競合を避けることができます。現在の名前空間を変更するにはin-ns関数が使用できます。*3

user=> (in-ns test)
nil
test=> a
java.lang.Exception: Unable to resolve symbol: a in this context (NO_SOURCE_FILE:0)

現在の名前空間が変更されると、もう単純名aでもともとのvarを参照することはできなくなります。シンボルaがtest/aとして解決されるようになるためです。そして、以下のように同じ単純名aで別のvarをtest名前空間に作成することができます。この場合、もともとのuserに属していたvarなそのまま残っているため、完全名で参照することもできます。

test=> (def a "TEST")
#'test/a
test=> a
"TEST"
test=> user/a
100
test=> 

完全名が名前空間名/単純名の形式になっていることからもわかるように、名前空間ファイルシステムのフォルダーのようなものと考え、varは値を保持するファイルのようなものでその名前がdefでシンボルと束縛されていると考えると理解しやすいのではないでしょうか。もちろん、名前空間は階層化できます。

test=> (in-ns test/child)
nil
test/child=> (def a "Hello World")
#'test/child/a
test/child=> 

useとrequire

Javaのimportと同じように他の名前空間にあるシンボルを自分の名前空間で単純名で参照させることができます。頻繁に使用する他の名前空間に属しているシンボルを単純名で参照できると便利です。他の名前空間の読み込みに関連する関数としてrequireとuseがあり、最初は両者の使い分けがなかなか頭に入りにくいところがありますが、

という意味になります。以下の実行結果を見てください。(requireやuseで引数に対してクオートを使っている点に注意)

user=> (clojure.contrib.math/round 1.7)
java.lang.ClassNotFoundException: clojure.contrib.math (NO_SOURCE_FILE:0)
user=> (require 'clojure.contrib.math)
nil
user=> (clojure.contrib.math/round 1.7)
2
user=> (round 1.7)
java.lang.Exception: Unable to resolve symbol: round in this context (NO_SOURCE_FILE:14)
user=> (use 'clojure.contrib.math)
nil
user=> (round 1.7)
2

requireをする前は読み込み自体がされていないので、完全名を指定しても実行できません。require実行後は完全名を指定したときのみ実行できます。最後にuseを利用するとround関数自体がuser名前空間で単純名として参照できるようになります。以上のようにuseするとclojure.contrib.mathにある全シンボルが現在の名前空間から参照できるようになってしまうため、以下のように特定のシンボルのみuseすることもできます。

user=> (use '[clojure.contrib.math :only (round)])
nil

なお、REPLで対話的に名前空間を読み込むのではなく大きなプログラムをClojureソースコードとして作成する場合はプログラムの先頭でnsマクロを使って宣言するのが一般的なようです。

(ns test
  (:use [clojure.string :only [capitalize]]))

以下の文献を参考にしました。

The Joy of Clojure: Thinking the Clojure Way

The Joy of Clojure: Thinking the Clojure Way

プログラミングClojure

プログラミングClojure

*1:シーケンスとはベクター、リストなどのコレクションの違いを抽象化した概念

*2:全てのスレッドから参照できるのでルート束縛と呼ばれる。

*3:代わりにnsマクロを使うこともできる。この場合はデフォルトでインポートされるパッケージが異なる。nsマクロはREPLの対話的処理でなく、Clojureのソース中で名前空間を宣言する場合に便利。

JavaEE標準の進化から最近の業務アプリケーション開発手法の変遷について考える

昨日まとめの記事EJBコンテナが分散コンポーネントモデルから軽量なDIコンテナに変化してきた歴史を振り返る - 達人プログラマーを目指してを書いてみて、この10年間でエンタープライズJava開発の生産性がいかに向上してきたということをあらためて思い出しました。しかし、ちょっと考えてみると記述すべきコーディングの分量が単に10分の1になったということだけではなく、開発手法に対する根本的な考え方が大きく10年前と比較して大きく変化しているという質的な変化も重要なのではないかと感じるところもあります。ここでは、Java EEの進化と開発手法の変化(日本の業界ではなく本やネットの情報から推測される世界の)について補足させていただきたいと思います。

ベンダー駆動型の仕様策定プロセスからOSSコミュニティーを中心とした開発者駆動型への変化

10年前は、エンタープライズ開発においてOSSフレームワークというものがそれほど一般的ではありませんでした。LinuxApacheなどのOSやWebサーバーなどは当初からありましたが、(今の状況から考えると信じられないのですが)より上位のフレームワークはごく限られていたのです。そして、J2EEなどの標準バージョンはアプリケーションサーバーなどのミドルウェア製品の開発ベンダーが中心となって策定されてきたという事実があります。仕様が標準化することにより、エンタープライズJava開発の市場が大きくなればより多くの顧客を引き付けることができると考えられたのでしょうか。もちろん、名目上は「複雑な基盤ロジックを標準ミドルウェアが提供することで、ビジネスロジックに集中し、高い開発生産性と保守性、再利用性を確保する」などということが言われていたわけなのですが、実際には開発生産性やコードの単純さということは後回しにされてきたという事実があるのではないでしょうか。実際、without EJBにおいて、「ベンダー駆動型アーキテクチャ」として批判されていますが、分散オブジェクトが本来不要な領域にまで適用することでより多くのハードウェアやライセンス料を稼ぐのが目的となる傾向があるという話が書かれています。そういう話は日本の業界だけでなく、どこの国でもあったのですね。
ただし、日本と海外が違うのは伝統的にSIerに丸投げ体質のところが多い日本のユーザー企業と比べて、システム開発投資に対する費用対効果というものをユーザー側が重視するということはあるのだと思います。OSSなどでより便利な開発手法が発明されれば、積極的に新しい技術を取り入れるし、そういう努力がコストダウンなどにつながって評価されるというところがあるのかもしれません。実際、Java EE5ではDIやORMの考え方が導入されていますし、Java EE6では実際にほとんどの仕様策定者が従来からのIBMやオラクルといったプレイヤーに加えて、Spring(VMWare)、GoogleJBossRed Hat)などOSSのプロダクトに関係する会社によって行われていることを考えれば、仕様策定の主体がベンダーからOSSの開発コミュニティー主体に移ってきているということは誰の目にも明らかなことだと思います。

アジャイルという考え方の普及

J2EEの誕生の時期は、XPやScrumなどのアジャイル開発プロセスが一般に知られるようになった年代とほぼ重なっています。10年前既に外国ではUMLなどのモデリングと繰り返し型の計画を利用したRUPなどの重量開発プロセスが伝統的なウォーターフォール型に加えて浸透し始めていた時代だと思うのですが、アジャイルという考え方はまだ新しくほとんど知られていなかったと思います。そして、アーキテクトがUMLなどを駆使して設計し、ソースコードはエディタなどなるべく手を触れずにツールを使って自動生成するといった考え方が普通で、J2EEの古い仕様がxmlファイルの記述など非常に冗長だったのもこういった思想が反映されたものと考えると納得がいきます。ソースコードをクリーンに保つとか、ソースコードを中心にして他の成果物を補助的なものとして考えるといったアジャイル的発想は一般的ではなかったと思います。実際、without EJB本でも、103ページ目に以下のような記述があります。

In my experience the notion of tiered teams - with God-like architect, backed by an application server, taking care of all these big picture issues while a team of code monkeys knocks out the code without fully understanding its context, doesn't work well in practice.

私の経験上、階層化されたチームの考え方、すなわち、アプリケーションサーバーの能力に支えられた神のようなアーキテクトが全体像のすべてを把握する一方で、脳なしのPGからなるチームが文脈もよくわきまえずひたすらコードを書き下すといったような考え方は、実際にはうまく機能しない。

ただし、現在ではアジャイル開発の有効性は業務アプリケーション開発の世界でも十分に浸透してきているようです。実際、海外のある統計データでは7割以上の開発がアジャイルのプロセスに従って行われているというデータもあるようですし、インドの進んだSI会社ではヨーロッパからのオフショアの大部分の開発がXPやScrumといった手法で行われているとも聞きました。
アジャイル西遊記(SI先進国インドに学ぶ) - Togetter
アジャイル開発はさまざまな解釈がされていますし、特に、ドキュメントを書かないとか、くり返し型といった面が強調されるきらいがありますが、アジャイル憲章の内容を読む限り、成果物として動くソフトウェアの品質を最重視し、そのためにコードの品質を高めることに重点を置くということはXPやScrumなどの手法の違いにかかわらず忘れてはならない点であると思います。
したがって、アジャイル開発の観点からは

といったことが重視されるようになってきていると考えられるのです。
実際以下は一例ですが、クリーンコードを重視するような本が、2000年以降になって海外でたくさん出版されていることもこうした事実を如実に物語っているのではないでしょうか。

Clean Code アジャイルソフトウェア達人の技

Clean Code アジャイルソフトウェア達人の技

実装パターン

実装パターン

基盤技術主体から業務モデル主体への変化

「技術から業務へ」などと書くと私の普段書いていることと矛盾するようですが、ここでは業務モデルとはSOA的なサービスの切り出しや、DDD的なドメインモデリングを中心にした設計を指しています。従来のEJBの仕様を考えると分散オブジェクトや、リソースのプールといった基盤技術的な要素が非常に強調されている一方で、これらの業務的な観点でのアーキテクチャ設計というのは軽視こそされていなくても、非常にやりにくい構造になっていたことは確かでしょう。それは先日のエントリの例を見ても明らかなのですが、本当に実行したい本質的なロジックが無駄な鋳型コード(boilerplateコード)で埋もれてしまう傾向がありました。
当時は、当時でいろいろな工夫があり、SI業界でもっとも一般的なのは

EJBデザインパターン

EJBデザインパターン

で説明されている、EJB Commandパターンを採用するやり方ですね。これは設定の面倒なEJBを単にトランザクション境界設定の目的のためだけに一つだけ定義しておき、実際のロジックはexecute()メソッドを実装する普通のクラスに委譲するといったしかけです。

ただし、この方法は小規模なサンプルアプリケーション程度ではよいのですが、機能が何百も必要になる大規模なアプリケーションだとサービスのような凝集度の高い自然なモジュールの塊に分割できないし、各コマンドも規約で全ロジックをexecute()内に書くなどすると、長大な手続き型のロジックとなり、そこらじゅうにコピペが蔓延するといった傾向に陥りがちです。
一方、最新のJava EE6のPojoを使った開発モデルでは、以下のような設計が可能になります。

  • 大きな機能やデータの集約ごとにインターフェースをサービスとして抽出する
  • 複雑な領域であれば各サービスの実装クラスはドメインモデルを実装したドメイン層に処理を委譲する。


すなわち、業務サービスも普通にPojoで自由に設計して必要ならEJBにし、ドメインモデルもJPAを使えばPojoとしてかなり柔軟に設計することができます。
以前は技術的制約からサービス指向やドメインモデルというものを取り込むことが大変だったのですが、最新バージョンではそういった制約が少なくなっているのです。ただし、この辺りは日本と欧米ではPOJOの意義に対して微妙な解釈の違いがあるような気がする - 達人プログラマーを目指してでも以前書いたのですが、

  • どういう単位でサービスを切り出すか
  • 業務を表現するドメインモデルをどのように設計するか

といったアーキテクト的なスキルがプログラマーに要求されるようになるという点を見逃してはなりません。そして、どうしてSEでなくてプログラマーが設計しなくてはならないかですが、これはプログラミングと設計は本来切り離せないものなのでは - 達人プログラマーを目指してで書いたようにコードを書きながら徐々に理解を深めリファクタリングするような手法で行わないと、適切なサービス分割やドメインモデルといったことには簡単に到達できないからです。
もちろん、コストや納期の問題もありますし、すべてのシステムでこうしたオブジェクト指向設計が必須というわけではありません。食事でも手軽で美味しいインスタント食品を適切に取り入れることが有用なように、クラウドのサービスなどを活用することで使い捨てのシステムなどは簡単に構築できる時代です。しかし、Java EEを使ってカスタム開発するような領域であれば、ユーザーとしては一般には長期的に保守・発展可能なシステムを作りたいはずですし、そのためにはプロフェッショナルなプログラマーが必要とされる時代になってきているということは言えると思います。高価なミドルウェアやハードを調達する一方で、プログラマーのスキルが低いという状態は、高級食材を集めておきながら素人に調理させるようなものであり、食材のコストが活かせずもったいないことになると思います。

まとめ

ここでは、JavaEEの発展の背景について

  • ベンダー中心からOSSコミュニティー中心への主役の変化
  • アジャイル開発手法の普及
  • 基盤技術主体から業務モデル主体への変化(ハイスキルな業務プログラマーの必要性)

という観点から考えてみました。SI業界自体は10年前とあまり変わっていない気もするのですが、少なくとも海外ではプログラミングモデル以上にこうした背景の部分が変わってきているということがあるのではないかと思います。そして、こういうところに、SI業界のリファクタリングのヒントが隠されているのではないかと思います。