どうも、kaminalyです。
「てめぇの馬鹿さ加減にはなぁ、父ちゃん情けなくて涙が出てくらぁ」
ということがあったので訂正します。
この前、「CoffeeScriptでstatic/private/publicなメンバ/メソッドをもったクラスのつくりかた」
というメモを書いたのですが、
最終的に落ち着いた方法だと、片手落ちというか、かなり問題があることがわかったので、
再度メモします。
前回は、最終的に
コンストラクタのスコープ内に押し込める形で
privateな変数/メソッドを表現するのがベストなのかなぁという感じで終わりました。
こんな感じです。
class Animal ### ---------------------------------------- PRIVATE STATIC MEMBER ---------------------------------------- ### _count = 0 ### ---------------------------------------- PRIVATE STATIC METHOD ---------------------------------------- ### _inclement = -> ++_count ### ---------------------------------------- PUBLIC STATIC MEMBER ---------------------------------------- ### @semanticName: 'Unknown' ### ---------------------------------------- PUBLIC STATIC METHOD ---------------------------------------- ### @getCount: -> _count constructor: (sex)-> ### ---------------------------------------- PRIVATE INSTANCE MEMBER ---------------------------------------- ### _age = 0 _sex = sex ### ---------------------------------------- PRIVATE INSTANCE METHOD ---------------------------------------- ### _aging = -> ++_age; ### ---------------------------------------- PUBLIC INSTANCE MEMBER ---------------------------------------- ### @name = '' ### ---------------------------------------- PUBLIC INSTANCE METHOD ---------------------------------------- ### @getAge = -> _age @getSex = -> _sex @grow = -> _aging() ### call private static method ### _inclement();
でも、この方法だと問題がありました。
コンストラクタ内でpublicなメソッドを定義した場合、
継承した時のオーバーライドが不完全になります。
class NgSuper1
constructor: ->
@publicFunction = =>
console.log "super"
class NgSub1 extends NgSuper1
constructor: ->
@publicFunction = =>
console.log "sub"
super()
#-------------------------------------
class NgSuper2
constructor: ->
@publicFunction = =>
console.log "super"
class NgSub2 extends NgSuper2
publicFunction: =>
console.log "sub"
super()
#-------------------------------------
class NgSuper3
constructor: ->
publicFunction: =>
console.log "super"
class NgSub3 extends NgSuper3
constructor: ->
@publicFunction = =>
console.log "sub"
super()
#-------------------------------------
class OkSuper
constructor: ->
publicFunction: =>
console.log "super"
class OkSub extends OkSuper
publicFunction: =>
console.log "sub"
super()
それぞれのサブクラスをnewしてpublicFunctionを実行したら
sub
super
と表示するのが望ましい結果なんだが、
以下が実際の結果。
////// NG1 sub ReferenceError: publicFunction is not defined ////// NG2 super ////// NG3 sub ReferenceError: publicFunction is not defined ////// OK sub super
という感じ。
両方prototypeベースでないと、
オーバーライド出来なかったり、
super()が使えなかったりする仕様のようです。
ややこしいので、publicはprototypeベースになる記述方法にする決まりにするとなると、
コンストラクタのスコープ内に押し込めたprivateな変数やらメソッドは呼べなくなる。
もはや、privateなんてなかった事にして実装するしかないのか・・・
protectedも使えないし、やっぱり、疑似的なものでしかないんだなぁ。
継承のないクラスだとなんとか表現できてるんだけど。
色々しらべてたら、CoffeeScriptのprivateについては
GitHubのissueで結構前から色々議論されている様子。
class Person
`function __p(t,s){return(t.__P1||(s={},t.__P1=function(f){return(f===__p)?s:{}}))(__p)}`
constructor: (name) ->
__p(this).name = name
say: (what) ->
console.log "#{what}, says #{__p(this).name}"
meet: (other) ->
console.log "#{__p(this).name} meets #{__p(other).name}"
class Contact extends Person
`function __p(t,s){return(t.__P2||(s={},t.__P2=function(f){return(f===__p)?s:{}}))(__p)}`
constructor: (name, phoneNumber) ->
super name
__p(this).phoneNumber = phoneNumber
dial: () ->
console.log "Calling #{__p(this).phoneNumber}"
console.log "Private name: #{__p(this).name}"
bob = new Person 'Bob'
harry = new Contact 'Harry', '12341234'
harry.dial()
harry.say 'Hi'
bob.say 'Hello'
bob.meet harry
### output:
Calling 12341234
Private name: undefined
Hi, says Harry
Hello, says Bob
Bob meets Harry
###
#-別の実装---------------------------
id = 0
privatize = (cls) ->
_ = {}
register = (obj) ->
id += 1 if "#{obj}" != "#{id}"
strid = "#{id}"
obj.toString = ->
return strid
_[strid] = {}
[register, _]
class Foo
[ register, _ ] = privatize @
constructor: (name) ->
register @
_[@].name = name
sayHi: ->
#console.log "kita"
console.log "foo says Hi #{_[@].name}"
reportPrivate: ->
console.log _
class Boo extends Foo
[ register, _ ] = privatize @
constructor: (name) ->
super()
register @
_[@].name = name
booSayHi: ->
console.log "boo says Hi #{_[@].name}"
reportPrivate: ->
console.log _
#console.log "hoge"
a = new Foo "Alice"
b = new Foo "Bob"
c = new Boo "Jane"
a.sayHi()
b.sayHi()
c.sayHi()
c.booSayHi()
a.reportPrivate()
c.reportPrivate()
### output:
foo says Hi Alice
foo says Hi Bob
foo says Hi undefined
boo says Hi Jane
{ '1': { name: 'Alice' },
'2': { name: 'Bob' },
'3': { name: undefined } }
{ '3': { name: 'Jane' } }
###
#-別の実装---------------------------
#こう書いたら
class Test
constructor: (@name) ->
greeting: ->
@internalMethod()
private:
internalMethod: ->
"Hello #{@name}!"
### こう吐き出したらどうか?的な
var Test;
(function() {
var __private = {};
Test = function(_a) {
this.name = _a;
return this;
}
Test.prototype.greeting = function() {
return __private.internalMethod.call(this);
}
//private methods
__private.internalMethod = function() {
return ("Hello " + (this.name) + "!");
}
})();
###
まあ、未だに実装はされていないんだな。
上二つは取りあえずコードを足せば使えるので、
CoffeeScriptで実装されるまでは上記のコードや自前で用意したもので
代用するのが妥当な着地点なのかもしれない。
ああ、protectedも欲しいな。

