Sencha Touch 2の基礎 – クラスシステム その1
前回、前々回とSencha Touch 2のインストール、そしてSencha Cmdを使ったアプリの骨組みの作成を解説しました。ここまで来ればあとはゴリゴリとコードを書いていくだけですが、その前に1つだけ理解しておいたほうがいいSenchaの仕組みがあります。それがクラスシステムです。
僕は大抵、新たな言語を使い始めるとき、サンプルコードやチュートリアルを見ながらとりあえず作ってみる派なんですが、これだけは知っておくべきだったかなと思います。
SenchaはJavascriptというプロトタイプベース言語にクラスという概念を取り入れるため、コードの書き方にいくつかのルールを設けています。これを知らないまま作り始めると、なにかフワフワした感じでアプリを作ることになり、後々応用が効かなくなります。Senchaがなかなかとっつきにくいフレームワークと言われているのは、ちゃんとここを理解せずにコーディングに入る私のような開発者が多いからではないかなぁと思います。
ということで前置きが長くなりましたが、自由自在にSenchaを使いこなすために、クラスシステムを見ていきましょう。
クラスを作る時のルール
全てのクラスはアプリの名前(Namespace)で始まり、グループ名、クラス名とつける
例えばこのように付けることができます。→「AppName.group.ClassName」。ここでのグループは、機能的に似通ったものを総称する名前です。アプリの名前が「MyApp」で、DropdownMenuというクラスを作る場合、「MyApp.menu.DropdownMenu」のように付けます。グループの中にサブグループを作ることもでき、その場合は「MyApp.menu.top.DropdownMenu」のようになります。
クラスはファイルごとに分ける
一つのファイルにクラスは1つだけ定義するようにします。またクラスを2つのファイルに分けて定義することはしません。
ファイル名とクラス名は一致する
クラス名はファイル名と完全に一致しなければなりません。また、クラスの定義はファイルの保存場所(パス)と一致しなければなりません。先ほどの「MyApp.menu.DropdownMenu」は「MyApp/menu/DropdownMenu.js」になります。
例:
Ext.chart.Label→Ext/chart/Label.jsExt.data.writer.Xml→Ext/data/writer/Xml.jsMyApp.field.Password→MyApp/field/Password.js
このルールを設けておくおかげでクラス名からファイルの場所を特定することができます。
クラスの定義
クラスファイルの作成は上記のように行いますが、肝心のファイル(クラス)の中身はどのように作っていくのでしょうか。まず、クラス定義には”Ext.define”関数を使います(Sencha touch 1.xでは”Ext.extend()”で作成していましたが大きく改定されました)。そしてその引数にクラス名とその中身を書いていきます。
Ext.define('クラス名', {中身});
どの言語でもクラスは大抵その属性とメソッドを定義します。Senchaのクラス定義も同じです。今、Personクラスを/My/sample/Person.jsファイルに以下のように定義したとします。
Ext.define('My.sample.Person', {
config: {
name: null
},
constructor: function(config) {
this.initConfig(config);
},
walk: function(steps) {
alert(this.getName() + 'is walking ' + steps + ' steps');
}
});
このようにconfigが属性を定義する場所で、その他がメソッドの定義になります。このPersonクラスはクラスをインスタンス化するときにもしnameを取得出来れば、7行目のinitConfig()で3行目のnameを書き換え、自分のnameに設定します。また、walkメソッドが呼ばれた時は歩いた数を出力します。
ここでいうインスタンス化とは、人間というクラスがあったとすると、人間クラスのトミーさんを作ることです。つまり具体化、具現化です。上記のように書いてもわかりづらいと思うので、以下に使用例を示します。
var tommy = Ext.create('My.sample.Person', {
name: 'Tommy'
});
tommy.walk(100); //alerts 'Tommy is walking 100 steps'
クラスをインスタンス化する際は”Ext.create()”メソッドを用います。そしてname属性にTommyという値を設定しました。これで人間クラスのトミーさんの完成です。5行目がメソッドの呼び出しです。Personクラスのwalkメソッドを呼び出し、引数100を与えています。これにより、walkメソッドのstepに100が代入され、「Tommy is walking 100 steps」と出力されます。「Tommy」を取得している”this.getName()”メソッドの解説は後述します。
とにかくこのように簡単なクラスを定義することができます。
クラスの継承
クラスの継承とは、親となるクラス(super class)の機能を引き継いだ子クラス(subclass)を作ることです。例えば、今「開発者」というクラスを作るとします。開発者はもちろん人間ですから人間クラスの機能、特徴を持っており、更にプログラミングという独自機能を持ちます。このようなときに継承という概念を使えば、一から人間クラスの機能を作ることなく開発者クラスを作ることができます。実際にコードを見てみましょう。Personクラスを継承したDeveloperクラスは以下のように書くことができます。
Ext.define('My.sample.Developer', {
extend: 'My.sample.Person',
code: function(language) {
alert(this.getName() + ' is coding in ' + language);
}
});
ここで重要なのは2行目のextendキーワードです。ここに親クラスを定義することで、このクラスがPersonクラスの特徴を引き継いでいることを明記します。これにより、nameという要素やwalkメソッド、初期化のconstructorメソッドを記述しなくても、このクラスはそれらを親から引き継いで使うことができます。したがって、Developerクラスは新たに追加する”code()“というメソッドだけが定義されています。実際の使用例を見てみましょう。
var bob = Ext.create('My.sample.Developer', {
name: 'Bob'
});
bob.walk(50); //alerts 'Bob is walking 50 steps'
bob.code('JavaScript'); //alerts 'Bob is coding in JavaScript'
ボブさんは1行目で開発者クラスとしてインスタンス化されていますが、開発者は人間クラスを継承しているので5行目のようにwalkメソッドが使えます。そして、開発者独自のメソッドであるcodeメソッドももちろん使用出来ます。
このようにSenchaはクラスの継承という概念を取り入れたことで効率的なコーディングをすることができるようになりました。
ゲッタ、セッタの自動生成
クラスの定義、継承の方法がわかった所で、少し話を戻します。walkメソッドを定義した際に”this.getName()”というメソッドを使用し、Personのname値を取得しました。ではいつどこでこの”getName()”メソッドが定義されたのでしょう。実はこれがSenchaクラスシステムの機能の一つで、ゲッタとセッタの自動生成です。
ゲッタ、セッタとはオブジェクト指向の言語でよく見られるメソッドで、それぞれの属性値を取得したり、逆にセットしたりするためのものです。getNameならばnameという属性値を取得するためのゲッタになり、setNameならばnameに新たな値を設定するセッタになります。これらのメソッドの定義は同じような機能を各属性に割り振らなくてはならず、プログラマにとっては面倒な作業になりがちです。
Senchaのクラスシステムはこの面倒な作業を自動で行なってくれます。この作業を行うメソッドがwalkメソッドの上に書かれている”initConfig()“メソッドです。ここで、configキーワード内に定義された属性を認識し、自動でゲッタとセッタを作ってくれます。実際にどのように使われるのかを見てみましょう。
Ext.define('My.sample.Person', {
config: { //初めの属性値の初期設定
name: 'Anonymous',
age: 0,
gender: 'Male'
},
constructor: function(config) {
this.initConfig(config); //ここでゲッタ、セッタを自動生成
}
});
初めのPersonクラスを上記のようなに変更し、ageとgender属性を追加しました。そして以下のようにrobertさんを作成すると、自動でgetAgeやsetAgeメソッドが作られ、操作できるようになります。
var robert = Ext.create('My.sample.Person', {
name: 'Robert', //属性値の上書き
age: 21 //属性値の上書き
});
alert(robert.getName()); //alerts 'Robert'
robert.setName('Rob');
alert(robert.getName()); //alerts 'Rob'
alert(robert.getAge()); //alerts '21'
robert.setAge(22);
alert(robert.getAge()); //alerts '22'
このプログラマーにとって繰り返しになる作業を自動化したことがSenchaクラスシステムの大きな特徴の一つと言えます。
複雑な構造の属性を保つ場合
以下の様なDeveloperクラスが定義されていたとします。
Ext.define('My.sample.Developer', {
extend: 'My.sample.Person',
config: {
level: 'Junior',
location: {
city: 'Unknown',
state: 'Unknown',
country: 'Unknown'
}
}
});
DeveloperクラスはPersonクラスを継承し、追加属性にlevelとlocationを持ちます。またlocation属性の値はオブジェクトになっており、city、state、countryキーワードがあります。
このような場合は以下のようにインスタンス化します。
var edward = Ext.create('My.sample.Developer', {
age: 26,
level: 'Senior',
location: { city: 'Palo Alto', state: 'CA', country: 'USA' }
});
そして属性値は以下のように使用可能です。
edward.setName('Edward');
alert(edward.getLocation().city); //alerts 'Palo Alto'
ここまででクラスシステムの重要な部分は大方説明できたかなと思います。次回は、あまり使うことはないですが、知っておいたほうがいい概念である、アプライヤーとアップデイターのお話。あとはクラスの依存関係などに触れて行きたいと思います。