クラスシステム
このガイドは、Ext JS 4.x や 5.x で、新しいクラスを生成したり、既存のクラスを拡張しようとする開発者を対象とします。
概要
Ext JS 5 には 300 以上のクラスがあります。現在 200 万人以上の開発者が Sencha を利用していて、彼らのバックグラウンドや住まいもさまざまです。 そんな規模では、次の様な条件を満たす、共通のコードアーキテクチャを提供するのは大変難しいことでした。
- 馴染みがあって、覚え易い
- 速く開発できて、簡単にデバッグできて、簡単にデプロイできる
- 整理されて、拡張も可能で、メンテナンスも可能
JavaScript はクラスの無いプロトタイプ指向言語であるため、最も強力な特徴の一つは柔軟性です。どんな問題に対してでも複数の解決方法があり、そこではコーディングスタイルやテクニックの違いがあります。しかし、これよるコストは予測不能です。統一した構造がなければ、JavaScript のコードの理解、保守、再利用が難しくなります。
クラスベースのプログラミングは、オブジェクト指向プログラミングの最もポピュラーなスタイルです。クラスベースの言語では、一般的に強い型制約、カプセル化、標準コード規約が必要です。開発者が広範囲に及ぶ規則に従うことで、作成されるコードは長きに渡って、予想可能で、拡張性が高く、スケーラブルとなります。しかし、このモデルには JavaScript の動的な能力が含まれていません。
それぞれのアプローチには長所と短所がありますが、両方のアプローチの長所を残したまま、短所を隠すことができるでしょうか?その答えは “Yes” で、Ext JS の中にその解決方法が見つかるでしょう。
命名規則
コードの中で、クラス、名前空間、ファイル名に対して、統一した命名規則を使うと、あなたのコードは整理され、構造化され読やすくなります。
クラス
クラス名は、英数字のみを使います。数字は許可されているものの、技術用語でない限り使いません。アンダースコア、ハイフン、その他の英数字以外の文字は使わないでください。例を挙げますと。
MyCompany.useful_util.Debug_Toolbar
はよくありませんMyCompany.util.Base64
は許可されます
必要に応じて、クラス名をパッケージにグループ化し、オブジェクトプロパティのドット記述 (.) を使って名前空間を付けます。少なくとも、一つのユニークなトップレベルの名前空間を用意し、そこにクラス名を続けます。例えば:
MyCompany.data.CoolProxy MyCompany.Application |
トップレベルの名前空間と実際のクラス名はキャメルケースにします。他は全て小文字にします。例えば。
MyCompany.form.action.AutoLoad |
Sencha が提供しているのではないクラスは、トップレベルの名前空間として Ext は使用してはいけません
頭字語も上記のキャメルケースの規則に従います。例えば:
Ext.data.JSONProxy
ではなくExt.data.JsonProxy
MyCompary.parser.HTMLParser
ではなくMyCompany.util.HtmlParser
MyCompany.server.HTTP
ではなくMyCompany.server.Http
ソースファイル
クラス名はクラスが格納されるファイルパスと直接マッピングされます。 その結果、各ファイルには一つのクラスだけあることになります。例えば。
Ext.util.Observable
はpath/to/src/Ext/util/Observable.js
に格納されますExt.form.action.Submit
はpath/to/src/Ext/form/action/Submit.js
に格納されますMyCompany.chart.axis.Numeric
はpath/to/src/MyCompany/chart/axis/Numeric.js
に格納されます
path/to/src
はアプリケーションのクラスのディレクトリです。全てのクラスはこの共通のルートの下に置き、最適な開発、メンテナンス、デプロイのためには、適切な名前空間をつけるようにします。
メソッドと変数
- クラス名と同様に、メソッドや変数の名前も英数字のみを使います。数字は許可されているものの、技術用語でない限り使いません。アンダースコア、ハイフン、その他の英数字以外の文字は使わないでください。
- メソッドと変数の名前はキャメルケースにします。これは頭字語にも当てはまります。
例
- 受け入れられるメソッド名:
encodeUsingMd5()
getHTML()
ではなくgetHtml()
getJSONResponse()
ではなくgetJsonResponse()
parseXMLContent()
ではなくparseXmlContent()
- 受け入れられる変数の名前: var isGoodName var base64Encoder var xmlReader var httpServer
プロパティ
- クラスプロパティの名前は、静的定数ではない限り、全く同じ規則に従います。
定数である静的なクラスプロパティは全て大文字にします。例えば。
Ext.MessageBox.YES = "Yes"
Ext.MessageBox.NO = "No"
MyCompany.alien.Math.PI = "4.13"
宣言
以前の方法
Ext JS 4 より前のバージョンを使った経験がおありでしたら、
クラスを作るときにExt.extend
のを使うのになれていらっしゃるでしょう。
var MyWindow = Ext.extend(Object, { ... }); |
このアプローチは、他のクラスを継承した新しいクラスを作るということがよくわかります。 直接継承する以外に、クラス作るための他の方法に関するAPIはありませんでした。config、statics、mixins などのものは含まれていませんでした。これらの項目については、後ほど説明します。
また別な例をご覧ください:
My.cool.Window = Ext.extend(Ext.Window, { ... }); |
このサンプルでは、新しいクラスに名前空間を与えて、Ext.Window
から拡張するようにしようとしています。ここには二つの検討するべき課題があります:
Window
をプロパティとして割り当てらる前に、My.cool
は既に存在するオブジェクトでなければなりません。Ext.Window
は参照される前に、ページ上に読み込まれ存在している必要があります
一つ目は Ext.namespace
(エイリアスは Ext.ns
) で解決できます。このメソッドは、オブジェクトやプロパティのツリーを再帰的にたどって、それぞれが存在していなかったら生成します。そのため、Ext.extend
の前に次の様に追加する必要があります。
Ext.ns('My.cool'); My.cool.Window = Ext.extend(Ext.Window, { ... }); |
二つ目の問題に取り組むのは簡単ではありません。Ext.Window
は、たくさんの他のクラスに依存している可能性があるからです。
また、この依存性はさらに他のクラスの存在に依存する可能性もあります。
そのため Ext JS 4 以前に作成されたアプリケーションでは、アプリケーションがフレームワークの一部分だけ必要としていても、ext-all.js でライブラリを全体的にインクルードしていました。
新しい方法
Ext JS 4 では、クラスを生成する一つのメソッド (Ext.define
) で全ての弱点を解決しました。次がその基本のシンタクスです。
Ext.define(className, members, onClassCreated); |
className
: クラス名members
: キー値ペアのコレクションを表現するオブジェクトですonClassCreated
: は、定義されたクラスの依存性が全て解決され、クラス自体も完全に生成された時に呼び出されるオプショナルな関数コールバックです。クラス生成は非同期の形のため、このコールバックは様々な状況で役に立ちます。これはセクション IV でより詳しく説明します。
サンプル:
Ext.define('My.sample.Person', { name: 'Unknown', constructor: function(name) { if (name) { this.name = name; } }, eat: function(foodType) { alert(this.name + " is eating: " + foodType); } }); var aaron = Ext.create('My.sample.Person', 'Aaron'); aaron.eat("Salad"); // alert("Aaron is eating: Salad"); |
注: Ext.create()
メソッドを使って、My.sample.Person
の新しいインスタンスを生成しました。new
キーワード (new My.sample.Person()
) を使うこともできますが、Ext.create
を使うと動的ローディングが利用できますので、常に Ext.create
を使うようにすることを推奨します。動的ローディングについて、詳しくは Getting Started guide ガイドをご覧ください。
コンフィグ (Configuration)
Ext JS 4 では、クラスが生成される前に、Ext.Class
の強力なプリプロセッサで処理される専用の config
プロパティを導入しました。
その機能は次の様なものです。
- コンフィグは他のクラスメンバーから完全にカプセル化されています
- メソッドが定義されていない場合、クラス生成中にクラスプロトタイプに、各コンフィグのゲッターとセッターメソッドが、自動的に生成されます。
- 各コンフィグプロパティには
apply
メソッドも生成されます。自動的に生成されたセッターメソッドは、値を設定する前に、内部でapply
メソッドを呼び出します。値をセットする前にカスタムロジックを実行する必要があれば、コンフィグプロパティのapply
メソッドをオーバーライドできます。apply
が値を返さない場合には、セッターは値をセットしません。
Ext JS 5 では手動で initConfig()
を呼び出す必要をなくしました。
下記のコンフィグのサンプルをご覧ください。
Ext.define('My.own.Window', { /** @readonly */ isWindow: true, config: { title: 'Title Here', bottomBar: { height: 50, resizable: false } }, applyTitle: function(title) { if (!Ext.isString(title) || title.length === 0) { alert('Error: Title must be a valid non-empty string'); } else { return title; } }, applyBottomBar: function(bottomBar) { if (bottomBar) { if (!this.bottomBar) { return Ext.create('My.own.WindowBottomBar', bottomBar); } else { this.bottomBar.setConfig(bottomBar); } } } }); /** サンプルを完成させる子コンポーネント */ Ext.define('My.own.WindowBottomBar', { config: { height: undefined, resizable: true } }); |
次のサンプルはそれの使い方です。
var myWindow = Ext.create('My.own.Window', { title: 'Hello World', bottomBar: { height: 60 } }); alert(myWindow.getTitle()); // "Hello World" とアラート myWindow.setTitle('Something New'); alert(myWindow.getTitle()); // "Something New" とアラート myWindow.setTitle(null); // "Error: Title must be a valid non-empty string" とアラート myWindow.setBottomBar({ height: 100 }); alert(myWindow.getBottomBar().getHeight()); // 100 とアラート |
Statics
静的なメンバーは statics
コンフィグを利用して定義できます。
Ext.define('Computer', { statics: { instanceCount: 0, factory: function(brand) { // 静的メソッドの this はクラス自身の参照となる return new this({brand: brand}); } }, config: { brand: null } }); var dellComputer = Computer.factory('Dell'); var appleComputer = Computer.factory('Mac'); alert(appleComputer.getBrand()); // 自動生成されたゲッターでコンフィグの値を取得する。"Mac" とアラート |
エラーハンドリングとデバッグ
Ext JS には、エラーハンドリングとデバッグに役に立つ機能がいくつかあります。
Ext.getDisplayName()
を使うと、メソッドの表示名を取得できます。エラーをスローする際にデスクリプションの中にクラス名やメソッド名を記述するときに、特に役に立ちます。throw new Error('['+ Ext.getDisplayName(arguments.callee) +'] Some message here');
Ext.define()
で定義されたクラスやメソッドでエラーがスローされたら、WebKitベースのブラウザを使っている場合には、コールスタック内にメソッドとクラス名が表示されます。例えば Chrome の場合には次の様になります。