パッケージ化の方法について
簡単な方法~齟齬あり
クラス/オブジェクトのプロパティにする
クラスやオブジェクトをパッケージの名前とし、パッケージ内のクラスなどはそのプロパティとして定義する。
ただしこの方法は、最適とは言えない。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# Package 1 class Pack1 class MyClass constructor: -> @name = "P1-MyClass" Pack1.MyClass = MyClass #Package 2 Pack2 = {} # not in package class MyClass constructor: -> @name = "P2-MyClass" Pack2.MyClass = MyClass ins1 = new Pack1.MyClass() ins2 = new Pack2.MyClass() ins3 = new MyClass() console.log(ins1.name) # P1-MyClass console.log(ins2.name) # P2-MyClass console.log(ins3.name) # P2-MyClass |
1行目で名前空間に用いるクラスを定義し、7行目でMyClass
をPack1
のプロパティとしている。
10行目で名前空間に用いるオブジェクトを定義し、16行目でMyClass
Pack2のプロパティとして
いる。
20行目でMyClass
単独を用いた場合、直前に13行で定義されたMyClass
が参照される。
パッケージ内での名前参照の祖語
パッケージ間で名前が重複する場合、下手をすると想定外のオブジェクトが使われてしまう。
次の例は、Pack1
とPack2
の2つのパッケージで同じクラス名を使い、各パッケージの中ではパッケージ名なしで使おうとしたもの。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
class Pack1 class InnerClass constructor: -> @name = "P1-Inner" class MyClass constructor: -> @name = "P1-MyClass" @inner = new InnerClass() Pack1.MyClass = MyClass Pack2 = {} class InnerClass constructor: -> @name = "P2-Inner" class MyClass constructor: -> @name = "P2-MyClass" @inner = new InnerClass() Pack2.MyClass = MyClass ins1 = new Pack1.MyClass() ins2 = new Pack2.MyClass() console.log(ins1.name) # P1-MyClass console.log(ins1.inner.name) # P2-Inner!! console.log(ins2.name) # P2-MyClass console.log(ins2.inner.name) # P2-Inner |
ところが29行目で、Pack1
のクラスのはずのところがInnerClass
にはPack2
のものが使われてしまっている。
これは、25行目以降の実行文の前にグローバル変数InnerClassの内容
がPack1のもの
からPack2
のものへと上書き定義されたため。
結局この方法だと、各パッケージの中でもクラス名の前にパッケージ名をつけないといけないようで、コーディング上は元のクラス名に識別子を組み込むのとあまり変わらない。
有効な方法~即時実行関数
先の方法は、すべてグローバル領域で行われていたため、パッケージ内でのみ参照するクラスも上書きされてしまった。
そこで、パッケージごとに即時実行される無名関数で括ってみる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
Package1 = {} do -> class InnerClass constructor: -> @name = "P1-Inner" class MyClass constructor: -> @name = "P1-Class" @inner = new InnerClass() Package1.MyClass = MyClass Package2 = {} do -> class InnerClass constructor: -> @name = "P2-Inner" class MyClass constructor: -> @name = "P2-Class" @inner = new InnerClass() Package2.MyClass = MyClass ins1 = new Package1.MyClass() ins2 = new Package2.MyClass() console.log(ins1.name) # P1-Class console.log(ins1.inner.name) # P1-Inner console.log(ins2.name) # P2-Class console.log(ins2.inner.name) # P2-Inner |
1行目のようにグローバル領域でパッケージ名に使うオブジェクト/クラスを定義し、パッケージごとに即時無名関数で括る。
パッケージ内ではパッケージなしでアクセスし、外からアクセスするときはパッケージ名を付してアクセス。
これで一応、パッケージ化とコーディングの簡易化を両立させることができる。
パッケージ名について
パッケージ名は、「世界規模での名前衝突を避ける」ように潔癖を極めるなら、Javaで行われているようなドメイン名管理が答となる。
1 2 3 4 5 |
com.taustation.game.MyClass do -> class MyClass ... |
なお、Javaの場合はimport文で指定したパッケージはクラス名のみで使えるが、Javascriptではエイリアスを明示的に指定しなければならないらしい。
JavaScriptスタイルガイドでは、「長い型名をエイリアスし可読性を向上させる」ために以下のように示唆されている。
“ローカルのエイリアスを使うことで長い型名の可読性を向上できる場合はそうしてください. エイリアスの名前は型名の最後の部分にしてください.”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/** * @constructor */ some.long.namespace.MyClass = function() { }; /** * @param {some.long.namespace.MyClass} a */ some.long.namespace.MyClass.staticHelper = function(a) { ... }; myapp.main = function() { var MyClass = some.long.namespace.MyClass; var staticHelper = some.long.namespace.MyClass.staticHelper; staticHelper(new MyClass()); }; |
なお、npmのサイトでいろいろなパッケージを見てみると、区切りにはアンダースコア(_)ではなくマイナス(-)を使っている。