ES2015でPrototype

色々残念だ。心より恥じるしかない。

class Product {
  use(str) {}
  createClone() {}
}

class Manager {
  constructor() {
    this.showcase = new Map()
  }
  register(name, product) {
    this.showcase.set(name, product)
  }
  create(name) {
    const product = this.showcase.get(name)
    return product.createClone()
  }
}

class MessageBox extends Product {
  constructor(decoChar) {
    super()
    this.decoChar = decoChar
  }

  use(str) {
    const length = str.length
    let char = ''
    for (let i = 0; i < length + 4; i++) {
      char += this.decoChar
    }
    console.log(char)
    console.log(`${this.decoChar} ${str} ${this.decoChar}`)
    char = ''
    for (let i = 0; i < length + 4; i++) {
      char += this.decoChar
    }
    console.log(char)
  }

  createClone() {
    return this
  }
}

class UnderLinePen extends Product {
  constructor(ulchar) {
    super()
    this.ulchar = ulchar
  }

  use(str) {
    const length = str.length
    console.log(`\" ${str} \"`)
    let char = ''
    for (let i = 0; i < length; i++) {
      char += this.ulchar
    }
    console.log(char)
  }
  createClone() {
    return this
  }
}

const manager = new Manager()
const upen = new UnderLinePen('~')
const mbox = new MessageBox('*')
const sbox = new MessageBox('/')
manager.register('strong message', upen)
manager.register('warning box', mbox)
manager.register('slash box', sbox)

const p1 = manager.create('strong message')
p1.use('Hello, world')
const p2 = manager.create('warning box')
p2.use('Hello, world')
const p3 = manager.create('slash box')
p3.use('Hello, world')

一番の残念ポイントは、オブジェクトのコピーをしていないところ…。
当初、Object.assign(...)を使っていたのですが、フィールドしかコピーされないんですね。(俗にいうシャローコピー)
Function含めてコピーする方法として、lodashやjQuery使うってのもありましたが、 雑に対応しちゃいました。

ES2015でSingleton

イマイチ感がすごすぎる…。

class Singleton {
  constructor() {}

  echo() {
    console.log('hello')
  }

  static getInstance() {
    if (Singleton.instance === null) {
      Singleton.instance = new Singleton()
    }
    return Singleton.instance
  }
}
Singleton.instance = null

const instance1 = Singleton.getInstance()
const instance2 = Singleton.getInstance()
console.log(instance1 === instance2)
instance1.echo()

getInstance()経由で唯一のインスタンスもらえるのはまあいいのですが、
外からnew出来ちゃうのが問題ですねこれ。

どうSingletonを実現するのがスマートなんだろうか。

エンジニアになる覚悟

Twitterで素晴しい資料が流れてきました。

speakerdeck.com

50Pからの「非機能要件にこだわろう」、がすごいしっくり来ました。
開発現場では、ともすれば機能要件優先になりがちなので、意識して非機能要件を作っておかないといけないなと思いましたです。

非機能要件があまり無いと保守性や不具合調査で大変になりますし…。

ES2015でFactory Method

シリーズ化してきましたね。
Factory Methodはこんな感じですね。

/**
 * 製品を表す抽象クラスもどき。
 * 具体的な処理はサブクラスで定義する。
 */
class Product {
  use() {}
}

/**
 * 工場を表す抽象クラスもどき。
 * 具体的な処理はサブクラスで定義する。
 */
class Factory {
  create(owner) {
    const product = this.createProduct(owner)
    this.registerProduct(product)
    return product
  }
  createProduct(owner) {}
  registerProduct(product) {}
}
/**
 * IDカードを表すクラス。
 */
class IDCard extends Product {
  constructor(owner) {
    super()
    console.log(`${owner}のカードを作ります`)
    this.owner = owner
  }
  use() {
    console.log(`${this.owner}のカードを使います`)
  }
  getOwner() {
    return this.owner
  }
}

class IDCardFactory extends Factory {
  constructor() {
    super()
    this.owners = new Array()
  }
  createProduct(owner) {
    return new IDCard(owner)
  }
  registerProduct(product) {
    this.owners.push(product.getOwner())
  }
  getOwners() {
    return this.owners
  }
}

const factory = new IDCardFactory()
const card1 = factory.create('SCOTT')
const card2 = factory.create('TIGER')
const card3 = factory.create('MIKE')

card1.use()
card2.use()
card3.use()

ES2015でAdapterもどき

こんな感じでしょうか。

/**
 * 既にあるクラス
 */
class Banner {
  constructor(message) {
    this.message = message
  }
  showWithParen() {
    console.log(`( ${this.message} )`)
  }
  showWithAster() {
    console.log(`* ${this.message} *`)
  }
}

/**
 * 文字を表示するための操作が定義されているインタフェースもどき
 */
class Print {
  printWeak() {}
  printStrong() {}
}

/**
 * アダプタークラス
 */
class PrintBanner extends Print {
  constructor(message) {
    super(message)
    this.banner = new Banner(message)
  }

  printWeak() {
    this.banner.showWithParen()
  }

  printStrong() {
    this.banner.showWithAster()
  }
}

const print = new PrintBanner('Hello')
print.printWeak()
print.printStrong()

既に存在しているクラスを部品として再利用したい場合に役立ちそうですね。

ES2015でIteratorもどき

IteratorをES2015で書くならこんな感じかなと。

/**
 * 集合体を表すインタフェースもどき
 */
class Aggregate {
  iterator() {}
}

/**
 * 要素を順次スキャンするための操作を定義するインタフェースもどき
 */
class Iterator {
  hasNext() {}
  next() {}
}

/**
 * 本を表すクラス
 */
class Book {
  constructor(name) {
    this.name = name
  }

  getName() {
    return this.name
  }
}

/**
 * 本棚を本の集合体として見做すクラス
 */
class BookShelf extends Aggregate {
  constructor() {
    super()
    this.books = new Array()
  }

  getBookAt(idx) {
    return this.books[idx]
  }
  appendBook(book) {
    this.books.push(book)
  }

  getLength() {
    return this.books.length
  }

  iterator() {
    return new BookShelfIterator(this)
  }
}

/**
 * 本棚を順次スキャンするイテレータクラス
 */
class BookShelfIterator extends Iterator {
  constructor(bookShelf) {
    super()
    this.bookShelf = bookShelf
    this.idx = 0
  }

  hasNext() {
    return this.idx < this.bookShelf.getLength()
  }

  next() {
    const book = this.bookShelf.getBookAt(this.idx)
    this.idx++
    return book
  }
}

const bookShelf = new BookShelf()
bookShelf.appendBook(new Book('Around the World in 80 days'))
bookShelf.appendBook(new Book('bible'))
bookShelf.appendBook(new Book('Cinderella'))
bookShelf.appendBook(new Book('Daddy-Long-Legs'))
const iterator = bookShelf.iterator()
while (iterator.hasNext()) {
  const book = iterator.next()
  console.log(book)
}

ES2015でTemplateMethodもどき

ES2015はclassベースの構文が導入されています。
これを上手いこと使えばTemplateMethodっぽいことが出来るかなーと思って試してみました。

class AbstractDisplay {
  open() {
    throw new Error('a subclass have to override this open method.')
  }

  print() {
    throw new Error('a subclass have to override this print method.')
  }

  close() {
    throw new Error('a subclass have to override this close method.')
  }

  display() {
    this.open()
    for (let i = 0; i < 5; i++) {
      this.print()
    }
    this.close()
  }
}

class CharDisplay extends AbstractDisplay {
  constructor() {
    super()
  }
  open() {
    console.log('open')
  }
  print() {
    console.log('Hello World')
  }
  close() {
    console.log('close')
  }
}

const char = new CharDisplay()
char.display()

AbstractDisplayで処理の枠組みを定めておき、AbstractDisplayを継承したサブクラスで具体的内容を定義するようにします。
この例では、サブクラスではopen, print, closeをオーバライドすることを期待しています。

単にconsole.logするだけですが、雰囲気は掴んでもらえるかなぁと思っています。