月別アーカイブ: 2015年12月

Javaのクラス・インターフェース入門 – メイドさんでも分かるプログラミングシリーズ vol.1

プログラミングというのは、カンがいいと、言われたとおりに入力しているだけである程度のことができるようになります。しかし、そういうやり方では「ここはおまじない」「ここは決まり文句」という部分がどうしても多くなってしまいます。

ちょっと訳あって、秋葉原のメイドさんにプログラミングを教えています。そのメイドさんたちはまさにそうで、彼女たちは既にAndroid Studioで画面レイアウトをいじり、画面遷移をするプログラムを書く能力はありますが、「おまじない」「決まり文句」の多さに困惑しているように思います。

そこで、今日から「メイドさんでも分かるプログラミング」シリーズを初め、プログラミングの基礎をやさしく説明していこうと思います。

読者のレベルとしては、PCを日常的に使いこなしていているが、プログラミングの専門教育を受けていないという想定です。

なお、あらかじめ、次の記事レベルのJavaの基礎知識は学習済みであることを前提とします。

プログラム初心者に贈るJavaプログラミング - Qiita

では、早速初めます。

第1回は、クラスとインターフェースについてです。

クラスってなに?

Javaのプログラムを書くと、必ずこんな「おまじない」を書いてくださいと言われたと思います。

public class Hello {
    // ....(省略)
}

「パブリック」「クラス」と読みます。何でしょうこれは?

実はJavaのプログラムは「クラス」の集まりとして定義する「オブジェクト指向言語」なのです。

「オブジェクト指向」???

また新しい言葉が出てしまいました。

大丈夫です。これからゆっくり、説明します。

オブジェクト指向とは

「オブジェクト指向」とは、まじめに説明すると本が一冊書けてしまいます。ここに知り合いが書いた本気な資料がありますので、興味があったら読んでみてください。

ざっくり言うと、プログラムを「オブジェクト」という塊の集まりで作成します。「オブジェクト」とは「モノ」です。ここで、ちょっと日常世界を見てください。あらゆるモノがありますよね。

例えば、次の絵では、テーブルの上にコーヒーが淹れてあります。コーヒーはコーヒーカップに注がれていて、ソーサーの上に乗っています。スプーンも付いています。テーブルは木製ですね。金属の脚が付いています。「コーヒー」「カップ」「ソーサー」「スプーン」「テーブル」これらすべてがオブジェクトです。しかも「テーブル」は「木の板」「金属の脚」などの部品から構成されています。部品もオブジェクトです。

このように日常世界はあらゆる「モノ」の集まりで構成されている

このように日常世界はあらゆる「モノ」の集まりで構成されている

オブジェクト指向では、このように、あらゆる物に注目していきます。そうすると、一つ一つの「モノ」はシンプルに表現することができます。「カップ」はコーヒーを注ぐことができればいい。「スプーン」はお砂糖を掬うことができれば良いといった機能に特化することができるからです。

なぜそんなことをするかというと、昨今のコンピュータプログラムはとても複雑だからです。もし、すべてを一つのプログラムで書いていたら、神様のようにもの凄く巨大な存在になってしまいます。人間に神を作ることはできません。オブジェクト指向プログラミングがあるお陰で、複雑なプログラムも部品の組み合わせとして簡単に作れるというわけです。

クラスとインスタンス

さて、オブジェクトは「クラス」と「インスタンス」に分かれます。クラスはオブジェクトの設計図、インスタンスは設計図から作られたオブジェクトの実体です。一つの設計図からたくさんの実体を作ることができます。

これは、たい焼きモデルで説明されることがあります。

たい焼きモデル

さて、Javaはこのうち「クラス」の部分をプログラミングして行きます。プログラムの初めに public class と書いているのはそのためです。

実際にプログラムが動く時には「インスタンス」の方が使われます。「クラス」から「インスタンス」を作るためには次のように書きます。

new クラス名();

作ったクラスは通常、次のように書いて「変数」に格納して使います。変数は覚えていますよね?

クラス名 変数名 = new クラス名();

あれ、クラス名を2度書いていますね?実は、左側のクラス名は正しくは「型名」と言います。変数には「型」がありました。例えばこんな型です。

意味
int 整数
long 大きな整数
double 浮動小数点数
boolean true or false
Object オブジェクトインスタンスを格納
クラス名 そのクラスに属するオブジェクトインスタンスを格納

オブジェクトインスタンスを格納する変数型としては「Object」という型が存在します。なので、次のように書いてもエラーにはなりません。

Object 変数名 = new クラス名();

しかし、通常「Object型」は使用しません。それはなぜでしょうか?変数の型は、プログラミング言語に「その変数は何ができるのか?」と教える印なのです。つまり、

Object 変数名 = new クラス名();

というプログラムは「その変数はモノです」と言ってるに過ぎないのです。

「モノ」であることは分かっています。でも「モノ」と言われても、何ができるのかわからないですよね。「自動車」も「鍋」も「コーヒー」も、「人間」さえも、「モノ」ですから。

そこで、変数名の左にクラス名を書くことで、それが「モノ」の中でも何なのかを教えてあげているのです。

自動車 mycar = new 自動車();
mycar.走れ(); // これはOK

Object mycar = new 自動車();
mycar.走れ(); // 「モノ」が「走る」とは限らないからエラー

フィールドとメソッド

モノの設計図であるクラスは次のようにプログラミングします。

class クラス名 {
    フィールド定義;
                :
                :

    メソッド定義
                :
                :
}

「フィールド」とは、クラスに属する変数のことです。Java以外の言語では「メンバー変数」ということもあります。そして「メソッド」はクラスに属する関数のことです。同様に「メンバー関数」と呼ぶこともあります。

ざっくり言うと、クラスの定義は「フィールド」と「メソッド」を定義するだけです。それでどうしてモノの設計図になるのでしょうか?

状態と振る舞い

モノの概念に立ち戻ってみると、モノは「状態」と「振る舞い」という概念を持ちます。

「状態」というのは、年齢とか、職業とか、モノそれぞれによって、あるいは同じモノでもその時々によって変化する情報です。

「振る舞い」というのは、モノの外側から見える動作です。

ティーカップを例にすると、次のような状態と振る舞いがあります。

  • 振る舞い

    • 液体を注ぐ
    • 注いだ液体を飲む
  • 状態

    • 汚れの有無
    • ヒビの有無
    • 今注がれているモノ

カップのユースケース図

カップの状態

そして、Javaのフィールドは「状態」を、メソッドは「振る舞い」を表現しているのです。

ねこ様をプログラミング

それでは、ニャーと鳴くねこ様をプログラミングしてみましょう。

このねこ様は外部から見ると、こんな振る舞いをするようです。

ねこ様ユースケース図

ここで、ねこ様は「鳴け」「寝ろ」「起きろ」なんていう人間の命令を聞かないとは思わないでください。コンピュータプログラミングというのは、日常世界全てを表現する必要はありません。今、自分が必要な範囲だけプログラミングすれば良いのです。

ねこ様の状態として「寝ている」という状態があるとします。このプログラムでは、寝ている時と、起きている時で鳴き方が違うということを表現します。

以下がプログラミング例です。

/** ねこ様 */
class Cat {
    /** 寝ているかどうか。true:寝ている, false:起きている */
    boolean slept;
    /** 鳴け */
    void mew() {
        if (slept)  {
            System.out.println("ふにゅあ")
        } else {
            System.out.println("にゃー")
        }        
    }
    /** 寝ろ */
    void goToSleep() {
        slept = true;
    }
    /** 起きろ */
    void wakeUp() {
        slept = false;
    }
}

早速、ねこ様クラスを使ってみましょう

Cat tama = new Cat();
tama.mew();
tama.goToSleep();
tama.mew();
tama.wakeUp();
tama.mew();

実行結果

ニャー
ふにゃあ
ニャー

※ 最初のmewは「ニャー」となっています。これは、sleptフィールドの初期値がfalseであることを意味しています。このようにフィールド変数は、初期値が決められています。

初期値
数値 0
boolean false
オブジェクト null

アクセス修飾子

よく見かけるpubicとか、privateとは何でしょうか?これは外部に公開するかどうかを指定する記述です。ざっくり言うと

アクセス修飾子 意味
private 外部に非公開
public 外部に公開

という風に分類されます。他にprotectedというのがありますが、後ほど説明します。

もし、さっきのねこ様クラスをprivate classとして作成してしまうと、別のプログラムから参照できなくなります。(実際は記述は許されません)

では、何でもpublicにしてしまえば便利かというと、世の中何でもかんでも中のことをオープンにしてしまうことは不都合だったりします(笑)

オブジェクト指向プログラミングでは、積極的に、外側から見て不要なものを隠蔽していきます。そのようなテクニックを「カプセル化」と呼びます。

常に当てはまるわけではありませんが、「メソッド」は公開し、「フィールド」は非公開するのがセオリーです。もし、フィールドの値を直接確認したい場合は、getフィールド名()setフィールド名()という「メソッド」を作るのが作法とされます。こういったメソッドを「アクセッサー」と呼びます。

アクセッサーの例

public class Human {
    private String name;
    private int age;
    public void setName(String name) {
        this.name = name;
    }
    public void getName() {
        return name;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public int getAge() {
        return age;
    }
}

メソッドもまた「非公開」にすることがあります。これは、外部からの振る舞いといよりも、プログラミングの都合上で共通部分を抜き出した場合などに、そのようにします。

/** 名前を教えると挨拶してくれるマシーン */
public class HelloMachiene {
    private String firstName;
    private String lastName;
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    // 姓と名をくっつけて使うことが多いので共通化
    private String getFullName() {
        return lastName + firstName;
    }
    public void sayHello() {
        System.out.println("こんにちは" + getFullName() + "さん");    
    }
    public void sayGoodBye() {
        System.out.println("さようなら" + getFullName() + "さん");    
    }
}

コンストラクタ

new クラス名()

と書いた時にプログラムを動かして、フィールドを初期化したいことが良くあります。このようなときに使うものとして「コンストラクタ」というものがあります。

コンストラクタはクラス内に次のように記述します。

public クラス名() {
    // 初期化プログラム
}

メソッドと似ていますが、返り値の型がありません。メソッドは

public 返り値の型 メソッド名() {
    // プログラム
}

でした。ところで、コンストラクタは、メソッドと同じようにパラメータを持つことが出来ます。上手く使うとプログラムを完結に書くことが出来ます。

public class Human {
    private String name;
    private int age;
    public Human(String name, int age) {
        this.name = name;
        this.age = age;    
    }
    public String toString() {
        return name + "(" + age + ")";
    }
}

使用例

Human satoshi = new Human("サトシ", 10);
Human okido = new Human("オーキド", 55);
System.out.println(satochi);
System.out.println(okido);

結果

サトシ(10)
オーキド(55)

※toStringという名前のメソッドを作って文字列が必要な場所で使用すると、toStringを省略してもtoStringが呼ばれます。これはJavaの仕様です。つまり

System.out.println(satochi.toString());

と書いても同じ結果になります。

継承・多態性

新しいことが次々に出てきて大変ですね!でも、もう少しでオブジェクト指向の一番大事なところを知ることが出来ます。大変ですが、どうか我慢してください。

クラスを作っていくと、同じようなクラスができてくることに気が付きます。そして、それらを束ねたいことがあります。その時に使用するのが「継承」というしくみです。

例えば、「ニャー」と鳴くねこ様のほかに「ワン」と鳴く犬クラス、「モー」と鳴く牛クラス、「ブヒー」と鳴くブタクラスを作ったとします

鳴け!

class Cat {
    public makeSounds() {
        System.out.println("ニャー");
    }
}
class Dog {
    public makeSounds() {
        System.out.println("ワン");
    }
}
class Cow {
    public makeSounds() {
        System.out.println("モー");
    }
}
class Pig {
    public makeSounds() {
        System.out.println("ブヒー");
    }
}

※英語とは面倒なもので、ねこはmew, 犬はbarkのように「鳴く」という動詞が異なるため、音を鳴らすというmakeSoundsで統一しました。

あるメソッドがあって、動物を鳴かせたいとします。しかし、ねこを鳴かせるメソッドを使って、犬を鳴かせることは出来ません。

void makeSounds(Cat cat) {
    cat.makeSounds();
}
Cat tama = new Cat();
Dog pochi = new Dog();
makeSounds(tama); // これはOK
makeSounds(dog); // これはダメ

これでは不便です。なぜなら、CatもDogも、makeSoundsというメソッドを持っているからです。

そこで、そこで、次の2つの考え方があります。

考えた方1: 犬もねこも「鳴く」ことができる

外部から見て、共通の振る舞いを持っているものは、「インターフェース」というしくみで束ねることが出来ます。

インターフェースは次のように定義します

interface インターフェース名 {
    返り値の型 メソッド名();
    返り値の型 メソッド名();
        :
        :
}

中身の無いメソッドの名前だけを並べて行くのです。

そして、クラスを作るときにimplementsというキーワードでインターフェースを指定すると、そのクラスのインスタンスは、そのクラス型の変数の他に、そのインターフェース型の変数にも代入することが出来ます。

難しいのでアンダーラインを引きました。深呼吸して、この部分を5回読んでみてください。

あせらないで!ゆっくりで良いですよ!

5回読みましたか?

では、具体例を書きますね。

class Cat implements SoundsMaker {
    // ...
}

として定義したら、こんな書き方がOKになります。

SoundsMaker tama = new Cat();

そして、次のプログラミングコードが成立します。

interface SoundsMaker {
    void makeSounds();
}
class Cat implements SoundsMaker {
    public makeSounds() {
        System.out.println("ニャー");
    }
}
class Dog implements SoundsMaker {
    public makeSounds() {
        System.out.println("ワン");
    }
}
class Cow implements SoundsMaker {
    public makeSounds() {
        System.out.println("モー");
    }
}
class Pig implements SoundsMaker {
    public makeSounds() {
        System.out.println("ブヒー");
    }
}
public makeSounds(SoundsMaker soundsMaker) {
    soundsMaker.makeSounds();
}
Cat tama = new Cat();
Dog pochi = new Dog();
makeSounds(tama);
makeSounds(pochi);

実行結果

ニャー
ワン

インターフェース版鳴け!

このテクニックを「インターフェース継承」あるいは「インターフェース抽出」と呼びます。

考え方2:犬もねこも「動物」である

犬も、ねこも、牛も「動物」という共通の概念を持っています。そういう共通の概念をスーパークラスと呼びます(親クラスとも呼びます)。反対は「サブクラス」(子クラス)です。

Javaではextendsというキーワードでスーパークラスを指定します。

class Animal {
    void makeSounds() {
        System.out.println("???");
    }
}
class Cat extends Animal {
    public makeSounds() {
        System.out.println("ニャー");
    }
}
class Dog extends Animal {
    public makeSounds() {
        System.out.println("ワン");
    }
}

インターフェースとの違いは、スーパークラスはあくまでクラスであるということです。

継承版鳴け!

new Animal()

と書いてしまうことも出来ます(禁止することも出来ますが今は割愛します)。またフィールドを持ったり、メソッドのプログラムを記述することも出来ます。そのようにすることで、共通メソッドなどを作ることも出来ます。

このテクニックを「クラス継承」、または単に「継承」と呼ぶことがあります。

どっちを使ったら良いの?

もし、振る舞いを束ねたいだけ場合は、インターフェースを使用してください。でも、インターフェースはめんどくさいこともあります。特にたくさんのメソッドを束ねるときには向いていません。また実装を持つことが出来ないため、コピーペーストが増えてしまいます。

スーパークラスは、一つしか持つことが出来ません。つまり、安易にスーパークラスを使うと後で後悔することになります。

どんなときにクラス継承を使うか、インターフェースを使うかなどを指南するものの一つとして、「デザインパターン」というテクニック集が存在します。このシリーズの中でも紹介して行きたく思います。

多態性(ポリモーフィズム)

「多態性」とは聞き慣れないキーワードですね。しかし、これこそが、オブジェクト指向の真髄なのです。しかも、既にあなたはこのテクニックを使っています!

先程の例で、SoundsMakerインターフェースを使った例でも、Animalスーパークラスを使った例でも、どちらでも結構です。

あるところに

SoundsMaker m;

という変数がありました。そして、紆余曲折を経てmには何か値が代入されています。その後

m.makeSounds();

と書いてあったとします。実行結果は次のうちどれでしょうか?

  • ニャー
  • ワン
  • ブヒー
  • モー
  • ???

正解は「わからない」です。

インターフェース版鳴け!?

引っかけでごめんなさい。そうなのです。その時点で、どのように動くのか、実行してみないと分からないという柔軟なプログラミングが出来るのです。これが出来ると、プログラミングが大変便利になります。

とにかく「鳴く」ことが出来るものを(実際にどんな鳴き声かは気にせず)一緒くたに全部束ねてプログラミングしておいて、後から「鳴く」何かを当てはめるというプログラミング手法が実際には非常によく行われます。

そうすれば、たったひとつのプログラムで何通りものプログラムが作れてしまうからです。

インラインサブクラス

ここまで読めたあなたはもうオブジェクト指向マスター一歩手前です。(おめでとうございます!パチパチ)

最後に、先ほどの多態性を超強力に使えるプログラミングテクニックを紹介します。

ここまでの復習でオブジェクトとは何かを振り返ってみましょう

  • オブジェクトの設計図として、クラスを定義する
  • クラスはメソッドとフィールドを持つ
  • クラスはインターフェースまたは別のクラスを「継承」することができる
  • 継承元のインターフェースやクラスを型とした変数には、「継承」先のサブクラスのインスタンスを代入することができる

ここで「継承先のサブクラス」というのは、実は、いつでも作ることができます。いちいち、javaファイルを作って、名前を決めてextendsって書いたりせずに、欲しくなったらいきなり書いてしまうことが出来るのです。

先ほどの、SoundsMakerインターフェースを使った例を元に話を進めますね。

SoundsMaker s = new SoundsMaker() {
    public void makeSounds() {
        System.out.println("ぎゃああああああ");
    }
};
makeSounds(s);

実行結果

きゃああああああ

こんな風にいきなりプログラムの途中に作られたクラスを、「インラインサブクラス」と呼びます。この技はインターフェースに対しても、スーパークラスに対しても使用可能です。

なぜこんなテクニックが必要かというと、名前を決めることが結構大変だからです。親になった気持ちになってください。その子の名前をいい加減に決められますか?その子の将来がかかっているので、他人から見て変じゃないかとか、縁起が良いかとか、あれこれ考えてようやく名前が決まります。

プログラミングしていて圧倒的に多くの時間を費やすのが「名前決め」です。ですから、一度しか使わないような子には、名前を与えずいきなり使ってしまえば良いのです。(笑)

変数すら省略できる

オブジェクトインスタンスは変数に格納すると書きました。しかし、その場で一回だけ使えば良いなら、なにも変数すらも使わなくても良いのです。

new Cat().makeSounds()

実行結果

ニャー

これらの技を組み合わせると、こんなコードも書けてしまいます。

new SoundsMaker() {
    void makeSounds() {
        System.out.println("ぎょええ~~~")
    }
}.makeSounds();

分かりますか?「ぎょええ~~~」と鳴くモノを作って、その場で鳴かせているのです。

なぜこんなことを書いたかというと、次回以降で多用するからです。


テンプレートエンジンMayaaについて知りたいすべての人へ

これは Mayaa Advent Calendar 2015 の25日目です。昨日は「Mayaaソースコードの読み方」でした。

マヤー(メリー)・クリスマス!

今日はクリスマスです!Mayaaアドベントカレンダーは本日が最終日です。

結局最後まで一人で書きました。はじめは心細く、4,5日目あたりが一番つらかったです。今日まで続けてこれたのは、TwitterやFecebookで「いいね」をしてくれたり、リアルで応援をしてくれた皆さんのお陰です。

ここまでを振り返って

Mayaaアドベントカレンダーを通じて、自分の持っているMayaaのノウハウを放出することで、世界で一番詳しいMayaaのノウハウ集をアウトプットしようと考えていました。

振り返ってみると、コーディングルールだったり、フレームワークとの連携だったり、拡張方法だったり、教育や、ソースコードの読み方など、本当にあらゆることを書けました。

そこで、今日はここまで書いたこと、及び、公式ドキュメントや、このブログの過去の記事、別の方の記事などをインデックス化することで、Mayaaについて調べる時のポータルページを作りたいと思います。

この、世界一詳しいMayaaのノウハウ集を、皆さんへの本当のクリスマスプレゼントとして捧げたいと思います。

Mayaaとは何か知りたい

プログラマー向け

Mayaaの使い方を覚えたい

アーキテクト・上級プログラマー向け

ここから先は、Mayaaをどのように活用するかという話になります。

Mayaaを拡張・チューニングしたい、トラブルシューティングしたい

プロジェクトマネージャ、リーダー向け

Mayaaを使ったプロジェクト運営論

豆知識系

まとめ

25日間お疲れ様でした。
いかがでしたでしょうか?

ブログを毎日書くのは初めての体験で雑なところなどもあったと思います。半ば書ければいいやなどの投げやりな気持ちも1mmくらいはあったと思います。

しかし、今時派手ではない、テンプレートエンジンのノウハウはこうでもしないとアウトプットされないでしょうから、この機会があって良かったと思います。

今後僕は、あらゆる場面でこのアドベントカレンダーの一覧の記事を活用していこうと思います。その際にあらに気づいたら自ら直して行くことでしょう。

つまり、世界一詳しいMayaaノウハウ集づくりは、ここが起点なのです。Mayaaが続く限り、将来にわたってこれらの記事をメンテし続けることを約束します。

それではみなさん、メリークリスマス、そして良いお年を!


Mayaaソースコードの読み方

これは Mayaa Advent Calendar 2015 の24日目です。昨日は「Mayaaの学び方・教え方」でした。

今日はクリスマスイブです!リア充が爆発している中、硬派な皆さんはMayaaアドベントカレンダーを読んでくださりありがとうございます。

クリスマスプレゼントとして、今日は徹底的にガチなことを書きます。

Mayaaのソースコードの読み方

MayaaのソースコードはGitHubにあります。

さて、git cloneで落としましたか?

今日はこいつの読み方を説明します。準備は良いでしょうか?

プロジェクト構成を眺める

プロジェクトフォルダをざっと見ると、

  • src-api
  • src-impl

2つのフォルダがあります。僕はこのようなフォルダ構成はMayaaしか知りません。試しにGoogleで「src-impl」と打ち込むと、Mayaaのソースが出てくるからMayaaの独特の構成かもしれません。

contect フォルダはライセンス情報や依存ライブラリ、Webアプリとして単体起動させるための設定情報などがあるだけで、ライブラリとしてあまり重要なものはありませんので、コードリーディング時は無視してよいです。

src-apiでAPI構成を知る

src-apiにはinterfaceが定義されています。つまり、src-apiに存在するインターフェースは何らかの方法で実装を交換可能になっているということです。

実装が交換可能ということは、独立した部品とみなされていると言って良いです。つまり、src-apiをざっと見れば、Mayaaがどんな部品によって構成されているかを知ることが出来ます。

パッケージ階層を見ると次のような構成であることが分かります。

  • builder
  • cycle
  • engine
  • provider
  • source

Mayaa内部の用語なので、これだけ見ても、最初は慣れないと思いますが、そこで、Mayaaの処理する流れをざっと説明します。

engineはMayaa中心で起点と考えてよいです。

engineの中は、processorと、specificationに分かれます。specificationはmayaaファイルやテンプレートの定義情報をモデル化したものです。processorは、テンプレートエンジンがそのノードをどのように処理するかをオブジェクト化したものです。つまり、specificationによって組み立てられたprocessorがあればMayaaエンジンはHTMLを出力することが出来ます。

providerはアプリケーションスコープのリソースです。エンジンの設定や、各インターフェースの実装クラスの選択はここが担当します。そのため、Mayaaの設定はorg.seasar.mayaa.provider.ServiceProviderなのです。

sourceは、mayaaファイルやhtmlテンプレートなどユーザーが作成するソースファイルを抽象化しています。他のインターフェースに依存しませんが、実装上は、ビルド時にほぼ必ず使用します。

cycleは、リクエスト処理のコンテキストを扱います。リクエスト・レスポンス、現在処理中のノードといったスレッドローカルなコンテキストはここに格納されていきます。

builderは(主にsourceを元にして)SpecificationNodeを組み立てます。

Mayaaの構成

つまり、provider, cycleは事実上Engineを動かすのに必要ですが、何らかの方法でspecification/processorが構成できてしまえば、builder, sourceは切り離せるとも言えます。

継承の起点としてのインターフェースを理解する

次に、クラス階層から実装の作法を理解してみましょう。

こんなクラスが実装の起点に存在しています。

  • PositionAware

    • NodeTreeWalker
    • ParameterAware
  • ContextAware
  • UnifiedFactory

これらをざっと眺めておくと後々実装を追いやすくなります。

実装クラスの起点はMayaaServletから

なんだかんだ言っても実装はMayaaServletから追い始めるのがベターです。

MayaaServlet#doServiceで、Cycleを初期化し、ProviderからEngineを取得して実行しているのが分かります。

EngineImpl#doServiceを追って行くと、EngineImpl#doPageServiceという大きなメソッドに行き着きます。ここが、一リクエスト分のレンダリング処理の主要部分です。ここを起点に、テンプレートの情報を取得し、各部品を実行することで、結果としてのHTML文書が出力しされます。

大枠を理解できたら後は部品ごとの設計を理解する

例えば、SourceDescriptorは内部でCompositeパターンになっています。Processorを理解するには、TemplateProcessorSupportという巨大なクラスの機能を理解する必要があるでしょう。

また、Rhinoスクリプティング機能は、cycle.script.rhino内に収められています。したがって、これと同等の部品を実装して、独自のCompiledScriptが実装ができれば、スクリプトエンジンをRhino以外に切り替えることもできます。

まとめ

Mayaaのソースコードは、各部品の独立性と抽象度が高く設計されています。このため、全体を各部品に切り離して考えることが出来、一部分を拡張したり、変更することが行いやすくなっています。

FactoryFactoryがある時点で、DIコンテナが普及する以前の香りがしますね。Seasarプロジェクトと言いながらSeasarに依存しないのは、このように独自にフレームワークを作っているからなのです!また、今回は触れませんでしたが、内部には、GCの管理や、シリアライザーをガリガリいじったり、相当低レベルのこともしています。(それらを読んでいるととても勉強になります)

こういった低レベルな部分がソースを膨らませているとも言えます。何も知らないで読むと、溺れてしまいそうです。しかし、フレームワーク的なところ、低レベル過ぎるところは一旦置いておくと、読むべき範囲は限定されてきます。

ソースコードを読む動機というのは様々で、拡張したい時の他に、トラブルシューティングをするとき、パフォーマンスチューニングしたい時などいろいろあると思いますが、その時必要な範囲で読めるように、普段から慣れておけば、そんなに怖いものではありません。