unhurried

コンピュータ関連ネタがほとんど、ときどき趣味も…

JavaScript オブジェクト指向

私はJavaを長く使ってきたためオブジェクト指向の考え方は馴染み深いのですが、JavaScriptではを使うときにはあまり意識していませんでした。JavaScriptはクラスを定義する方法がいくつかあってややこしいので、簡単に整理してみました。

  • ES5以前ではclass構文が利用できないためfunctionを利用します。
var ES5Class = (function() {

    // コンストラクタ
    var ES5Class = function(member) {
        // new演算子を指定せずに下記のように呼び出されたときための対策
        // var instance = ES5Class();
        if(!(this instanceof ES5Class)) {
            return new ES5Class(member);
        }

        // メンバ変数
        // プライベートにしたい変数には慣習的にアンダースコアを付ける。
        // (実際には外部からのアクセスは可能であるが、変数の用途を明示するため。)
        this._member = member;
    };

    // プロトタイプを使ってメソッドを定義する。
    // this.printMethod = function... でも定義できるが、
    // インスタンス生成の度に関数が作成されるため効率が悪い。
    var p = ES5Class.prototype;
    
    // メソッド
    p.printMember = function () {
        console.log(this._member);
    }

    return ES5Class;
})();

module.exports = ES5Class;
  • ES6以降ではclass構文を使ってクラスの定義ができます。
class ES6Class {

    // コンストラクタ
    constructor(member) {
        // メンバ変数
        // プライベートにしたい変数には慣習的にアンダースコアを付ける。
        // (実際には外部からのアクセスは可能であるが、変数の用途を明示するため。)
        this._member = member;
    }

    printMember() {
        console.log(this._member);
    }
}

module.exports = ES6Class
  • class構文では静的メソッドも定義できますが、静的メンバは定義できません。
// class構文には静的メンバを定義する方法は用意されていないため、
// 代替手段として、グローバルスコープの変数を利用する。
var _member = 0;

class ES6StaticClass {

    // 静的メソッド
    static printMember() {
        _member++;
        console.log(_member);
    }
}

module.exports = ES6StaticClass

Node.js モジュール読み込み

Node.jsのモジュール読み込みではmodule.exports、exportsが利用できますが、違いがいまいち理解できていなかったので自分なりに整理してみました。
実装上の違いといった細かいことはリンク先にとても詳しく記載されていますので、今回は使い分け方に焦点を当てています。

module.exportsとexportsの使い分け

単一のクラスや関数、オブジェクトをエクスポートする場合はmodule.exportsを使います。

class MyClass {
    ...
}
module.exports = MyClass;

複数の関数やオブジェクトを持つオブジェクトをエクスポートする場合はexportsを使います。

function myFunc1 {
    ...
}
function myFunc2 {
    ...
}
exports.myFunc1 = myFunc1;
exports.myFunc2 = myFunc2;

※ module.exportsを利用しても記述できますが、exportsの方がシンプルに記述できます。

module.exports = {
    myFunc1: myFunc1,
    myFunc2: myFunc1
}

requireを複数回実行したときの挙動

初回ロード時のみ評価され2回目以降はキャッシュされたオブジェクトが返却されます。

  • module.js
var _var = Math.random();
function func() {
    console.log(`module.js - func : _var=${_var}`)
}
exports.func = func;
  • main.js
const module1 = require('./module.js')
const module2 = require('./module.js')
module1.func()
module2.func()
  • 実行結果
module.js - func : _var=0.7381836391077983
module.js - func : _var=0.7381836391077983

参考

vim-mode-plusのキーバインドを変更する

Atomエディタプラグインvim-mode-plusには通常のVimエディタにあるvimrcを読み込む機能がないため、Atomのキーマップ設定ファイルであるkeymap.csonに設定し、複数のコマンドを割り当てたい場合はAtomの設定ファイルであるinit.coffeeに複数コマンドを1つのコマンドとして登録する必要があります。

例として、以下のvimrcファイルをinit.coffeeとkeymap.csonに変換する例をご紹介します。

変換前

vimrc

noremap j gj
noremap k gk

noremap <S-j> 10j
noremap <S-k> 10k
noremap <S-h> 10h
noremap <S-l> 10l

変換後

init.coffee

atom.commands.add 'atom-text-editor.vim-mode-plus', 'custom:move-left-10', ->
  view = atom.views.getView atom.workspace.getActiveTextEditor()
  atom.commands.dispatch view, 'vim-mode-plus:set-count-1'
  atom.commands.dispatch view, 'vim-mode-plus:set-count-0'
  atom.commands.dispatch view, 'vim-mode-plus:move-left'

atom.commands.add 'atom-text-editor.vim-mode-plus', 'custom:move-down-screen-10', ->
  view = atom.views.getView atom.workspace.getActiveTextEditor()
  atom.commands.dispatch view, 'vim-mode-plus:set-count-1'
  atom.commands.dispatch view, 'vim-mode-plus:set-count-0'
  atom.commands.dispatch view, 'vim-mode-plus:move-down-screen'

atom.commands.add 'atom-text-editor.vim-mode-plus', 'custom:move-up-screen-10', ->
  view = atom.views.getView atom.workspace.getActiveTextEditor()
  atom.commands.dispatch view, 'vim-mode-plus:set-count-1'
  atom.commands.dispatch view, 'vim-mode-plus:set-count-0'
  atom.commands.dispatch view, 'vim-mode-plus:move-up-screen'

atom.commands.add 'atom-text-editor.vim-mode-plus', 'custom:move-right-10', ->
  view = atom.views.getView atom.workspace.getActiveTextEditor()
  atom.commands.dispatch view, 'vim-mode-plus:set-count-1'
  atom.commands.dispatch view, 'vim-mode-plus:set-count-0'
  atom.commands.dispatch view, 'vim-mode-plus:move-right'

keymap.cson

'atom-text-editor.vim-mode-plus:not(.insert-mode)':
  'shift-h': 'custom:move-left-10'
  'shift-j': 'custom:move-down-screen-10'
  'shift-k': 'custom:move-up-screen-10'
  'shift-l': 'custom:move-right-10'
  'h': 'vim-mode-plus:move-left'
  'j': 'vim-mode-plus:move-down-screen'
  'k': 'vim-mode-plus:move-up-screen'
  'l': 'vim-mode-plus:move-right'

AtomにVimキーバインドを設定する

学生の頃からVimを愛用して来ましたが、用途によってはプラグインが豊富なAtomの方が便利な場合があるので、AtomVimキーバインドを設定する方法を調べてみました。

プラグインのインストー

Escで挿入モードを抜けたときにIMEをオフにする

キーマップを設定する

PlantUML クラス図

前回のシーケンス図に続いてクラス図も作成してみました。

' コメントはシングルクオーテーションの後に記述する

@startuml

' 色設定
skinparam classBorderColor black
skinparam classArrowColor black
skinparam noteBorderColor black
skinparam classAttributeIconSize 0

' クラス定義
class ClassA {
  - privateMember : Integer
  + publicMember : Integer
  # protectedMember : Integer
  ~ packageMember : Integer
  + method() : void
  + {static} staticMethod() : void
  + {abstract} abstractMethod() : void
}
' Note(Classに対して)
note top of ClassA: Note
note right of ClassA
  Note
end note

' Interface
interface Interface
ClassA ..|> Interface
' Note(Linkに対して)
note on link: Note

' Abstract Class
abstract AbstractClass
ClassA --|> AbstractClass

' Association
ClassA -- ClassB

' Composition
ClassA --* ClassC

' Aggregation
class ClassD
ClassA --o ClassD

' Dependency
class ClassE
ClassA ..> ClassE

@enduml

生成される図

http://www.plantuml.com/plantuml/png/XLBBIiD05DtdAowuy4N5jGj1MnU2kl4B9fCn3ardmcHKV5H80gBetiVEZLR5AheGeTIF6OhT-GlUIL9R9M0Mayavvzuvzqp6mIIl9hqsAQx79duooPlT9yycVJ09VjwRj6FI8xjQwjQoSRlNFJFnGxzrqVzuDl6bIKuTVCQXV_BIRptssbTELESY98eqW0OaYXQbyfYgoa0gS0D2wty52qh9dN9UICt-hzTQSNTBisKgnJhVOp1h3TaPqZkJlAEdp-wTascXcbN0lWDGWL3nRQBPAckuJC4SB0lDVAQGcuPmomquBQ76i0njKSsy4lOGGhH8_FAc3QOtfJSnYV2sv5w6xKUQQ4wRaBzNooJ4ZRGYL3TXiFihQ-B4QsZhgtESZOXtr7lgclZSn3TVdHF79WbQXY0tYWpcCdrEAEvlwY65XsOS4rvs1JPEEuRQ89GvVB0RmOfOPsOEvaVG7qihNDHB7Ka10NA55onOAERBdJY3SN_m72wEgrJmkD-CRH55adBCKegXCE-tQFcgR8Gouh_vgLnGpHhulc9-tc3ahwaDrNA0eBh6Gio92RexAbuQHZ9V8Dy0

PlantUML シーケンス図

AtomでPlantUMLを編集する環境が整ったので、今回はシーケンス図のサンプルを作成しました。よく使いそうな記法を網羅するようにしていますので、記法を忘れていてもこのサンプルを修正しながら図を記述していくことができると思います。

' コメントはシングルクオーテーションの後に記述する

@startuml

' 色設定
skinparam actorBorderColor black
skinparam sequenceParticipantBorderColor black
skinparam sequenceLifeLineBorderColor black
skinparam sequenceArrowColor black
skinparam noteBorderColor black

' Participantの宣言(省略可能)
' asで別名を定義できる
actor Actor as A
participant Participant as P

A -> P : Synchronous Message
activate P
A <<-- P : Return Message
deactivate P

A ->> P : Asynchronous Message
A -> A : Message to Self

' Note(Messageに対して)
A -> P : Message
note right
 Note: note (left|right) ... end note
 (Or just "note (left|right) : ...")
end note
' Note(Participantに対して)'
note over A, P : note (left|rigght|over) of

' 文字装飾
A -> P : Multi Line\n(Line Break)
A -[#blue]> P : <font color="blue"><b>Change Style</b></font>

' 条件分岐
alt guard condition
  A -> P : Message
else guard condition
  A -> P : Message
end

' Message to SelfはActivateと上手く動作しない
A ->> P : Asynchronous Message
activate P
P -> P : Message to Self
deactivate P

@enduml

生成される図

http://www.plantuml.com/plantuml/png/ZLDTJnD157tVNt5J7qeJAUzDRRBmwaSZZ-h3T3kbQzSPd9t5aF3Wx2gsLW8nGTJW0o58XT0IJDH4g3zcM5YV-0lEJB4k7udTPAUxzvwUUy-vTxCWmwyosf2HEfjIz6JuNJ-7-pBQbQ5wtP7HeOmMzQbJspehkd7_ZHIxIUTzycjVYWyoR5lgoaBIEaWwUt7te-KtN3AB67e2oE6KJL9MnMoAUfH1nKDE8mNmyTC04mUN4UEkuyuYmaU1ttPhwYPu5ApD67rsFOHGVWs5qf9gHolkRYQTvsU7pTDrSRgw5I_takZexB2bWCYNOZjkRiKhIp9ygyIVzh-eY1HBsXSZ7snp8XziQ_OVRxg6JfOjouRn4fIX0DFpnAapIcZWmnti-sW6Qovt3d6iW3OKY-FZ1daVyu2H8QYAKp33Do2q_MiOJJbRPSy3m2bCOw-c3RYhd562ppDgtd6lByMQ59-rxc6ZVwcqaS3ScJgtp5yBnbeOyt2DBvXu3lBvF61IDHaBnkunU1pu739NaGMDpUIi8NhOpuMvNEWfE-Y1pc5b-4tJt4LYnRoWipcWHk79krVntbgo-VBtPZybAF2u2tgr7f8n_GEJ3AD6JYCUtAXu0Nuq01PhLCtDqMjpAwFZcLAnKfgg8wACdERp7YvEL4h52GqhcNgVDev_VekRY_71YeKy3ZC1ObN5GAekTocn0AxOYZqVZuGZLLtYqXpL9ssVRuCKdUCVhqzQRIcMu_RgyT6wCMv7YXV_svBKGfKlrHqkJ7hj_W00

PlantUML インストール方法

これまでUMLを記述するのには、仕事でも個人でもastah* communityを利用していました。astah* communityはバージョン7.0から商用利用不可となりましたが、私のUMLの用途はシステムの概要を説明するために簡単なクラス図とシーケンス図を使っていただけでしたので、有料版を購入するほどのものでもありません。そこでその代替となるツールを探してみることにしました。

Qiitaで人気そうなのはテキストでUMLを記述できるPlantUMLというツールでした。少し使ってみたところ、記法はシンプルで覚えやすく、テキストのために差分比較もでき、Atomプラグインを利用することでリアルタイムプレビューもできるため、中々使いやすいと感じました。大規模の図では探したい場所をテキスト中から探すのにが大変になるかもしれませんが、私の用途では全く困らなそうです。

以下では、PlantUMLのインストール方法を簡単にご紹介しています。

PlantUML

  • UMLをテキストで記述するための言語(DSL
  • 対応しているUMLのダイアグラム
    • シーケンス図
    • ユースケース
    • クラス図
    • アクティビティ図
    • コンポーネント
    • ステートマシン図
    • オブジェクト図
    • 配置図(ベータ版)
    • タイミング図(ベータ版)

Webサイトで簡単に試す

Atomでの編集環境を構築する(Windows10)

  1. Graphvizをインストールする

  2. Atomを起動してPlantUMLプラグインをインストールする

    • メニューから File -> Settings -> Install を選択します。
    • 「plantuml-viewer」を検索してインストールします。
  3. PlantUMLプラグインの設定を更新する

    • メニューから File -> Settings -> Packages -> plantuml-viewer -> Settings を選択します。
      • Charset: UTF-8
      • Graphviz Dot Executable: C:\Program Files (x86)\Graphviz2.38\bin\dot.exe (Graphvizをデフォルト設定のままインストールした場合)