Kengo's blog

Technical articles about original projects, JVM, Static Analysis and TypeScript.

7年働いた時点での私の仕事の極意

最重要

  • 実行に重きを置く
    • やらないで後悔するよりも、やって反省する。
      • 反省は成長を産み生産的だが、後悔は精神の無駄な消費。
      • 時間は有限で貴重な資源だが、たぶん今の段階では行動する前に得るものや結果を予測するのは難しい。
    • 正しい反省の方法とは何か、考え続けること。
      • 「正しく反省するために、何を記録しておくべきか」実行前に明らかにしておくこと。
      • 反省の結果は組織的な何かに落としこむ。組織構造、戦略、静的解析、自動テスト、教育など。意識しないでも巨人の肩に乗れる状況を作ることが、組織の成長につながる。
    • Done is Better Than Perfect
      • ただし、思考停止の言い訳にしないこと。詰めの甘さを擁護する言葉ではない。詰めの甘さは立場や考え方が違うひと3人くらいに意見を求めればだいたい炙り出せる。
      • 長期的視野を持ちつつ、それに引っ張られない。進展を作ること、現状を少しずつ変えることを意識する。
    • 「やる内容」ではなく「たどり着くべき結果」が大切だということを忘れないために、問題の特定と問題解決の実行にフェーズの概念を持ち込む。やりたいから仕事をやるのではなく、解決するにはこの仕事をやるべきだからやる、ということをチーム全員で意識する。
    • いつでも行動を取れるよう、自己を印象づけておきつつ周囲との繋がりを育てておく。
      • ひとりで完結する仕事はない。

人との交わり方

  • 推測の扱い

    • 議論の仮説にできる「建設的な推測」と根拠の無い「不安や期待」を分けて扱う。
    • 議論の仮説はあくまでも「仮説」であり、「前提」ではない。状況に応じてアップデートされるべき。
    • 不安や期待は上司あるいは信頼できる人に1on1で話す。それ以外の人と場所には話さない。マネジメントに限らず後輩を持つ身なら意識したい/させたいポイント。
      • 不安はコミュニケーション不足を表すシグナルであることが多い。
  • ヒューマンスキル

    • という、横文字に惑わされないこと。「みんなが幸せ」になるにはどうするか考えて行動しようという話。それだけ。
    • 「幸せの定義」「何をもって満足するか」「情報の非対称性」「優先度の違い」「面子」「私生活」などなど様々な変数が絡むのでわかりにくくなる。
      • 目的意識がバラバラだと「みんなが幸せ」という絵を描けなくなる。ヒューマンスキルの前提にはプロジェクトマネジメント、チームビルディングがある。
      • 情報の非対称性は人間が複数集まった時点で当然生じるので、情報を取りに行ったり率先して提供したりセルフブランディングしたりする必要がある。
      • セルフブランディングの理想は「あの人なら問題を解決してくれる」。機能を切り口にする「XXならなんでも分かる人」は次点。
      • 個人に敬意と関心を持つこと。個人を知らないことがチームビルディングのミスを誘発する。
    • 私は年上・目上に対して(本人にその気はないのに)面子を潰しに行く傾向にあるので自重する。日本人に対するコミュニケーションが下手だ(要らん遠慮をする)という説もある。

エンジニアとしての心構え

  • 技術的・ビジネス的な流行り廃りとのつきあいかた

    • 跳びつくのではなく、それが生まれた歴史的背景を考える。
    • その背景を自分たちが共有しているか?を落ち着いて判断する。ウリ文句には多分にポジショントークが含まれることを覚えておく。
    • どうしても跳びついてしまう人は存在する。たぶん経験の差で、デザインパターンを何にでも適用しようとするのと同じ。モチベーションを削がずに再考を促す方法は、まだ見つかっていない。
  • 武器を増やすための研究は個人で、武器を磨くための追求は仕事で

    • 課題はそれの解決に必要な武器を教えてくれない(教えてくれたとして、それからキャッチアップしているのでは遅いことがしばしば)。仕事が始まる時点で武器を知っているために、予め広い視野を持って武器を増やしておく。
      • JVMは数少ない「教えてもらった」武器。新人にはOJTでも良いので、何かひとつは渡すようにする。責任を持たせることにも繋がる。
      • ASM, Findbugs, AMD (RequireJS), Maven, Grunt, SLF4J, npm, CI, OOP, FP は自ら研究しておいたことで業務においてリードする立場になれた。
      • LLVM, GAE, Vagrant, PMD, Chef はまだ役だってないが、考察のための視野を育む上で役立った。
      • enchant.js, Travis CI, Golang, Android, Intel Edison については今のところ全く役立っていない。百発百中である必要はない。
    • 深い理解や応用は要求によって育まれる。個人で作れるツールやライブラリとは比べ物にならない要求と資源(時間)を、業務では使うことができる。
    • 重要なのは、この原則を他人に求めないこと。すべて仕事で完結させるのも正しい選択で、だから教育や自動化(人手の排除)が必要。
  • 目指すべきは専門家でも何でも屋でもない

    • エキスパートになることで、組織の部品として問題解決の手段として使いやすくなる。問題を持ち込まれやすくなる=活躍の機会が増える。
    • 何でも屋になることで、問題の本質を見抜き迅速な解決が可能になる。問題を発見したり、切り分け前の問題を持ち込まれやすくなる。
    • 真になるべきは、周囲のやる気と関心を引き出しチームとしての問題解決に貢献できる人。エキスパートとしての自分や何でも屋としての自分は道具であり手段。時には他のエキスパートにdispatchしたり、他の何でも屋に意見を求める。
  • 「技術できます」と言ってしまう

    • 自分を超える超級エンジニアが山のようにいらっしゃることをわかった上で、あえて「技術できます」と言う。
    • ”技術できる”の定義なんて存在しないので気にしない。”技術に詳しいらしい”ということを知ってもらうだけで、組織に貢献できる機会は増える。沈黙や謙遜は個人の美徳であって、チームや組織にはリターンがない。
      • ヒューマンスキル上のリスクはある。またビジネスがわからないのではという不安を持たれるので、行動で示していく。

マネジメントとしての心構え

  • とにかく聞いて話す

    • 日本人とは飲み会、中国人とは昼ごはん、というのが今のところの経験則。業務の枠を超えないと業務を向上させることはできない。
    • 自分がどう成長したいかわかっていない人がほとんどなので、3-6ヶ月かかっても良いから1on1を通じて意識を作っておく。
  • 組織として目指しているのはどこか、チームとして目指すべきはどこかを明確にする。他のチームの目標と現場も可能な限り伝える。

    • 「自分には何ができるか?」常に考えてもらう。
      • 例)Stand-up Meetingは報告の場ではなく共有の場であり、共有された問題に対して動くべきは自分であると認識させる。
  • 戦略とビジネスモデルはシンプルであるべき

    • 判断基準となるこれらは、シンプルでなければ使いまわせない。
    • 今やっているタスクが技術的に特異なことであればあるほど、基本となる「目標」を意識すること。

被マネジメントとしての心構え

  • 要求を明確に上げる

    • 「〜だったらいいな」「〜なのは嫌だな」ではなく「XXという問題がありYYで解決したいのでZZをください」と言う。「AAな理由がわからないのですが、BBなのでしょうか」と理解を確認するのも可。
    • 私の場合「仕事がつまらない」と感じたら即座に「何をやるべきと考えているか」伝えるべき。すぐに自分が思っている以上のパフォーマンス低下が出る。
  • 自分で判断せず、判断基準を提供する

    • 高台にいる人のほうが視野が広いのは当然。高台にいては見えないこと、自分だからわかることを論理的に言語化して伝える。論理的でないと、マネジメントが他のマネジメントや上司に伝えられない。
    • 判断はマネジメントに下してもらい、その判断に意見はしても文句は言わない。文句があるなら自分でマネジメントする。
  • 目標を求める

    • 目標が与えられずタスクだけ降ってくる状況は、中長期的に見てまず良いことがない。
    • 結局何をやることが顧客のため組織のためになるのか、マネジメントに考えさせる。マネジメントをマネジメントする。
  • 「マネジメントがXX(技術、自分、状況 その他)を分かってない」と言うのは非生産的

    • マネジメントにわからせろ!
    • マネジメントがそれをわかっていないのは、マネジメント個人の問題ではなく組織構造や自分の情報の扱いに起因する問題であるとも考えられる。

まとめ

  1. 実行せよ
  2. 正しく実行するためには、実行の目標とプロセスに関心を払い、役割を正しく演じよ
  3. 個人と組織の成長を促すため、常に会話し、顧客や仲間を幸せにし続けよ

The Art of Enbugging

I've read The Art of Enbugging (PDF) by Andy Hunt and Dave Thomas.

This is little long, but good article to share your topic with teammate. If you're trying to improve maintainability of your product, it will help your communication.

I feel that "The Paperboy and the Wallet" is good example, we can feel what is wrong and understand how to solve. I agree that Law of Demeter for Functions is good to keep software easy to maintain.

And, about "Shy". I know that a lot of developers are shy, this should become good metaphor to tell for beginners.

When/How to use goog.ui.Component#makeId()

goog.ui.Component#makeId() is a helper function to generate ID for DOM elements. Let's start discussion based on sample component which contains following DOM:

<form>
  <div>
    <input type="text">
  </div>
  <button type="submit">
</form>

To implement createDom() method, we may need to decide ID for each element. And of course each of them should be unique in the document.

How you'll solve this problem? Use random value as ID? Or create counter to use sequential value? It looks troublesome.
So it's time to try makeId() method! It will use goog.ui.IdGenerator as counter to generate sequential value, and generated ID starts with ':' so it will not conflict other hand-made value so easily.

/** @override */
jp.skypencil.Component.prototype.createDom = function() {
  var domHelper = this.getDomHelper();

  var $input = domHelper.createDom(goog.dom.TagName.INPUT, {
    type : 'text',
    id: this.makeId('input') // will return a string like ':1.input'
  });
  var $div = domHelper.createDom(goog.dom.TagName.DIV, {
    id : this.makeId('div') // will return a string like ':1.div'
  }, $input);
  var $button = domHelper.createDom(goog.dom.TagName.BUTTON, {
    type : 'submit',
    id : this.makeId('button') // will return a string like ':1.button'
  });

  var $form = domHelper.createDom(goog.dom.TagName.FORM, {
    id : this.makeId('form')  // will return a string like ':1.form'
  }, $div, $button);
  this.setElementInternal($form);
}

getElementByFragment() helps you to get Element easily

Here is another merit to use makeId(); you can get Element instance simply like below:

var $button = this.getElementByFragment('button');

Stay away from setId() and getId()

goog.ui.Component also has setId() and getId(), but this is not for handling id attribute of DOM element. This is for handling Component-ID will be used to maintain component tree.

It is not necessary to keep Component-ID and DOM element ID same. I recommend you to stay away from these 2 methods, you do not have to care about component tree handling (it's responsibility of closure-library!).

Reference

goog.Promise の使い方

日本語情報少ないので。

Promiseとは?

こちらをご覧ください。

Promiseオブジェクトを作る

newを使ってコンストラクタを呼ぶだけです。このとき、引数に関数をひとつ渡してやる必要があります。この関数はresolve関数とreject関数を受け取るので、関数内で

  • 処理成功時は resolve
  • 処理失敗時は reject

を呼んでやる必要があります。それぞれ引数に値をひとつ渡すことができ、他の処理に結果を受け渡すことができます。

var promise = new goog.Promise(function (resolve, reject) {
  if (...) {
    resolve(result);
  } else {
    reject(err);
  }
});

コンストラクタ引数は無名関数ではなく、メソッドあるいはローカルな関数として定義しても良いですね。公式ドキュメントではresolverと命名されています。

function resolver(resolve, reject) {
  if (...) {
    resolve(result);
  } else {
    reject(err);
  }
}

var promise = new goog.Promise(resolver);

成功時の処理を書く

var promise = new goog.Promise(resolver);
promise.then(function(result) {
  // resolve() の引数に渡した値をここで使える
  console.log(result);
});

失敗時の処理を書く

var promise = new goog.Promise(resolver);
promise.thenCatch(function(err) {
  // reject() の引数に渡した値をここで使える
  console.log(err);
});

必ず実行したい処理を書く

var promise = new goog.Promise(resolver);
promise.thenAlways(function() {
  // リソース解放など
});

合わせ技

then などのメソッドは自分自身を戻り値として返すので、メソッドチェーンが可能です。

var promise = new goog.Promise(resolver)
  .then(displayDialog)
  .thenCatch(displayAlert)
  .thenAlways(unlockSubmitButton);

Promiseの合成

静的メソッドとして、以下3種類が用意されています。

  • goog.Promise.all(promises)
  • goog.Promise.firstFulfilled(promises)
  • goog.Promise.rase(promises)

allはすべてresolveされた時のみresolveされるものです。thenにはすべてのPromiseの結果が配列に入れられて返ります。必要なリソースを並列で用意するときに利用できるでしょう。ES6のやつと同じです。

goog.Promise.all(assetLoader, dataLoader, calculator)
  .then(function(result) {
    var asserts = result[0];
    var data = result[1];
    var computed = result[2];
    doFoo(asserts, data, computed);
  });

firstFullfilledは、最初にresolveされた結果を返します。ひとつでもresolveされたら合成後のPromiseはresolvedになります。すべてrejectされたときのみrejectとなり、thenCatchが呼び出されます。同じ資源を複数の経路で準備できる際など、ひとつでも成功すれば良い・最も早く取得できた結果を使えれば良い時に利用できるでしょう。

goog.Promise.firstFullfilled(loadDataFromCDN, loadDataFromServer)
  .then(function(data) {
    doFoo(data);
  });

raseは最初にresolve/rejectされたPromiseの結果を返します。ES6のやつと同じです。

goog.resultとgoog.async.Deferred

これらは非推奨と明記されています。新規の利用は控えましょう。

  • NOTE: goog.result is soft deprecated - we expect to replace this and
  • {@link goog.async.Deferred} with {@link goog.Promise}.

Tips to accelerate your original game

To make your game more interesting, you should pay much attention to keep high performance. This is UX problem, but it is not designer who can solve. Only developer has solution for this problem, so we should learn about when we face problem and how many approaches we have.

Reducing I/O

Cache

This is popular tip for background-image, map-chip and character-chip.

Load picture and draw it onto cache on RAM, then you do not have to access to storage. For instance, your STG may load background-image when player decided the stage to play. By this cache, your STG does not have to read storage for background image during this stage.

Lazy-loading

In previous case, your STG needs to lock screen while it loads resources onto RAM*1. It's better to shorten this time, or player will feel that your game is boring.

To reduce locking, lazy-loading can be solution. At initialising phase, your STG does not have to load background-image of boss-stage. Postpone loading, then you can shorten the lock.

Pre-loading

Here is another and opposite solution. While player is choosing stage to play, your STG can load resources onto RAM. When player decides stage, necessary data is already loaded so you can start game immediately for player.

To use this tip, programmer should understand player's need and how they control your game. For instance, when player enters stage-select page, your STG may load SE (Sound Effect) of shooting action because you knows that player will choose stage so your STG should load such resources later. I think this is similar to implementing recommendation engine.

Are you implementing HTML5 game? If so, you may try pre-fetch API.

Reuse drawn picture

During user plays your STG, you should draw background-image and scroll it from right to left. So in each frame you should draw large picture, it costs resource of player's computer.

To reduce this cost, you can reuse background-image which is already drawn. You shift background-image some pixels, and draw only the right part. You can reuse almost all of drawn picture, and focus on drawing new parts.

Here is another example: even though you are implementing 3D game, you do not have to compute 3D models in each frame, because you can store computed images to RAM and use it instead.

Put multiple resource into one file

This is popular tip for map-chip and character-chip. And this is also very famous for web developer as CSS Sprites. Data URI also can be solution.

By this tip, you can reduce number of resource to load. So it's effective especially when resource is far from player. Learn latency for detail.

Compress data

You may like raw data format like BMP format, but when you publish your product it's better to use compressed format. Then you can reduce data size to load, so it will reduce time to load. Learn Throughput for detail.

To compress data, your may use ZIP or other format. It's OK but please remember that decompression costs machine resource.

Reducing computing cost

divide main-loop and loop-to-render

When you implement graphical game, you should have main-loop like below:

function main(){
  compute();
  render();
  setTimeout(main, 15); // about 60 FPS
}
main();

It works, but it has one problem.

This code tries to render image 66.6 times per second, but in my environment 6 of them has no meaning because we render twice or more between 2 display refresh. You can try it at this page.

It means that we render needless picture 6 times per second... it should be stopped.

var previousTimeToRender = 0;
function tryToRender(){
  var now = Date.now();
  if (now - previousTimeToRender > 1000/60) {
    previousTimeToRender = now;
    draw();
  }
  requestAnimationFrame(tryToRender);
};

function main(){
  compute();
  setTimeout(main, 1000/60);
}

main();
requestAnimationFrame(tryToRender);

Looks too complex? Then capsule it:

function Renderer(fps) {
  this.fps_ = fps;
  this.previousTimeToRender_ = 0;
  this.callback_ = null;
};
Renderer.prototype.tick = function(){
  var now = Date.now();
  if (now - this.previousTimeToRender_ > 1000/this.fps_) {
    this.previousTimeToRender_ = now;
    this.callback_();
  }
  requestAnimationFrame(this.tick.bind(this));
};
Renderer.prototype.register = function(callback){
  this.callback_ = callback;
  this.tick();
};

// then you can code like below:
function main(){
  compute();
  setTimeout(main, 1000/60);
}
main();
var renderer = new Renderer(60);
renderer.register(render);

And if we can reduce FPS to draw without UX problem, it's also good way to improve performance.

var renderer = new Renderer(30); // draw 30 times per seconds. we can keep computing 60 times per second.

Algorithm

Algorithm is very important to implement game. Routing, Z-sort, collision detection, AI... there are a lot of algorithm used in game. Brute-force is acceptable during development, but before you publish your game, it's better to consider to replace it with efficiently implementation.

This is too large topic for this article, so I just list up sample codes:

Now I'm interested in Simulated Annealing, I'll try to code a simple one.

*1:you may know that some PSP games display 'LOADING...' again and again

Intel Edisonを購入した

Intel Edison Breakout Board Kitを淘宝网にて515人民元で購入。当然ながら技適マークは無いです。

まずは以下を試験実施。

wget, python, node が動くことを確認。

翌日起動したところ *** Ready to receive application *** から進まなくなった(U-Bootが起動しなくなった)のでここを参考に再インストール。

$ brew install dfu-util coreutils gnu-getopt
$ cd edison-image-rel1-maint-rel1-ww42-14
$ ./flashall.sh
Using U-Boot target: edison-blank
Now waiting for dfu device 8087:0a99
Please plug and reboot the board
Timed out while waiting for dfu device 8087:0a99
DEBUG: lsusb
./flashall.sh: line 77: lsusb: command not found
DEBUG: dfu-util -l
dfu-util 0.8

Copyright 2005-2009 Weston Schmidt, Harald Welte and OpenMoko Inc.
Copyright 2010-2014 Tormod Volden and Stefan Schmidt
This program is Free Software and has ABSOLUTELY NO WARRANTY
Please report bugs to dfu-util@lists.gnumonks.org

Did you plug and reboot your board?
If yes, please try a recovery by calling this script with the --recovery option

DFUデバイスとして認識されていない。--recoveryはOSXで使えないので対応を考える。

Windowsマシンに切り替えて再接続すると、特に苦労なく2つのLEDが点灯し起動完了。OSXとの相性問題?flashall.shのサポート状況を見ても、開発にはLinuxWindowsを選択したほうが無難だろう。

と、思っていたらWindowsが起動しなくなった。ドライバ入れたから?OS再インストールが必要そう。OSXでは相変わらず。

Windowsは(MacBook ProのBootcampで動いているので)パラメータRAMのリセットを行うことでOSXが起動するように。念の為OSXを最新にあげて再挑戦したところ、とりあえずWindowsは起動した。USB接続で2つのLEDが点灯することも確認済み。

goog.ui.Componentの継承で気をつけるべきこと一覧

基本

インスタンスの状態

  • クラスフィールド(static変数)にインスタンスの状態を入れない。
  • インスタンスフィールドはprivateにする。
  • 親クラスのフィールドには触れない。

DOM要素の操作

  • DOM要素を指定する際にはIDではなくclassやカスタムdata属性などを使う。
    • IDは、コンポーネントが画面に常に一つしか存在しないことを保証できる時のみ利用する(1つの画面に2つ以上同じIDを置けないので)。
    • Closure Libraryはclassによる要素検索メソッドを多数提供しているので、classを使うことが望ましい。
  • 自分自身のDOM要素に含まれるDOM要素のみ触ることが望ましい。
    • goog.dom.getElementByClass() などgoog.dom名前空間にあるメソッドやDomHelperのメソッドよりも、 this.getElementsByClass() などgoog.ui.Componentに用意されたメソッドを使うことが望ましい。

各メソッドで気をつけること

コンストラクタ

  • 必ずgoog.base(this, opt_domHelper)する。DomHelperは省略して良いが、引数で受け取る癖をつけて統一感を持たせる。
  • この時点ではDocumentに入っていない状態かつ自分自身のDOM要素がない状態。DOMの更新は控える。
  • コンポーネントインスタンスは極力この時点で作成する。あるいはコンストラクタ引数として受け取る。
  • this.addChild(childComponent)は親子でライフサイクルを統一するためにも効果的。これによって自身がdisposeされたタイミングで自動的に子コンポーネントもdisposeされるようになる。何らかの理由によりthis.addChild()できない場合はthis.registerDisposable(anotherComponent)しておくと良い。
/**
 * @constructor
 * @extends {goog.ui.Component}
 * @param {goog.dom.DomHelper=} opt_domHelper
 */
my.ui.Component = function(opt_domHelper) {
  goog.base(this, opt_domHelper);
  this.addChild(new my.ui.ChildComponent(this.getDomHelper()));
};
goog.inherits(my.ui.Component, goog.ui.Component);

createDom()

  • 必ずthis.setElementInternal(element)する。
/** @override */
my.ui.Component.prototype.createDom = function() {
  var element = this.getDomHelper().createDom(goog.dom.TagName.DIV, { 'data-foo': 'bar' });
  this.setElementInternal(element);
};

canDecorate()

  • decorateできるDOM要素に制限がある場合は、ここでそのチェックを行う。
  • falseを返した時のエラーメッセージ(goog.ui.Component.Error.DECORATE_INVALID)は汎用的なものなので、decorateに適さない理由をデバッグログに出しておくと良い。
/** @override */
my.ui.Component.prototype.createDom = function(element) {
  return element.tagName === goog.dom.TagName.DIV;
};

decorateInternal()

  • 必ずthis.setElementInternal(element)する。またはgoog.base(this, 'decotateInternal', element)でも良いが、特に親クラス実装を呼び出す必要性はない。
/** @override */
my.ui.Component.prototype.decorateInternal = function(element) {
  this.setElementInternal(element);
};

enterDocument()

  • まずgoog.base(this, 'enterDocument')してから、自クラス固有の処理を行う。
  • goog.base(this, 'enterDocument')は必ず呼び出すこと。
  • イベントのlistenはgoog.events.listen()ではなくthis.getHandler().listen()によって行う。これによってexitDocument時に手動でunlistenをする必要がなくなる。
/** @override */
my.ui.Component.prototype.enterDocument = function() {
  goog.base(this, 'enterDocument');
  this.getHandler().listen(this.getElement(), goog.events.EventType.CLICK, function(event) { ... });
};

exitDocument()

  • まず自クラス固有の処理を行ってから、goog.base(this, 'exitDocument')を呼び出す。
  • goog.base(this, 'exitDocument')は必ず呼び出すこと。
  • enterDocument時に自身や他のオブジェクトに加えられた変更があれば、これを巻き戻す。ただしthis.getHandler().listen()で作成されたイベントハンドラは親クラス実装で自動的にunlistenされるので気にする必要はない。
/** @override */
my.ui.Component.prototype.exitDocument = function() {
  // do some operation if we need

  goog.base(this, 'exitDocument');
};

disposeInternal()

  • まず自クラス固有のクリーンアップ処理を行ってから、goog.base(this, 'disposeInternal') する。
    • 自クラス固有の処理を先に行うとdispose()実行時に、自クラス固有のdisposeInstance→自クラス固有のexitDocument→親クラスのexitDocument→親クラスのdisposeInstanceの順に時に処理が行われることになり、exitDocumentがdisposeInstanceに遅れて実行されてしまう。
    • 公式に推奨されている。
  • goog.base(this, 'disposeInternal')は必ず呼び出すこと。
  • インスタンスフィールドにある他のオブジェクトへの参照をnull代入あるいはdelete演算子により断ち切ること。
  • this.isDisposed()が真かどうかは気にしなくて良い(偽であることを前提にして良い)。dispose()がthis.isDisposed()が偽の時のみdisposeInternal()を呼び出してくれる。
/** @override */
my.ui.Component.prototype.disposeInternal = function() {
  goog.base(this, 'disposeInternal');

  // do some operation if we need
};

getContentElement()

  • コンポーネントを追加するためのDOM要素がthis.getElement()と異なる場合のみ、このメソッドをoverrideする。
/** @override */
my.ui.Component.prototype.getContentElement = function() {
  return this.getRequiredElementByClass('container');
};

継承してはいけないメソッド

  • setElementInternal()
    • JSDocに Considered protected and final と明記されている
  • dispose()
    • 代わりに disposeInternal() を継承する
  • decorate()
    • 代わりに decorateInternal() を継承する
  • render(), renderBefore()
    • 代わりに createDom() を継承する
  • その他、継承する必要がないもの(ドキュメントには明記されていないが、これら標準実装に手を加える必要性はないものと思われる)
    • getElement()
    • getElementBy...()
    • getParent()
    • getRequiredElementByClass()
    • wasDecorated()
    • コンポーネントを扱うメソッド全般

参考資料

  1. goog.ui.Component のはぐれかた
  2. http://docs.closure-library.googlecode.com/git/class_goog_ui_Component.html
  3. GitHub - google/closure-library: Google's common JavaScript library