JJUG CCC 2014 Springに参加してきました

本日、日本Javaユーザーグループ(JJUG)主催のCCC 2014 SpringというJavaの勉強会に行ってきました。会場は、ベルサール西新宿で、都営大江戸線都庁前のA5出口を出て、新宿中央公園の5分くらい歩いたところにありました。今はスマートフォンで地図を確認しながら行けるので、初めての場所でも方向音痴の私でも電車の駅さえ間違わなければ大丈夫ですね。

CCCというのはCross Community Conferenceの略で、さまざまなコミュニティーの交流の場となる会議という趣旨でしょうか?このCCCというイベントは2012から開催されているようなのですが(CCC | 日本Javaユーザーグループ)、今回初めて参加させていただきました。残念ながら個人的な都合から、基調講演と午後の前半のセッションのみで後半と懇親会には参加できませんでしたが、参加したセッションについてまとめます。その他のセッションについては、以下に情報があります。

K-1 詳説 Java SE 8 – CCC Edition(櫻庭 祐一氏)

最初のセッションは、最先端のJava技術を研究し、最近Java8紹介記事も多く執筆されている櫻庭さん(@skrb)より、Java SE8の新機能のポイントについて紹介していただきました。Java SE8というと、まず、Lambdaプロジェクトの成果であるラムダ式やストリームAPIを使った新しい関数型のJavaプログラミングが注目されますが、それ以外にも、

など、その他の新機能についても解説されました。途中、

「for文やforEachを使ったら負け。ラムダ式禁止と戦おう」

というコメントが印象的でした。現状は後の岩崎氏の講演で説明があったように、アプリケーションサーバーなどの対応が進んでいないという制約から、Java8が実際の案件で普及するまでにはにはもうしばらく時間がかかるだろうとは思いますが、今のうちに新しい機能について先行して学習しておき、実際使えるチャンスがあったら、便利な新機能を積極的に導入できるよう準備しておきたいですね。

なお、この基調講演の元ネタは、「現在、ITpro において連載している詳説 Java SE 8 を CCC で再現します。」とのことですので、
詳解 Java SE 8 第1回 Java SE 8概説
からはじまる、素晴らしい詳細な連載記事も是非ご覧ください。

K-2 Java 8 ラムダ式と Stream の概要について(Stuart Marks氏)

基調講演の2番目は、オラクルのStuart Marks氏より、ラムダ式、ストリームAPIについて、基本的なところから解説していただきました。いきなり、新しい関数型の記述方法で説明されると、慣れていない場合混乱する場合もあると思いますが、以前の書き方と対応させながら丁寧に解説していただき、理解しやすかったです。
今回の講義で特に印象的だったのは、関数型や不変性という話を前提にせず、あくまでも従来からある無名内部クラスの代替として型安全にコードブロックを渡すための便利な記法、内部イテレーターの記述手段としてラムダ式を説明していたことです。例として

List<Person> list = ... ;

// Java7 
for (Iterator<Person> iter = list.iterator(); iter.hasNext();) {
    Person p = iter.next();
    if("Jones".equals(p.getName())) {
        iter.remove();
    }
}

// Java8
list.removeIf(p -> "Jones".equals(p.getName()));

のように、Collectionインターフェースに追加されている、removeIfを使って、従来のイテレーターの処理を置き換えるコードが出てきました。さらに、従来の外部イテレータとfor文を使って処理した場合と比較して、同期化が必要なコレクションの場合に、きちんと同期化をしてくれるというようなところも便利ですね。*2

先の櫻庭さんの発言にもありますし、今まで私自身ラムダ式を使う以上、なるべく関数型の発想で考えるべきであり、可変な(mutable)な操作は避けるべきと考えていました。しかしながら、もともと、Javaは可変な変数を当たり前に使う手続き型(imperative)なコードを前提としており、不変性などは後から追加されたというところがありますし、このようにラムダ式は可変な操作にも使えるのだというのはかえって新鮮な発見でした。特に、古いコードをラムダ式を使って簡潔にリファクタリングするという場合などには、あえて不変性にこだわらないというのも一つの考え方かもしれません。

Stuart氏の話で、もう一つ印象的だったのは大量の基本型の数値を使った計算に、ストリームAPIを適用するという以下の例*3です。

System.out.println(
    LongStream.range(0, 1_000_000L)
        .parallel()
        .map(i -> ((i & 1) == 0 ? 1 : -1 * (2 * i + 1))
        .mapToDouble(i -> 4.0 / i)
        .sum());

従来の常識としては、大量の数値をListなどのコレクションを使って計算すると、基本型からオブジェクへの変換(boxing)が行われて性能で問題が出るので、代わりにlong[]のように基本型の配列を使わなくてはならないということがありました。しかし、これはコレクションなど便利なAPIが使えずC言語のようなコードを書かないといけないということを意味し、パラレル化などの最適化には相当の努力が必要でした。ストリームAPIでは、LongStreamやmapToDoubleといった基本型専用のAPIをきちんと使いさえすれば、遅延評価がきちんと行われ、実際に大量の値のboxingが発生せず、オーバーヘッドを最小限に抑えることが可能なようです。確かに、Javaでは基本型の扱いはユーザーにとって完全に透過というわけにはいかず注意が必要なのですが、こうした数値計算にも関数型を活用できるというのは面白いですね。

R1-1 Java 8 for Java EE 7/6(岩崎 浩文氏)

金融、製造、公共など様々なドメインでJavaEEを使った多くのシステム開発を担当してこられ、雑誌等への執筆もされている楽天の岩崎氏(@HirofumiIwasaki)より、アプリケーションサーバーのJava8対応の状況についてお話いただきました。
JavaSE 8が正式にリリースされたということで、早速現在のプロジェクトで活用しようという気になってしまいますが、実際のところは、JavaEE 7ではJavaSE 8はサポート対象外であり、さらに、そもそもJavaEE 7に対応したアプリケーションサーバーがGlassfishやWildFlyといったオープンソースのサーバーに限られ、その他の商用のサーバーではいまだにJavaEE6しかサポートされていないという事実があります。ただし、講義中で紹介されていたようにGlassfishのdaily build版ではJava8が使えるということですし、WildFlyなど他のサーバーについても比較的早く対応されるのではないかという期待がありますね。

また、パラレルコレクションなどは通常のEJBのようなオンライントランザクション系のシステムとは相性が悪いと思います。しかし、EJBがJavaEEのすべてではないですし、JavaSE 8が利用できるようになれば、日付APIやラムダを使ったロジックの簡潔化など、いろいろなメリットがあるものと期待されます。

なお、日本語の情報が少ないという問題はありますが、JavaEEについては以下のNetBeansのチュートリアルにあるサンプルに取り組むのがよいとのことです。
https://netbeans.org/kb/trails/java-ee.html

H-2 Javaトラブルに備えよう(上妻 宜人氏)

上妻氏は見習いプログラミング日記でJava EEを中心とした情報の発信をされていますが、単にプログラミングを中心とした開発手法のみならず、運用時に必要なモニタリングツールの知識など有用な情報を書かれています。どうしても、新しいAPIや言語の機能、開発ツールなどに興味がいきがちですが、実際には運用時のトラブルシューティングをするためのログ解析手法などは非常に大切です。また、必要なツールを正しく使いこなせば、トラブルシューティングが容易であるというのもJavaの重要な特徴の一つなのではないかと思います。つまり、Javaは単にプログラミング言語というだけではく、デプロイメント・運用時の環境まで含めたプラットフォームなのであるということですね。

講義の中では、トラブルシューティングのために適切な情報の取得が大切であるとして、まず、以下の情報出力の方法について説明されました。

  • GCログの出力設定
  • クラスヒストグラムの出力
  • ヒープダンプの出力と解析
  • スレッドダンプの出力

さらに、OutOfMemoryErrorが発生するさまざまなケースを突出型、じわじわ型などパターンごとに解析する手法について解説されました。基本的な手法は昔から大きく変わっていないとはいえ、具体的なコマンドの使い方はバージョンによって異なっているため、上妻氏の最近の経験に基づいた情報は非常に有用なものであると思います。いざ、トラブルが発生すると焦ってしまい、冷静な判断ができないということもあると思いますので、Javaのエンジニアとしては日頃からこうした解析の手法に慣れておくことは大切だと思いました。*4

H-3 初めての Java EE 開発から学んだこと(菊田 洋一氏)

株式会社 構造計画研究所の菊田氏(@kikutaro_)より、ご自身が初めてJava EEの開発に取り組まれたプロジェクトの経験をもとにお話されました。菊田氏は以下のブログでもJava EEの情報を発信しておられます。Challenge Java EE !

恥ずかしながら、講演の中で私のブログもちょっと紹介していただいたのですが、3年近く前(もうそんなに経つんですね)にちょっとJavaEE 6について調べてこのブログでも紹介したことがあった(Java EE6標準の範囲でフルスタックのWebアプリケーションが簡単に作成できることを確かめてみました。 - 達人プログラマーを目指して)ので、昔のことを思い出してちょっと懐かしくなりました。

残念ながら、私自身はその後JavaEEから離れてしまい、新しいEEのフレームワークを実際の案件で使うことはできなかったのですが、菊田氏の発表はJavaEEを実際に適用した成果に基づくものとして、大変興味深く聴かせていただきました。
発表にもあるように、もともと.NET系の開発からJavaEEに移行されて、まだほんの一年半という短い期間にも関わらず、データ層からプレゼン層まで「フルスタック」のアーキテクチャを構築されたということは大変感銘を受けました。これはもちろん菊田氏の努力の賜物だと思いますが、JavaEEが標準のフレームワークを組み合わせるだけで実用的に使える、.NET並みに簡単に使えるようになったということの証明となる事例なのではないかと思います。

  • 標準以外のフレームワークやツールがたくさんあって選択にまようけど、いろいろ悩むのは楽しい
  • 日本語の書籍が少ない
  • Javaはコミュニティーが盛んで初心者にも間口が広い

といったことは、私も以前から感じていたことで、多くの点で大変共感しました。菊田さんご本人の以下のエントリーもどうぞ
#JJUG_CCC Spring 2014で「初めての Java EE 開発から学んだこと」というタイトルで発表させて頂きました! - Challenge Java EE !

*1:時間の関係で残念ながらほとんどスキップされましたが、櫻庭さんはJavaFXを推進されており、日頃プレゼン資料自体もJavaFXで作成しているとのことです。http://www.slideshare.net/skrb/20140321-javafx8も参考になります。

*2:Coolectionsのようなユーティリティクラスと違い、インターフェースに定義されたデフォルトメソッドはオーバーライド可能なので、サブクラスごとに独自の実装が持てる。

*3:円周率を計算するテイラー級数の計算

*4:DevOpsという考え方があるように、最近はプログラマーも開発だけでなく、サーバーの運用やトラブルシュートをすることが多いと思われます。

開発チームにアーキテクトがいないなと感じてしまうような、残念なコードスメルの例

まったく個人的なモチベーションの問題から、前回の最終更新から2年以上が経過してしまい、多くの読者のみなさんにはご心配をおかけいたしました。「プログラミングに関して調べたことや日々感じたことをメモとして残していきたいと思います。」というもともとの原点に立ち返って、あまり気負わずに、また今後も時々更新していけたらと思います。今までこのブログの主なテーマとして、JavaEEやSpringといったような、いわゆる業務開発で使われるような技術を中心としてきたわけですが、最近Springを使ったJavaの開発に(アーキテクトではなく)プログラマーとしてちょっと参加する機会があったので、その時気づいたこと、感じたことを書いてみたいと思います。

さて、皆さんはアーキテクチャやアーキテクトという言葉に対してはどのようなものをイメージするでしょうか。システムのセキュリティを確保するための方式であったり、大量のデータを短時間に処理する並列化の仕組みであったり、また、効率的な業務フローの構築だったり、ソフトウェアアーキテクチャという言葉はコンテキストによって実に様々な対象を意味します。実際には、ソフトウェアアーキテクトといってもさまざまな仕事があり、また、それぞれの分野において膨大な知識が必要なため、最近ではお医者さんのように専門の得意分野に特化せざるを得ないということもあるかもしれません。基本的には、アーキテクトはプロジェクトの成功を左右するような基本的な設計方針、技術の選択といったことに関わってきます。

ここでは特に、クラスやパッケージ、設定ファイルの分割といった開発作業に深くかかわってくる、プログラミングの基本指針のことを単にアーキテクチャと呼ぶことにします。自分の経験からも、このプログラミングに関わるアーキテクチャはプログラマーの開発生産性や、後々のコードの再利用性、保守性といった点に深くかかわってくる開発の最重要ポイントの一つであり、他のアーキテクチャ指針とともにプロジェクト成功のためには軽視できないものであると考えています。この意味において有効なアーキテクチャを構築するにあたっては、アーキテクトがアプリケーション開発チームのメンバーとして仕事をするか、少なくとも、深く関与するということが大切だと思います。アーキテクト自身もアプリケーション開発者のコードを少なくともレビューできる体制になっているべきだし、「現場のプログラマーの気持ちがわかる」状態が望ましいと思います。さらに、こういったプログラミングアーキテクチャは事前にあらかじめ決められるものではなく、繰り返しのリファクタリングを通して、繰り返し洗練していくというアジャイルなプロセスが必須であると思います。以前から私が「下流アーキテクト」という言葉を何回か使ってきましたが、少なくとも有効なプログラミングモデルを構築するにあたって、アーキテクトは重役椅子に座ってパワーポイントの資料を編集しているだけではなくて、実際に開発チームを率いるプログラマーでもあるべきなのです。(アーキテクトもプログラミングするべきか? - 達人プログラマーを目指して)

残念ながら、多くのプロジェクトにおいては、開発チームの中にこうしたアーキテクトがいないのでないかと感じることがあります。実際に、そういった事実は実際にソースコードを読んでいるといくつかの特徴から明らかになります。ここでは、アーキテクト不在を感じるソースコードの不吉な臭い(スメル)の例について、いくつか書いてみたいと思います。

間違った過剰なコンポーネント(プロジェクト)分割

初心者のソフトウェアアーキテクトでも、最初に考えるであろうことは、何とかして全体のコードを分割しなくてはならないということでしょう。モノリシック(一枚岩)なアーキテクチャがダメという考えがあるためか、とにかく、コードをいくつのコンポーネントに分割したくなります。全体のソースコードが多くのコンポーネントに分割されていれば、それだけでいい仕事をした気分になってしまうというところもあるかもしれません。*1
しかしながら、コンポーネントの分割には一定のコストが伴うということを忘れていはいけません。単にチェックアウトしてIDEの中で扱わなくてはいけないプロジェクトが多ければそれだけ面倒が増えますし、全体的なビルド時間も分割が増えるにしたがって急速に長くなる傾向があるため、CIの効率的な実行に支障が出ます。XP的な要件を満たすもっともシンプルな設計を目指すのであれば、どうして分割が必要なのかというメリットを考慮したうえで、分割は必要最低限にするべきだと思います。適切なコンポーネントの分割は簡単そうでいて、実は非常に難しいところがあると思いますし、実際、要件に合った最適な分割は何回かのイテレーションを経てようやく到達できるということも珍しくありません。一方、チームにアーキテクトが不在だと、以下のような形式的な分割の基準をあらかじめルールとして設定してしまい、結果として開発生産性を大きく下げるということになりがちです。

  • 画面ごとに分割する
  • 機能ごとに分割する
  • インターフェース、アノテーション、クラス、設定といったソースの種類ごとに分割する*2

こうした形式的な基準に従って分割した場合、一つの開発チームに割り当てられたプロジェクトの数が何百個になることさえあります。実際問題として、プロジェクトの数以上に問題なのは、こうした形式的な分割方法によって得られるのは多くの場合、密結合で凝集性の低いアーキテクチャであるということです。要するに、一見きれいにコンポーネントが分割されているようにみえるが、実際は関連した機能があちこちに分散し、実質はモノリシックなシステムと変わらない、結局は何も分割したことになってはいないという状態です。例として、Springのbean定義をxmlで記述しているとして*3、xmlのbean定義ファイルと実際のbeanの実装クラスを別々のプロジェクトに分割して管理するというルールを考えてみてください。当然beanの定義と実際のクラスは密接に依存し合っているので、通常は同時に更新する必要がありますし、別々のプロジェクトとしてビルドすることで常に不整合が発生することを避けられません。プログラマーの気持ちがわかっているアーキテクトであれば、最初からこんなルールにはしないし、むしろアノテーションを使ったりJavaソースを使ってbeanの設定とクラス定義を近くに配置するということを考えると思いますが、そうでないと、設定ファイルは別々のプロジェクトで管理するものという常識で考えてしまいます。

私としてはeclipseプロジェクトのようなビルドの単位となるコンポーネントの分割は最低限にすることをお勧めします。もちろん、分割するメリットとして、

  • 別々にビルド・デプロイできる(SOA的にコンポーネントごとに別々のチームで運用する場合など)*4
  • 依存関係を強制できる(データ層からプレゼン層のクラスを間違って参照できないなど)
  • ビューなど頻繁に変更される領域とドメインロジックなど安定した領域を分ける

といったようなことがありますが、比較的小規模なプロジェクトであれば、

くらいの分割からスタートするので十分なのではないかと思いますし、むしろ最初から過剰に分割された構造で進めるより、実際上ははるかに生産性が高いと思います。

フレームワークによって強制されたパラレル継承

これは、ずっと以前に侵略的なフレームワーク - 達人プログラマーを目指してで書いたことの繰り返しになりますが、フレームワークの規約によって一つの機能ごとに様々なクラスや設定を作成しなくてはならないルールになっている場合がよくあります。たとえば、

  • MusicTrackDao
  • MusicTrackModel
  • MusicTrackService
  • MusicTrackView
  • MusicTrackViewModel
  • musicTrack.jsp
  • JSPのタグ
  • 個々のbean定義
  • 個々の単体テストクラス

といったように、同じ名前で始まる大量のソースが存在していれば、パラレル継承のスメルを発しているといって間違いありません。*5

なお、関連してこのようなケースでは、レイヤーごとに値の入れ物として別々のクラスを定義することが強制されている場合も多いのですが、実際に多くの場合

musicTrackViewModel.setTitle(musicTrackModel.getTitle());
musicTrackViewModel.setTrackData(musicTrackModel.getTrackData());
...

のように、記述するコードの大半が単なる値の転記となることもあります。
これも、実際にプロジェクトにアーキテクトが参加していれば、そんなナンセンスな設計ルールにはしないはずなのですが、事前にアプリケーション開発チームとは別の標準フレームワークチームがルールを決めた場合など、このアンチパターンによく出会います。コンポーネント分割の場合と同様にたくさんのクラスを作成させることで、メンテナンス可能なアーキテクチャを作っているんだという気分になるという人もいるかもしれませんが、実際のところは、これも密結合・低凝集なアーキテクチャの原因となり、生産性を大きく低下させる元となります。大量の無駄なクラスを作成しなくてはならないために、初期の開発に時間がかかるという点に加えて、さらなる問題は「変更の分散」といったコードスメルの原因となることにありますね。つまり、後々のメンテナンスであちこちのファイルを同時に編集しなくてはならないということです。

もちろん、本当に継承が適切ならば継承を使えばよいのですが、JUnitやSpringMVCの昔のバージョンと今の最新バージョンを比較してみるとわかるように、最近のフレームワークでは共通のベースクラスを避ける傾向があると思います。*6

共通コードのプルアップがされていない

何らかのMVCのフレームワークでアプリケーションを作っているとして、以下のようなコードがあったとします。いったい、何が問題なのでしょうか。

/**
 * 共通ベースコントローラー
 */
public abstract class AbstractController {
    protected abstract void execute(RequestContext context);
...
}

/**
 * 返品ポリシーのリンクを描画するためのコントローラー
 */
public class ReturnPolicyController extends AbstractController {

    public static final String RETURN_POLICY_MODEL_ID;

    @Inject
    private ModelRegistory modelRegistry;

    protected void execute(RequestContext context) {
        ReturnPolcyViewModel viewModel = modelRegistory.findModel(ReturnPolcyViewModel.class,  RETURN_POLICY_MODEL_ID);
        if (viewModel == null) {
           throw new InvalidConfigrationException("No model found for " + RETURN_POLICY_MODEL_ID);
        }
 
        viewModel.setURL(URLConverter.convertURL(context, model.getURL()));
        .....           
 
        context.setViewModel(viewModel); 
    }
}

/**
 * 音楽のトラック情報を描画するためのコントローラー
 */
public class MusicTracksController extends AbstractController {

    public static final String MUSIC_TRACKS_MODEL_ID;

    @Inject
    private ModelRegistory modelRegistry;

    @Inject
    private MusicTracksDataSource musicTracksDataSource;

    protected void execute(RequestContext context) {
        MusicTracksViewModel viewModel = modelRegistory.findModel(MusicTrakcsViewModel.class,  MUSIC_TRACKS_MODEL_ID);
        if (viewModel == null) {
           throw new InvalidConfigrationException("No model found for " + MUSIC_TRACKS_MODEL_ID);
        }
 
        .....           

        context.setViewModel(viewModel); 
    }
}

...似たようなサブクラスが何百と存在

普通の感覚であれば、各サブクラスのコードの類似性に気づくと思います。共通のロジックを親クラスに移動(プルアップ)するなどして、重複を減らすべきでしょう。しかし、ここではイメージなので実際のロジックを省略していますが、実際に各画面ごとのロジックが記述されている場合、この類似性に気づかずに大量の似たようなクラスを作成してしまうことがあります。こうしたコードの類似性は実際にいくつかのサブクラスを作成してみないと気づかないことが多く、フレームワークチームから与えられたベースクラスは多くの場合重複コードの削減のためにあまり役立ちません。アーキテクトが開発にかかわっているのであれば、こうした重複を発見する都度リファクタリングによって取り除く努力をすべきだと思います。

手続き型のコード

これはこのブログでも何度か取り上げてきたことですが、Javaなどのオブジェクト指向の言語を使っているにも関わらず、ほとんど活用できていないというケースが結構あると思います。実際、そういったプロジェクトで作成されたコードをみると、

  • データとgetter、setterだけのクラス
  • すべてstaticメソッドからなるXXXUtilクラス*7、あるいは巨大なサービスクラス
  • ポリモーフィズム(多態性)が使えておらず、Enumや型によって似たようなswitch文の構造が繰り返し現れる

といったような手続き型の言語で設計したのと大差のない構造になってしまっています。これは、要件やチームのスキルによっては必ずしもアンチパターンとまでは言えないかもしれませんが、やはり、プログラミング言語のあるべき設計を使いこなせるアーキテクトがチームに存在していないのが原因の一つなのではないかと思います。

ところどころに散在する実験コードの痕跡

コードのアーキテクチャのスメルは、なにもスキルの低いプログラマーのみで構成されたチームで起こるとは限りません。非常にモチベーションがあって、やる気のあるプログラマーであれば、自然なこととして、いろいろと自分の新しいアイデアをどんどん試したくなります。それは、技術者としてはすばらしいことなのですが、業務アプリケーション開発のプロジェクトの顧客の要件を最短で満たすという制約された条件の中では、ある程度節度をもって計画的にやらないと問題となることがあります。実際、うまくいかなかったアイデアなどは未練を捨てて早期にクリーンアップするべきだと思いますが、実際上は時間の制約からか、あるいはプログラマーが入れ替わったからか、ところどころに過去の遺産がデッドコードとして残されることがあります。そういったデッドコードが存在していると、障害の解析が困難になったり、後々機能追加が大変になったりメンテナンスの上で苦労することになります。
そういった問題を防ぐためには、やはりアーキテクト的な視点からコードの状態を監視し、常に余分な複雑性が取り残されないようにする努力やプロセスが必要だと思います。

最後に、最近気づいたことを

最後に、最近になって気づいた自分の間違えについて書いておかなくてはなりませんね。以前であればこうした設計上の問題は日本のSI業界の構造が問題なのであるという話をしていたかもしれません(^_^;)が、ここで書いたような話は多少フィクションが入っているとはいえ、実際私がSI業界以外の今の会社で体験したことに基づいていると告白しなくてはなりません。言語の特性から、Javaで開発していると、こういった設計上の問題が起きやすいということがある可能性もありますが、こういった話はSIer以外でも、どこの国の開発チームでもあるのだなということですね。

こうした設計に陥る原因として考えられるのは、SI業界の構造というより、むしろ、アーキテクトがアプリケーション開発チームと一緒に仕事していないという問題にあるのではないかと思います。そして、実際多くの場合、「フレームワークチーム」「共通チーム」というのがアプリケーション開発チームとは切り離されて存在しており、フレームワークを開発プロジェクトが開始する前に作成して提供するというようなモデルの場合に起こりがちなのではと思います。さらに、非常によくあるパターンとして、あるバージョンのフレームワークを使ってアプリケーションの開発が進められている時点では、フレームワークは既にアクティブに開発されておらず、フレームワークの改善に興味がないということがあります。既にフレームワークチームは「次世代フレームワーク」の開発に目が移っていて、古いフレームワークはよほどのバグがない限り手を付けないというようなケースが多いかもしれません。

自分としては、なるべくオープンソースのフレームワークを活用するなどして、自社フレームワークの部分を最小限にし、アーキテクトはアプリケーションのアーキテクチャの改善にもっと目を向けるべきなのではないかと思うのですが、さまざまな政治的、ビジネス的理由からそれができないのが問題でないかと思いますね。

*1:なお、ここではコンポーネントとはJavaパッケージのような論理的な名前空間の分割ではなくて、eclipseやmavenのプロジェクトに相当するようなビルド・デプロイの分割単位のことを指すものとします。

*2:場合によってはインテーフェースやアノテーションを分けるのが有効な場合ももちろんありますが、それにはちゃんとした理由があります。リモートのサービスでインターフェースだけをクライアントから使うとか、昔のSpringがそうしていたように、JDK1.4をサポートするために、アノテーション依存の部分を分けるといったような例があります

*3:レガシーコードだったらともかくも、新しいプロジェクトでxmlに個々のbean定義を書くような規約だったら、これ自体かなり疑問な設計方針なのですが

*4:コンポーネント分割の構造とチームの構造がマッチしているのは基本的によいことだと考えられます。

*5:そして、多くの場合作成すべき各コードにベースとなる親クラスやインターフェースが存在してそれを継承して作成するルールになっているのですが、実際親はマーカーというか、実際になんら本質的な役割を持っていないという場合も多いです。これは親クラスが実際の必要性から作られたのではなく、最初からルールとして存在しているという何よりの証拠といえます。

*6:いわゆるPlain Old Java Object=POJOとして作成可能、継承などはアプリケーション側の裁量で自由に使えるようにする。

*7:StringUtilsやCollectionsのような標準のライブラリーにもみられるように、どこのクラスにも属さないロジックの格納場所として、最低限のUtilクラスは特に問題ではないと思います。ここで問題としているのは、大部分のビジネスロジックがUtilクラスで書かれているというようなケースですね。

アマゾンにおけるソフトウェア開発の仕事について感じたこと

ちょうど、先日アマゾンのオープンハウスというイベントでお話をさせていただく機会があったのですが、開発者向けの20日のセクションだけで90名近くの方々にご参加いただきました。平日にもかかわらず、多数の方々にご参加いただき、どうもありがとうございました。

私自身は、昨年秋にSIerからアマゾンに転職してまだ半年ですが、この機会にアマゾンにおけるソフトウェア開発の文化や考え方について、ブログでご紹介できる範囲でまとめてみたいと思います。

私は、ずっとブログに書いてきたようにSI業界からの転職だったのですが、一般的なSIerにおけるソフトウェア開発の考え方や手法といろいろな面で違っているということは予想していたというか、もともと覚悟の上での転職でした。それでもやはり最初のうちはあまりにも大きな変化に自分の仕事のスタイルを合わせるのにいろいろと苦労しました。基本的には転職したての頃に抱いた感想(転職して感じたウォーターフォール文化とアジャイル文化の違いについて - 達人プログラマーを目指して)から大きくは変わっていないのですが、徐々に会社の文化や仕事のスタイルに慣れ、また、開発環境やツールにも習熟してくるにつれて、始めの頃の不安や戸惑いがようやく薄れて、徐々に自信や面白みに変化してきたかなという段階です。

SDEはやはりプログラミングが仕事の中心

アマゾンでもアプリケーション開発からインフラ、コンサルティングまで様々なエンジニアのポジションがあるのですが、私はソフトウェア開発技術者(Software Development Engineer、SDE)という役割で、携帯電話用のWebサイトを開発するAnywhereというチームで働いています。*1SDEとはいわゆるプログラマーのことなので、SIerで働いていた頃と違って、実際にアプリケーションのコードを自分で作成・修正するということが仕事の中心となります。

もちろん、開発環境が動かなくなってトラブルシュートに時間を取られたり、他のチームとのコミュニケーションに必要な時間もあるため、毎日すべての時間をコーディング作業に費やすということが可能なわけではありませんが、できる限りコーディングに時間をさけるようにすることが生産性の高いSDEとしては義務であり権利でもあると考えられています。前職では何か月もExcelの設計書を書いたり、パワーポイントの提案資料を考えたりという時期があり、コードを書くことも他の開発者向けのサンプル程度というプロジェクトが多くあり、プログラマーとしては欲求不満になってしまう場合が多くありました。現職では、とにかく実際にアプリケーションの機能を自分自身で実装するという仕事に堂々と集中することができ、プログラミング技術を発揮し、さらに腕を磨きたいと考えていた私にとっては大変に適合した仕事でうれしく思っています。

そういうわけもあって、SDEの採用面接では、まず何よりもプログラミングや開発の基礎力が問われます。プログラミング言語を使ったロジックの記述から、データ構造やアルゴリズムといったコンピューターサイエンスの基礎知識が問われるようです。個人的には実際の開発に必要な経験や知識はそれ以外に、開発ツール、フレームワーク、ミドルウェアを使いこなすことが重要だとは思いますが、多くの場合は面接で能力を測る基準としては基礎力を重視しているようですね。

プロジェクトの規模とペース

それから、SIerの場合と大きく異なることとして、一般に個々のチームやプロジェクトの規模が小さく*2、とにかく数週間から数か月といった早いペースで機能を連続的にリリースしていくということがあります。オープンハウスでも紹介したように、アマゾンでは、Scrumというアジャイルプロセスが標準的に多くのチームで採用されています。通常は提案や画面設計に何か月も費やすということはありませんし、プロジェクトがキックオフしたその日のうちからプロトタイプの着手に取り掛かるということが普通です。そして、数週間から数か月といった単位で様々なプロジェクトが並行して活動し、次々と機能追加をリリースしていきます。

もちろん、アマゾンでもキンドルやプライムといったビジネスモデルに大きく影響を与えるようなプロジェクトだと世界中のたくさんの開発者が参加する大規模なプロジェクトになるわけですが、個々のチームは比較的小規模なチームに分かれていますので、一つのチームの単位としては1人からせいぜい5人程度の小規模なグループで作業します。

文書化の作業はどうしても優先度が大変に低いと考えられているので、最初のうちは呼び出すAPIがわからないとか、そもそも準備されていないといったことや、リリース直前のUATの段階でUIの変更が入ったりということもあります。一般的なSI案件のように決まった設計書を念入りに準備して、その通りに作成、納品するという考え方とは当然大きく違っています。私も、Excel方眼紙などに細かく仕様を記述する方法は嫌いなのですが、APIのドキュメントやアーキテクチャ概要のドキュメントなどはもう少ししっかりしたものがあればなあと時々思うことはあります。特に、自分の英語力の問題から、口頭やラフなメールで説明されるより、フォーマルな文書が基準としてあった方が便利なのにと感じることはありますが、変化のペースがとにかく速いので、文書化はどうしても後回しになってしまう傾向があるようです。

SDEに与えられる大きな権限

個々のSDEは単にコードを記述するというだけでなく、仕様変更やリリース時期の調整などを含めてプロダクトマネージャやデザインチームに対して意見を述べることが期待されます。また、リファクタリングやツール、プロセスの改善などを積極的に工夫し、自らプロジェクトとして提案するというような活動は大いに歓迎されます。*3とにかく、手を動かすということが評価されるようですね。

それから、デプロイメントの自動化などのツールは充実しているので、SDEの権限で本番環境を含めて様々な環境にアプリケーションをリリースすることが可能です。緊急の大きな障害が発生したような場合、一般のスケジュールとは別に急きょ試験、リリーススケジュールを立て、本番へのリリースを行うといったことも発生します。もちろん、さらに大きな障害につながる恐れもあるわけで、これは、私もなかなか最初はすごく緊張したのですが、最近はようやく肝が据わってきたかなという感じです。

新規開発よりも、既存システムの改善

私の担当したSIer案件では、アーキテクチャの提案やビジネスプロセスモデリングといった上流工程からプロジェクトが開始するということは結構普通でした。私はアーキテクトという役割から、比較的新しいフレームワークの活用方法を考えたり、全体的なシステムのしくみを考えるといったこともありました。現在の自分の経験と立場ではそこまで大きな視点でシステムを考えるという段階ではないところもありますが、既存のバグを修正したり、リファクタリングを行ったりという作業が日々の作業の中心となっています。SOAのような大きなアーキテクチャは長い時間の間に徐々に完成させられてきたため、基本的には小さな改善をこつこつと積み重ねるというボトムアップのアプローチです。ただし、新しい技術の採用など、時には思いきった大きな変更やブレークスルーも必要だと思います。

学ぶべきツールやフレームワークなどが無数にある

アマゾンの場合は、AWSなどの公開されたツールもたくさんありますが、社内にはそれ以上にたくさんのツールやフレームワーク、活用すべきデータがあります。他のチームのソースコードは社内では基本的に公開されています。残念ながら、多くのツールは外部には非公開なので、ここではあまり詳しく書けませんが、そういう意味で、いろいろと研究のしがいがあります。

具体的にソフトウェアの改善効果が数値化されている

先日のイベントでもA-Bテスト*4という考え方について紹介したのですが、アマゾンではレスポンス時間などシステム的なことはもちろん、アクセス数や売り上げなどビジネスにつながるさまざまなメトリクスを自動計測する仕掛けが提供されています。したがって、追加した機能が効果的かどうかということがグラフとしてすぐにわかってしまう仕組みになっています。自分の作成した機能の効果がどんなものなのかがリリース直後から確かめられるのは、楽しいものがあります。

もちろん、アマゾンの社員が目指すべき最も大切な目標としてCustomer Obsession(顧客第一)というのがあるので、そういった情報はユーザーエクスペリエンスの向上に活用されます。

英語を使ったコミュニケーションの必要性

アマゾンジャパンの公用語は日本語なので、日本語が通じる人には堂々と日本語を使ってもよいのですが、外国人や海外のチームとコミュニケーションする際にはどうしても英語を使わなくてはなりません。Scrumなどの会議もすべて英語で行われています。*5

結局、私の所属するチームでは、ほぼ英語でコミュニケーションする必要があります。自分の英語力では、何とか会議の話について行って、進捗を報告するというくらいはできても、なかなか自由に自分の考え方を伝えることは簡単ではないというところがあります。やはり、この点に関しては私にとって英語圏の人に比べてものすごいハンディがあると感じざるを得ませんが、技術力などでカバーしていくしかないですね。

英語のハンディという点では多くの日本人だけでなく、中国人も同様のようなのですが、最低限メールやチャットなどで会話ができれば何とかコミュニケーションはできます。また、コードレビューもソースコードのdiffを使ってオンラインでコミュニケーションするのでまあ何とかなりますね。

ちなみに、ランチタイムは重要な英会話レッスンのチャンスだと思ってできる限り外国人の同僚と一緒に食べに行くようにしているのですが、おかげでハンバーガーや中華など脂っこい食事を食べる機会が大幅に増えました。アマゾンの目黒新オフィスでは社員食堂ができたので、今は自分の好きなメニューを選択できるようになりましたが。

まとめ

以上、最近仕事について感じたことをいくつか簡単に書いてみました。いろいろ苦労するところもありますが、自信を持って日々大いに楽しんで仕事ができているかなと思います。そういうこともあって、最近は情報をインプットして処理するという方にどうしても時間が割かれてしまい、ここのところ、ブログの更新頻度が下がっています。どこまで技術的なことを書いてよいかということはありましたが、JavaやPerlなどの一般的な言語や公開されているツールの話については、大丈夫なようですし、また余裕が出てきたら時々更新していければと思います。

*1:日本に携帯サイトの開発チームがおかれているのは、やはり世界的にみても日本の携帯電話の技術や使い方がユニークで進んでいるという事実があります。

*2:SOAに従ったソフトウェアの構造にならい、開発組織もカート、カタログ、検索、注文プロセス、運用基盤など、システムやユースケース単位ごとに独立した小さな開発チームに分かれており、全体としてチームがいくつあるのかさえ把握が困難。

*3:最初私も勘違いしていましたが、アマゾンも全然理想郷ではないので、いろいろ問題や改善点がたくさんあります。エンジニアにとっては改善点があることはモチベーションにつながるのでよいのですが。

*4:異なるバージョンを一定割合ごとに同時にリリースして、統計的に効果を確かめるテスト手法。

*5:時々裏で中国語や日本語が混じりますが。

日本のユーザー企業は忍者のようなプログラマーをもっと登用して重用すべきでは

あの記事から一年、ひがやすを氏が以下のエントリーで、プログラマーとして、新しいサービスを作ることの難しさについて書かれています。
僕と君とSIerの生きる道 - yvsu pron. yas

確かに私自身は、サービスを作る側に回った(まだISIDにいるけど、ベンチャーで働いているようなものです)のですが、身を持って面白いサービスを作る難しさも経験しました。

面白いサービスを作るのはほんとうに難しい。その後、マネタイズにも成功するのはさらに難しい。サービスを作る側に回って成功するのはほんの人握りの人なんです。

もともと一年前におっしゃっていたことは、SIerのビジネスに将来性はないから優秀なプログラマーは自分でサービスを作る側に回らなくてはならないし、単によいコードを作れるだけでなくて、自分からアイデアを考えられるようにならなくてはならないということだったかと思います。一年前この記事を読んだときは、私としてはかなり衝撃を受けました。一人前のプログラマーとして認められるには、もはや単にきれいなコードを書く技術やフレームワークやミドルの知識だけでは不十分なのであり、自分からビジネスを創り上げるアイデアがなくてはならず、単に勉強して技術力を磨くというだけでは不足であるということのように思われたからです。
どうしても、停滞気味なSI業界の中でエンジニアとして働いていると、ビル・ゲイツやマーク・ザッカーバーグのように新しい時代を切り開いて成功者となったプログラマーにあこがれることは自然なことですし、プログラマーとして大きな目標を持って頑張るということは悪いことではありません。しかしながら、現実問題として、そういう成功者に誰もが簡単になれるわけではないですよね。
大きな目標を立てることは立派なことだけれども、これは、戦国時代に例えるならば、すべての武士に対して、信長や秀吉を目指せといっているのと同じくらいに難しいことのように思われます。しかし、一方で、政治の表舞台には登場しなくても、裏方として活躍した軍師や忍者として活躍したような人もたくさんいたと思われます。そういう忍者たちは、表向きの身分こそ低くても、自分の剣術や諜報スキルを磨くことで、大名に信頼されて、戦略上重要なプロジェクトを任されていたのではないでしょうか。
基本的に、信長の野望のようなシミュレーションゲームにおいては、大名のようなトップに立つ人はすべての能力において優秀でなくては務まらないと思いますが、忍者や軍師のタイプは智謀や武力など一部の能力に特化したスペシャリストとして登場します。私としては、多くのプログラマーは大名タイプよりも、むしろこうした忍者タイプを目指した方がよいのではないかと思うのですよね。
忍者はスペシャリストですが、大名のそばにいて、重要なプロジェクトを引き受けます。*1これは、現状のSI業界における開発が大部分百姓から臨時でかき集めたような傭兵による集団戦法に頼っているのとは対照的です。
Amazonではエンジニアのことを忍者と呼ぶことがありますが、単に外国人の興味を引くということだけでなく、この呼び方はエンジニアの職務を表現する上で非常にマッチしていると考えます。*2

取扱商品数の拡充等で成長を続けるオンラインストアだけでなく、最先端のクラウド技術を活かしたAmazon Web Services(AWS)の展開と、さらなる挑戦を続けるAmazon。その影の存在でありながら凄いことをするという思いを込め、Amazonのエンジニアは自分達を忍者(Ninja)と呼びます。

Amazon エンジニアキャリア採用サイト
army of traveling Code Ninjas
AWS、プライム、キンドル、モバイルなどそれぞれの戦略を担当するチームの中で、エンジニアが忍者のように裏方として活躍しています。これらの忍者たちは、必ずしも有名人とか大金持ちというわけではないですが、スキルを活かして、ビジネスの戦略を遂行する上で重要な役割を担っています。
もともと、忍者というものを生み出した国でありながら、日本のSI業界においては忍者のようにプログラマーが重用されることが少ないのが不思議なくらいです。大量の傭兵軍団からなる集団戦法を使って数で勝負というだけではなく、少数精鋭のメンバーで戦略的に勝つというようなやり方が、日本のユーザー企業でももっとポピュラーになれば、日本でも上級プログラマーの活躍する場所がもっと増えるのではないかと思いますし、こうした忍者衆をそばに抱えておけば、ユーザー企業にとってもITを本当にビジネスで活用する上で有効なのではないかと思いますがいかがでしょうか。

*1:実際には当時の忍者には上忍とか下忍とかの身分があり、服部半蔵のような有名な忍者が自分から現場で活躍したかどうかは疑わしいそうですが。

*2:PGというと伝統的にモノづくり職人、鉄砲鍛冶、刀鍛冶の職人のイメージがありますが、最近はITで得られた情報から素早く次の作戦を遂行するという役割があるので、忍者はイメージとして合っているのではと思いますね。つまり、作戦の考案から実行まで、上流から下流までプロジェクトを素早くタイムリーに遂行するということです。

ソフトウェア技術者軽視のシステム開発を続けるのはもう限界かもしれない

つい先日、富士通がグループで抱える3万人ものSEを再教育して、職務転換を行う計画であるというニュースを知りました。
富士通の3万人SE職務転換大作戦は成功するのか? - GoTheDistance

一つのシステムを複数の企業などが利用するクラウドサービスがこのまま普及すれば、顧客の要望を聞いて個別システムを作り込むSEは仕事がなくなり、余剰人員問題が顕在化するからだ。

クラウドの普及により、オーダーメイドでシステムをゼロから構築する必要がなくなり、そもそも顧客からの要件をまとめてシステムを設計するSEの仕事が不要になったり、基盤を構築、運用するエンジニアが不要になるということは、最近になってよく言われることであり、特に新しいことではありません。もちろん、クラウドの普及によって、これらの伝統的なSEの仕事が少なくなり、人員が余るという議論は間違いではないと思います。
ただし、一方でより本質的に重要なことは、クラウドの普及により、要件の定義から実現、運用までの期間が大幅に短縮できるようになったり、基盤構築など多くの仕事が自動化されることで、上流から下流まですべてを担当できるような真のソフトウェア技術者の役割がシステム開発で重要になってきているというところにあると思います。したがって、上流担当のSEだからといってプログラミング言語や基盤技術のことをまったく知らないといったことが許されない時代になってきているということであり、アメリカ的な意味でのプログラマー(ディベロッパー)の仕事がもっと重要になってきているということを理解する必要があります。プログラマーというと、単に設計書に従ってコードを打ち込む単純作業をする人や、逆にすごく難しい計算式とアルゴリズムを使うような研究者など、まったく異なるいろいろな職業を思い浮かべる人がいますが、少なくともグルーバルな意味でのエンタープライズ開発のコンテキストにおけるプログラマーとは、顧客の要件を素早く実現する方法を提案して、そのまま構築し、場合によってはテスト自動化やデプロイメントまで担当する人のことを指します。そういう意味では当然上流も下流もありません。もちろん、経験やスキルによって下級プログラマーから最上級のプリンシパルプログラマーまで幅はありますが、上級職であっても、オブジェクト指向プログラミングができなくてよいということではなく、むしろお手本となるコードを書いたりコードレビューができることが求められます。
クラウド時代になったので、技術のわからない人でもシステムを構築できるということが時々聞かれます。もちろん、一部にはそのようなシステムもあるとはいえ、基幹業務やビジネスの中心にかかわるようなシステムであれば、クラウドを利用するとは言え、アイデアさえあればまったくの素人がシステムを構築できるとは考えるべきではないでしょう。この点については、ちょうど一年ほど前にも以下で議論しています。
日本のSI業界でこそ、専門の技術者の必要性がもっと見直されるべきではないのか? - 達人プログラマーを目指して
そういう意味において、ついに富士通のようなSIerも、今回の職務転換で今までのような上流偏重型の技術軽視の考え方を改め、

などの知識を持った専門家をついに育成することになるのかと期待したのですが、実情は大きく異なるようですね。
富士通が説くSIにおける人材革新のツボ - ITmedia エンタープライズ

 「これまでは、いわゆるモノづくりに焦点を当てた人材づくりを行ってきた。現在、SEが担っているスキルとしては、コンサルティングから入って、開発のためのプロジェクトマネジメント、業務アーキテクチャ、品質マネジメント、ITアーキテクチャ、プロダクトアーキテクチャ、そして運用・保守を行うサービスマネジメントがある。しかし、これからは顧客価値の実現に向けて顧客とともに考えていくという取り組みが一層求められるようになる」

 それが、図の上部に示されているところの「スキルからロールへのシフト」である。同社が新たな時代に向けて再定義したロール、すなわち「役割」に応じた職務は、「ビジネスプロデューサ」「フィールド・イノベータ」「コンサルタント」「サービスインテグレータ」の4つ。

もちろん、詳しい内容は分からないのですが、結局のところ、以前にもまして上流重視の方針のように思われます。*1残念ながら、富士通のSEの職務転換先には、(アメリカ的な意味での)プログラマーもSDET*2もクラウド基盤運用管理の専門家も含まれていないようです。
ところで、最近特許庁のシステム開発中断という以下のニュースも話題になっています。
http://www.asahi.com/business/update/0124/TKY201201240616.html
調査報告書 平成22年8月20日 特許庁情報システムに関する調査委員会
特許庁の情報システムについて - myatsumoto blog
5年も前からずっと設計を行って結局完成しなかったということですが、その間コア機能を含む一部のサブシステムですら構築することはできなかったのでしょうか?このように何年もかけて上流の設計を続けても、まともなシステムが一つも完成しないという話は特許庁に限ったことではなく、メガバンクの基幹システム構築などいたるところで耳にします。そもそも設計に5年もかけていては、技術の内容もビジネスの内容も大きく変化してしまうわけでウォーターフォール的に設計を固定できるわけがないですし、仮になんとか完成にこぎつけたとしても使い物にならないシステムができるだけだと思います。
プログラマーの自分の目から見て、いつも非常に不思議に思うのは、こういった問題が起こった場合、

  • 管理の方法が悪かった
  • 顧客との仕様の調整がうまくいかなかった
  • 上流の基本設計がうまくまとまらなかった

というような話は出てくるのですが、決してプログラムや基盤の設計が原因とされることがないということですね。実際にはこういった実装面の問題は全く無視できるはずはなく、コピーアンドペーストだらけで複雑になり、まったく手が付けられなくなったり、あまりにも性能が悪くて要件が満たせないというような問題はよほど簡単なシステムでもない限り大きな問題となるはずです。(プログラミングと設計は本来切り離せないものなのでは - 達人プログラマーを目指して)
伝統的に、日本のSI業界ではその構造から実装、構築の作業を軽視する傾向があったわけですが、いい加減にこのような考え方でシステム開発を続けることは品質やコスト、スピードの面でもう限界に達していると考えられるのではないでしょうか。
富士通のようなSIerはSEの職種転換を目指すのであれば、単に上流のコンサルタントだけでなく、上級の開発者の仕事をもっと重視すべきなのです。もちろん、そのためにはござ先輩も書いているようにSIerはビジネスモデルの大変革が必要だと思いますが。あるいは、ユーザー企業がもっと率先して上級のプログラマーを雇ってより短期間で効率よくシステムを開発できるようになるべきかもしれません。その場合、SIerは文字通りシステム統合の手助けとか、開発基盤の提供など、ビジネスモデルなどではなく、より開発のインフラよりの部分を担当する役割を担うことになると思います。

*1:成果物がExcel方眼紙からパワーポイントになるという意味ではスキルの転換が必要かもしれませんが。

*2:Software Development Engineer in Test、テスト自動化などの専門家

開発コストや技術リスクを考えない「上流設計」がシステムの複雑化と大規模な障害の原因となっているのでは?

皆さん、明けましておめでとうございます。昨年の後半は私自身SI業界からWeb業界へ転職したことなど仕事環境の変化があり、ブログの更新頻度も鈍りがちになってしまっていましたが、本年もどうぞよろしくお願いいたします。
さて、ちょうど、一年前のお正月にはグルーポンのおせち料理事件が話題になっていましたが、私はおせち料理の品質とIT業界における品質の問題を絡めて、以下の記事を書きました。
グルーポンのおせち事件を受けてSI業界が本当に教訓とすべきこと - 達人プログラマーを目指して
この記事では、一般にSIerによって開発される日本のシステムはあの事件のおせち料理のように、低い品質に甘んじているが、多くの場合、社内システムなどではそういった品質の問題が公に明らかにされることが少ないのではということを指摘しました。ただ、その時は私の希望も込めて

最近はOSSやクラウドなどの影響で社内システムもどんどんネット上のオープンな世界から調達されるようになってきているわけですし、いい加減にこうした状態がこの先何年も続くことはないのではないかと思います。アリとキリギリスの話ではありませんが、来るべき冬の時代に備えて、本物の技術力(プログラミング力だけでなくて提案力などプロの技術者としての仕事力も含む)をつける勉強をしておくことは決して無駄な努力にならないと思います。自らが作り出して深く関わっているIT技術によって「真面目に勉強する人が損をする」「正直者が馬鹿を見る」業界の体質はそろそろ終わりにならざるを得ないのではないでしょうか?

のように結びました。残念ながら、その後1年間でシステム開発の方法が大きく進化し、品質の問題が劇的に改善されたという話はあまり聞きません。逆に、昨年3月の震災後にメガバンクが起こした大規模障害や最新スマートフォンの動作不具合など、多くのユーザーの生活や仕事に影響を与える大規模なシステム障害のニュースがたくさん耳に入ってきました。ますますB2Cのシステムが重要になってきているという一般的な傾向の中で、システムが直接消費者である顧客の目に触れる不具合が多くなってきたことで、いよいよごまかしが効かなくなってきているということは言えるかもしれません。もちろん、プログラムにバグは付き物ですし、AppleやGoogleなどのどんな優秀なプログラマーが作ったプログラムにも障害はたくさん含まれているわけですが、それにしても、日本製のシステムは最近多くの重大な障害を起こすように思われます。
さて、今年は早速ドコモのspモードで全国的にメールが送信できなくなるという障害のニュースがありました。
http://www.nttdocomo.co.jp/info/network/kanto/pages/120102_1_d.html
http://www3.nhk.or.jp/news/html/20120103/t10015025811000.html
spモードは昨年末にも電子メールの送信先が入れ替わってしまうシャッフル問題など重大な障害があったばかりですし、これまでもあまりよい評判を聞かないようですね。今回の新たな障害の原因は

spモードのサーバーの故障

と発表されていますが、やはり、これほどたびたび重大な障害を起こすということは、単に偶然ハードウェアが故障したという直接的な原因の前に、もっと根本的なシステムの設計や開発手法に問題があるのではないかとは誰もが想像するところです。spモードについては一部コンテンツプロバイダー(CP)向けの技術資料が以下で公開されているため、どのような開発が行われているのか、一部を伺い知ることができます。
http://i.mydocomo.com/docomoid/dlogin/pc/developer.html
上記のサイトから、docomo IDを用いたサイトの認証方式の仕様書がダウンロード可能です。
docomo ログイン I/F仕様書
これは、60ページほどのドキュメントで、シーケンス図など多くの図を用いて、spモードの「公式サイト」でdocomo IDを取得するためのインターフェースが説明されています。付録にはJavaとPHPによるサンプルコードも書かれているので、CP側で実際にどんなプログラムを書く必要があるのかもわかります。
spモードはドコモのスマートフォンでガラケーのiモードのようなサービスが利用可能になるサービスなわけですが、マイメニュー登録など課金インフラを利用するためには、前提として携帯電話を使っているユーザーを特定するIDをCP側で取得する必要があります。従来のガラケーではGUIDと呼ばれるIDが端末と簡単に結びつけられたわけですが、当然スマートフォンの場合はサービスを利用する前に、認証によってユーザーを正しく特定することが前提となります。それで、この仕様書の要点としては、CPはスマートフォンのユーザーを特定するSUIDを通知されるためにOpen IDプロトコルのRPを実装しなくてはならないということがわかります。
Open IDというのは認証IDを複数のサイトで共有する仕組みで、シングルサインオンのメカニズムの一つとして利用されているオープンな仕様です。ユーザーから見ればいったんドコモのサイトでdocomo IDを使ってログインすれば、個々のサイトごとにログイン名やパスワードを個別に記憶する必要がないので便利です。オープンな標準仕様であり、さまざまな言語でアクセス利用可能なライブラリーも多数存在しています。
OpenID Wiki / Libraries
認証基盤もCPごとに作りこまなくてもドコモにおまかせすればよいので、サイトの開発コストを抑えることができるでしょう。このように考えると、この仕様は一見合理的によく考えられているように思われます。しかし、本当に最後までサイトを構築することを考えると

  • Open ID自体は複雑な仕様のため、プロトコルを全部自前で実装するのは非現実的だが、実際にどのライブラリーを利用すればよいのか。
  • spモードのログイン仕様は、複数回のHTTPのやり取りが必要で、CPが何らかのサーバーサイドの状態を保持することを前提としたプロトコルとなっているように思われる。
  • 開発時にはどのような手順で接続し、試験すればよいのか
  • 既存のサイトでシングルサインオンの提供が難しい場合はどうすればよいか

などなど、実際に「下流の」開発者の立場で、細かいことを気にし出すと具体的には実際にどうやって作ったらよいのかということがまったく書かれていないことに気づかされます。多数の図を使って、一見非常にわかりやすく解説されてはいますが、実際の実装レベルを想定したアーキテクチャの詳細がほとんど書かれていませんし、技術リスクのようなことも十分に検討されているとは言い難いところがあります。
もちろん、自分はspモードシステム開発の当事者ではありませんし、直接的に開発にかかわったこともないので、もちろんこれは私の勝手な想像の域を出ないわけですが、結果として、開発側で細かいところは個別に何とか解決するしかなく、余程運に恵まれない限り、デスマといいますか、想像以上に開発リソースを必要とする非常に大変なプロジェクトになることが頭に浮かんできます。非公開の仕様にはもっと別の設計資料や開発SDKが存在する可能性もありますが、少なくとも公開されている仕様書を読む限り、これは、日本のIT業界には非常にありがちな上流の仕様書・設計書のように思われます。
GoogleやTwitterなど外国のIT企業の場合、このような分厚い設計書を用意する以前に、システム連携のための便利なAPIを主要な言語でライブラリーとして実装し、試験用のSDKとともに開発側に提供するのが普通のように思われます。外国では設計に上流も下流もなく、きちんと動作する仕掛けができていて初めて設計が完了していると考えます。もし、ドコモがこのようなライブラリーやWebサービスのインターフェースを実装して公開していれば、少なくとも各CP側の開発費用ははるかに抑えられたはずですし、プロジェクトが失敗する危険も小さくなるのではと思います。
もちろん、これは公開されている資料に基づいて想像されるspモードの設計の複雑性のほんの一部分の問題に過ぎず、今回の大規模障害との直接的なつながりはないと思いますが、他の部分でもこのように開発工程を上流、下流と分け、ウォーターフォール型で細かい技術リスクを下流の下請け業者に押し付けているのであれば問題ではないでしょうか。
やはり、私としては、日本のIT業界で伝統的に行われるウォーターフォール型の開発手法がシステムの複雑化と大規模な障害の原因となっているのではと考えてしまいますね。より、高度で複雑なシステムの開発を行うのであれば、下流工程を無視して「上流設計」を行うという考え方をいい加減に改めるべきではないかと思います。そして、キャリアーのようなインフラを担当する会社は、単に上流の設計書を仕様として作成するだけでなく、開発者が簡単に開発できるような開発環境やフレームワークを構築して提供するようにすべきではないでしょうか。少なくとも、簡単なサンプルだけではなく、参照実装としてのサンプルサイトのようなものをCP開発側に提供する必要があると思います。一方、プロトコルの詳細などは、RESTやSOAPといった標準的な方法に従った上位のインターフェースを提供するライブラリーによってうまく隠ぺいするようにすればよく、各CPに対して大量の文書でプロトコルの詳細を説明することも重要ではないと考えます。(むしろ、APIのソースが提供された方がよい。)実際、このブログに張り付けてある広告などの多くのウィジェットは何も考えなくても多数のサービスを簡単に利用できています。
そのためにも、上流設計のSE、コーディングのPGといった役割を分けるといった考え方ではなく、上級のプログラマーが企画段階から設計にかかわることが大切だと思います。
(追記)
一般的にシステム障害の原因がウォーターフォール型の開発手法のみにあると考えるのは、もちろん適切ではないと思います。ただし、ここで取り上げたspモードのように、今までになかった新しい仕組みをウォーターフォール型で構築するのはいろいろと困難を伴うことが多いということは言えるのではないでしょうか。後から見つかった仕様上の欠陥を補うために、後からいろいろな回避策を考えるにしたがって、より複雑なシステムになってしまうということは実際によくあるのではと思います。すなわち、下流工程と呼ばれるプログラムの実装やシステム基盤の構築作業から得られた問題点の指摘やアイデアを仕様にフィードバックすることが難しくなってしまいます。*1
ここでspモードを取り上げたのはたまたまタイミングの問題で、もちろんドコモ一社のやり方を非難することがここでの目的ではありません。新しいサービスをタイムリーに提供する上で、従来のウォーターフォール的な考え方が大失敗の大きな要因の一つになっているのではないかということです。

*1:Googleなどの場合はベータサービスという形で結構長い期間お試しのリリースが行われることが多いと思いますが、携帯電話のサービスの場合はそういう形は難しいのでしょうかね。

Javaのクラスとオブジェクトについて再度解説を試みる

オブジェクト指向プログラミングの考え方については、今までこのブログでも何度か取り上げてきました。
[オブジェクト指向] - 達人プログラマーを目指して
オブジェクト指向プログラミングはプログラミング技法のすべてではないとはいえ、Javaのようなオブジェクト指向言語で本格的なプログラムを作るには理解を避けて通ることができませんし、また、関数型言語など他のパラダイムの言語を利用するにしても、オブジェクト指向の考え方をまったく理解しないまま使いこなすということは困難でしょう。オブジェクト指向の考え方はデータ構造やアルゴリズムといったことと同様に、プロフェッショナルなプログラマーが理解しておくべき基本的な素養といってもよいと思います。実際、海外では募集要項でオブジェクト指向の理解を前提とすると書かれていることが普通ですし、プログラマーの面接試験で、アルゴリズムと並んでオブジェクト指向プログラミングの基本についての正しい理解を問うケースが多いようです。
しかしながら、特殊なケースを除いて、我が国ではいまだになかなか普及していないようですね。数週間の新人研修やOJTのみで短期間に理解できるほど簡単ではないというのは事実かもしれません。それゆえ、SIの開発案件ではJavaやVisualBasicといったオブジェクト指向言語を用いながらも、実際にはオブジェクト指向を利用しないで開発できるような規約を作るといったこともかなり一般的に行われているようです。
オブジェクト指向プログラミングの普及を妨げる原因の一つとして、わかりやすい入門向けの解説を作ることが難しく、また、実際にそのような解説が書かれている入門書も非常に少ないということがあると思います。実際に、Javaの入門書の場合、ずっとmainメソッドのみでifやforなどの構文を説明した後、いきなりVehicle(乗り物)クラスやCarクラスなどの説明に飛躍するといったケースも多く、初めてオブジェクト指向プログラミングを学ぶ人にとっては非常に理解しにくいところがあると思います。*1
さらに、一言でオブジェクト指向、(あるいはもっと範囲を狭めてオブジェクト指向プログラミング)と言っても、プログラミング言語によっていろいろな考え方やアプローチがあり、さまざまな人々がいろいろな解説を試みているので、学習する側の立場としてはどの考え方が正しいのか混乱に拍車がかかるというところがあるかもしれません。
私自身は「オブジェクト指向とはどう考えるべきか」という哲学的な研究をしているわけではなく、単に仕事をするうえで便利な考え方の枠組みの一つとして考えています。要するに、

  • 共通のロジックを流用しやすくする(再利用)
  • 大規模なプログラムを分割して扱い易くする(モジュール化)
  • 変化や拡張に強いプログラムにする(拡張性)
  • 設計のアイデアを共有する(パターン)
  • ビジネスルールをプログラムとしてわかりやすい形で表現する(モデル化)

といった目的を達成するための手段一つとして、欠かせないものとして考えています。
ここでは、Java言語のオブジェクト指向プログラミングの基礎となるクラスとオブジェクトについて、再度、自分なりの解説を試みてみることにしたいと思います。

staticおじさんの世界におけるクラスの役割

一昔前のJavaの解説では、「Javaは純粋なオブジェクト指向言語である」という説明のされ方をされることが多くありました。ご存じのように、Javaではどんなに簡単なプログラムでもクラスを作成することが必須です。実際、HelloWorldは以下のようにクラス内に定義する必要があります。

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, world!");
    }
}

最初から、クラスというオブジェクト指向の概念を使う必要があるから、純粋なオブジェクト指向言語なのであるということです。これは、「純粋なオブジェクト指向」という言葉の定義にもよりますが、一般にはちょっと不適切で勘違いしやすい用法だと思います。確かに、このプログラムに登場する文字列や配列はオブジェクトですが、ユーザー定義のオブジェクトは利用していません。ただし、HelloWorldというユーザー定義のクラスは使っているので、クラス指向といった方がよいかもしれません。
さて、このHelloWorldプログラムと同様の形式を拡張して、すべてのクラスのメンバー(フィールド、メソッド)にstaticキーワードをつけることで、完全にstaticな世界で*2プログラムを作成することができます。staticフィールドやstaticメソッドは、Smalltalkなどの純粋なオブジェクト指向言語の用語をまねてクラス変数、クラスメソッドなどと呼ばれることもありますが、本来staticというのは静的、すなわち、動的(dynamic)と反対の意味を持つ用語です。Java言語において、staticキーワードの持つ意味はあまり理解されていないようですが、staticフィールドについては、一旦クラスローダーによってクラス定義がJVMに読み込まれたら、クラスごとに固定的に変数領域が割り当てられてずっと存在しているということを示しています。また、staticメソッドはそのようなフィールドを操作するためのメソッドとして、やはりクラスごとに定義が読み込まれます。*3
この点を理解するためには、以下のごく簡単な例を考えてみてください。

public class ClassA {
    public static int field;

    public static void method() {
        System.out.pirntln("ClassAのメソッド");
    };
}

public class ClassB {
    public static int field;

    public static void method() {
        System.out.pirntln("ClassBのメソッド");
    };
}

public void MainClass {
    public static void main(String[] args) {
        ClassA.field = 100;
        ClassB.field = 200;

        System.out.println(ClassA.field); // 100
        System.out.println(ClassB.field); // 200

        ClassA.method(); // ClassAのメソッド
        ClassB.method(); // ClassBのメソッド
    }
}

ClassA、ClassBのそれぞれに独立した変数の定義、メソッドの定義が存在し、クラスが読み込まれるとともに静的にメモリーに読み込まれます。このことは、フィールド名やメソッド名が以上の例のように同じであっても影響を受けません。特に、この例で、int型のstaticフィールドに対応するメモリー領域は、クラスがロードされると共にクラスごとに用意されることに注意してください。
このように、staticな世界におけるクラスの役割とは、単にたくさんあるフィールドやメソッドを種類ごとに分類して、適切な部品の単位に分割するというモジュール化の役割があるに過ぎません。つまり、クラスは単にstaticフィールドやstaticメソッドの入れ物として機能しています。また、Javaの場合、基本的には一つのクラスを一つのソースファイルに記述しますから、クラスというのは物理的なソースコードの分割単位としての役割もあります。英語のclassifyに分類分けするという意味があるように、クラスには分類されたものという意味もありますから、大規模なプログラムで必要な変数やメソッドの定義を適切な単位に分割して扱うというのは、オブジェクト指向以前にクラスの持つ重要な役割であることがわかります。

抽象データ型の扱えることの便利さを理解することがstaticな世界を卒業する最初の一歩*4

このように、staticおじさんの世界でも適切にクラスを定義して、フィールドやメソッドをしかるべきクラスに定義することで、大規模なプログラムを適切な単位に分割して管理することができるようになります。しかしながら、この場合の問題は、プログラムが扱える変数やパラメーターの型が基本型、文字列型、またその配列といった非常に限定された型しか扱えないということです。FORTRANやCOBOLなどの昔の言語しかしらないstaticおじさん*5の問題点はプログラム言語が扱えるのはこのような限定された型のデータのみであると決めつけてしまっているところにあると思います。
ところが、実際の複雑なプログラムでは、

  • 注文をデータベースに登録する
  • 入力フォームを開く
  • カタログ一覧を検索する

といったように、プログラムで処理したい単位はintやfloatなどといった基本的な型のデータなのではなく、「注文」「入力フォーム」「カタログ項目」といった人間にとってもっと自然な単位でデータを扱いたいということがあります。JVMの仕組みを考えてみればわかるように、本当はコンピューターが扱いやすいデータというのは有限のビットの集まりとして表現できる数値や文字といったデータなのですが、そういう詳細のことは無視して、データをより抽象化して扱えるようになれば、プログラムを実際のユースケース(仕様)に近い形で記述でき、よりわかりやすく記述できるようになります。実際にJavaの場合は、intやfloatといった基本型の値だけでなく、文字列、日付、注文書といったさまざまなオブジェクトを利用することで目的に応じてあらゆるデータを変数から参照したり、メソッドのパラメータや戻り値としてやり取りできるようになります。
もちろん、オブジェクトを真に使いこなせるようになるには、カプセル化やポリモーフィズムといったさまざまな事柄を理解する必要があるのですが、いきなりそのような概念を理解しようとせず、まずはこのようなさまざまなデータを扱えることの便利さを理解することが、staticな世界を卒業する最初の一歩として重要なのではないでしょうか。

一般のJavaのオブジェクトを理解する前に、まず配列オブジェクトの性質を理解するとよい

ところで、一般のオブジェクトはクラスを使って自分で定義し、newを使ってメモリーに割り当てて使うわけですが、最初に勉強する際に、オブジェクトの性質と同時にクラスの定義方法を学習するのは、一気にいろいろな概念を覚えなくてはならずなかなか難しいかもしれません。そこでお勧めなのは、最初に配列の性質をきちんと理解するという学習手順です。配列はクラスで定義する必要がないため特殊ですが、Javaの配列は

  • new演算子を使ってオブジェクトがJVMのヒープと呼ばれるメモリー領域に動的に生成される。
  • 配列型変数は参照型の変数である。
  • 多数の値をまとめて扱う抽象データ型の一種である。

という性質を考えると、一般のオブジェクトと共通の性質を備えていることがわかります。
一般的な入門書の説明では、配列は複数の値を読み書きする箱が並んだ絵が説明に使われることが多くあります。一個の変数が値を格納する箱なので、これが連続して横に並んだものが配列というわけです。これは、昔のBASICやC、COBOLなど多くの言語の配列の説明には最適ですし、この説明自身は間違っていないのですが、これだけではJavaの配列が持つ以上の重要な性質を見逃してしまうことになります。

int[] x = new int[3];
int[] y = x;

x[0] = 1;
x[1] = 2;
x[2] = 3;

System.out.println(y[0]); // 1
System.out.println(y[1]); // 2
System.out.println(y[2]); // 3

以上のコードは理解している人にとっては当然のことですが、配列オブジェクトは一個しか生成されておらず、xとyという二つの別々の変数によって同一オブジェクトが参照される状態となっているため、変数xの要素を変更すると、連動してyの参照する値も変わって見えるということです。これは、独立した値の箱として扱われる普通のint型変数の振る舞いと大きく違っています。

int x = 1;
int y = x;

x = 2;

System.out.println(y); // 1のまま

もちろん、参照型の変数はオブジェクトを参照するためのものなので、nullという特別な値が代入されることで何もオブジェクトを指していない状態にもなれます。この状態の変数にアクセスすると、有名なNullPointerExceptionが発生することになります。
なお、C言語そっくりにするため、Javaの配列を宣言する際には中括弧の初期化子を使って宣言することも可能です。

int[] x = {1, 2, 3};

あるいは、Javaでは推奨されませんが、以下のように書くこともできて、これだと本当にC言語のように見えます。

int x[] = {1, 2, 3};

ただし、似ているようにみえるのは見かけだけで、C言語の配列とはメモリーの割り当て方が全く異なることに気を付けてください。このように、初期化子を使った場合でも、実際にはnewを使って動的に生成した場合と同様に配列オブジェクトは常に実行時に動的に生成されてメモリーに確保されます。*6
独自のメソッドを定義できないなど制約もありますが、配列は実際にオブジェクトの一種です。まずは、この配列の意味や配列変数の使い方に習熟することで、動的にオブジェクトを生成して使うというstaticでないプログラミングの世界への第一歩を踏み出すことができます。

ユーザー定義のオブジェクトを定義して生成する手段としてのクラス

以上で説明した、動的な配列オブジェクトの生成と配列型変数によるオブジェクトの参照ということが理解できたら、一般のオブジェクトを理解することも難しくありません。配列との違いは、以下の点です。

  • クラス内部にメンバー(フィールド、メソッド)を自由に定義できる。
  • オブジェクトを生成するもとになったクラスがそのオブジェクトを参照するための変数の型となる。

配列の場合は、同一の型の値の集合しか表現できませんが、クラスを使えば、任意の値の組み合わせからなる構造のオブジェクトを生成できるようになります。

public class Person {
    public int age;
    public String name;
}

Person personX = new Person();
Person personY = personX; // 二つの変数が同じオブジェクトを参照するようになる

personX.age = 20;
personX.name = "test";

System.out.println(personY.age); // 20
System.out.println(personY.name); // test

フィールドを自由に定義できることと、要素にアクセスする際の演算子がピリオドである点を除けば、配列オブジェクトとまったく同様の性質を持っていることがわかります。要するに、Java言語においてオブジェクトとは、ヒープに割り当てられたメモリ領域で、オブジェクト生成元のクラスを型として持つ変数によって参照されるものということになります。まずは、データの塊がヒープに生成されて、変数を使ってそれを参照して使うということをイメージできるようになることが何よりも大切です。つまり、Javaのオブジェクト指向プログラミングを理解する前提としては、

  • 変数の宣言とは独立して実行時にオブジェクトが生成される
  • newされるたびに新しいオブジェクトが生成されるので、同じ型でも複数のオブジェクトが存在する
  • 変数は生成したオブジェクトを参照する
  • 変数が何も参照しない場合はnullという値を持つ
  • 変数の数とヒープメモリー上のオブジェクトの数は一般に一致しない
  • 代入によってオブジェクトのコピーは作成されず、複数の変数が同一のオブジェクトを参照するようになる。

という性質をしっかりと理解することが重要だと思います。クラスがロードされたタイミングで最初から用意されるstaticなメモリ領域とは異なり、newされるたびに新しいオブジェクトが動的にヒープに割り当てられていきます。
ところで、どうしてJavaのオブジェクトには、このような性質があるのでしょうか。これも、配列の性質を考えてみれば納得ができると思います。配列は一般にはたくさんの要素を含むので、あらかじめ静的に変数の領域を確保したり、メソッドが呼び出されるつどメモリーを毎回スタックに割り当てるのはあまり効率的とは言えません。実行時に必要なサイズで動的にメモリーを割り当てて、必要がなくなるまで(ガーベッジコレクションされる)ヒープに確保して使うというのは、一般的なプログラムでは効率的と考えられます。さらに、変数への代入時やパラメーターの受け渡し時にすべての値のコピーを行うよりも、単に既にメモリー上にあるオブジェクトを変数が参照するようにした方が、プログラムの実行性能からも有利です。他の言語では、さまざまな方法でオブジェクトを割り当てることができるものがありますが、Javaの場合は常に動的にヒープに割り当てて、変数から参照するというという決まりになっています。常に、最適というわけではないかもしれませんが、JVMのガーベッジコレクションの仕組みと合わせて、この割り切った仕様により、Javaでは効率的にオブジェクトを扱えるようになっていると考えることができます。

オブジェクト指向プログラミングは、オブジェクトを定義するクラスとモジュールとしてのクラスを同一視することから始まる

前節で説明したクラスは、他の言語では構造体、レコード、あるいはユーザ定義型と呼ばれるものに過ぎず、複数の値から構成されるデータ構造を定義していました。
しかし、ここで本当に理解すべき重要なことは、Javaではオブジェクトの構造を定義して、そこから生成されたオブジェクトを参照する変数の型となるためのクラスと、「staticおじさんの世界におけるクラスの役割」の節で説明した、大きなプログラムを分割するモジュールとしての役割を持つクラスを同一視しているということにあります。
まず、Javaにおいて、この事実が当たり前と思えるようになれば、相当オブジェクト指向で考えられるようになっていると言えます。これは、Javaで適切な単位でクラスを設計し、オブジェクト指向でプログラミングできるようになるための大前提であり、オブ脳の基本回路が脳に形成されたといっていいでしょう。
この同一視により、カプセル化という考え方も自然に理解できると思います。つまり、モジュールとして何を一緒に含めるのが自然であるかということを考えれば、オブジェクトに含まれるデータに関連する処理をメソッドとして一緒にクラスに定義し、フィールド自体はprivateにして外部から勝手に操作されないようにする考え方も納得がいくのではないでしょうか。String型のオブジェクトに含まれる文字列を加工するためのメソッドはStringクラスに定義されているので、文字列オブジェクトに対してそのまま呼び出すことができます。一般的にはこのように関連するデータと処理を同じクラス内に定義することが基本となります。
どうすれば、オブ脳が鍛えられるのでしょうか。こればかりは、JDKやオープンソースのライブラリーをお手本として、また自分なりに似たような設計を試しながらプログラミングの経験を積むことに尽きると思います。

まとめ

ここでは、一般の入門書にあるような説明とは違ったアプローチで、Javaのクラスとオブジェクトについて理解するための説明を試みてみました。

  • オブジェクトを考えない世界(staticな世界)ではクラスは単にモジュール化の手段
  • オブジェクトとは動的にメモリーに割り当てられて変数から参照される値の集合
  • Javaの配列はオブジェクトの一種。一般のオブジェクトを理解する前に、メモリーへの割り当てや変数からの参照など配列の性質を完全に理解するのが近道
  • オブジェクトは変数から参照したり、メソッドのパラメーターや戻り値として簡単に受け渡すことができる
  • 一般のオブジェクトを定義して、生成するためにはクラスを利用する
  • オブジェクトを定義して生成する手段してのクラスとモジュール作成手段としてのクラスを同一視することがJavaにおけるOOPの始まり

実際には、最後の項目はかなり内容を端折っているので、クラスを有効なモジュールとして設計するためには、カプセル化の他に継承やポリモーフィズムなどさまざまなことを理解する必要があります。
いまさらですが、職業Javaプログラマーなら理解しておいてほしい「継承」の意味について - 達人プログラマーを目指して
さらに、実際の問題を上手に分割してクラスに割り当てるということができるためには、やはり、それなりの経験を積んで勘を養う必要があります。*7そして、クラスをオブジェクトと結びつけて自然な単位として扱うのがなぜよいのかといったことを納得するには時間がかかるでしょう。しかし、プログラミング言語としてのオブジェクトやクラスの意味や存在意義はここに書いたように非常に単純なものに過ぎないということもできます。
もちろん、オブジェクト指向をプログラミング言語で実現する方法はさまざまですし、Rubyなど、より純粋な言語を使って学ぶべきであるという意見もあるかもしれません。しかし、大部分のJavaプログラマーにとっては、モジュール、配列、構造体といった言語の仕組みの自然な拡張として、クラスやオブジェクトを理解するというパスがわかりやすいのではないかと思いますが、いかがでしょうか。乗り物や動物を使った説明や、逆に専門的すぎて難しい解説を読んでよく理解できなかったという人は、まずは、ここに書いたようにプログラミング上の当たり前の考え方との比較から徐々に理解するのがよいのではないかと思います。そして、この基本が理解できたら、お手本となるソースはいくらでも転がっているのですから、オブジェクト指向の良いクラスライブラリーを読んだり、拡張したりすることに是非挑戦してみてください。

*1:ただし、オブジェクト指向プログラミングを動物や乗り物で解説するのは和書だけでなく、世界的にも一般的な傾向のようです。

*2:ただし、厳密には文字列や配列オブジェクトは除く

*3:勘違いしやすいところですが、メソッド定義はstaticでもそうでなくても静的にクラスごとに読み込まれます。ただし、staticでないメソッドは暗黙のthisオブジェクトを受け取ることで、動的なインスタンス変数にアクセスできる点が違います。

*4:抽象データ型や参照はオブジェクト指向言語に限らず重要だと思います。スクリプト言語のPerlでも、バージョン5から参照が使えるようになって随分と高度なプログラミングが可能になっていますし、このあたりは本格的なプログラミングには欠かせないツールなのだと思います。

*5:本家のstaticおじさんはC#やVisualBasic使いなので、適切でなかったかもしれません。ここではオブジェクト嫌いなおじさんという意味の一般用語として使っています。

*6:重要な違いとして、C言語の配列サイズはコンパイル時に決定されますが、Javaの配列は実行時に決められます。

*7:経験を補うものとして、デザインパターンなどがあるわけですが。