11
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

JavaScriptにおけるデザインパターン3(振舞い系)

Last updated at Posted at 2021-06-10

デザインパターンを整理する

デザインパターンを次の3つに分けて考えます

  • 作成系デザインパターン (コンストラクター、ファクトリー、シングルトンなど)
  • 構造系デザインパターン (デコレータ、ファサードなど)
  • 振舞い系デザインパターン (メディエータ、オブザーバーなど)

振舞い系デザインパターン

オブジェクト間の通信を改善または合理化することに重点を置いています。

メディエーターパターン

メディエーターパターンは、オブジェクトのグループがどのように相互作用するかをカプセル化することにより、オブジェクトのグループに対する中央権限を提供します。

// https://github.com/pkellz/devsage/blob/master/DesignPatterns/Mediator.js より

function Member(name) {
  this.name = name
  this.chatroom = null
}

Member.prototype = {
  send: function (message, toMember) {
    this.chatroom.send(message, this, toMember)
  },
  receive: function (message, fromMember) {
    console.log(`${fromMember.name} to ${this.name}: ${message}`)
  }
}

function Chatroom() {
  this.members = {}
}

Chatroom.prototype = {
  addMember: function (member) {
    this.members[member.name] = member
    member.chatroom = this
  },
  send: function (message, fromMember, toMember) {
    toMember.receive(message, fromMember)
  }
}

const chat = new Chatroom()

const bob = new Member('Bob')
const john = new Member('John')
const tim = new Member('Tim')

chat.addMember(bob)
chat.addMember(john)
chat.addMember(tim)

bob.send('Hey, John', john)
john.send("What's up, Bob", bob)
tim.send('John, are you ok?', john)

// 結果
//
// Bob to John: Hey, John
// John to Bob: What's up, Bob
// Tim to John: John, are you ok?

注目して欲しいのは、この例でBobとJohnあるいはJohnとTimなどが直接やりとりしているのではなく、Chatroom(メディエーター)の中でやりとりしていることです。そうすることで、結果にあるようにBob to JohnJohn to Bobなど、誰と誰がやりとりをしているかをChatroom(メディエーター)で監視できているということです。
そのため、メディエーターに処理が集中してパフォーマンスが低下するとリスクもあります。

オブザーバーパターン

Learning JavaScript Design Patterns より

オブザーバーは、オブジェクト (サブジェクトと呼ばれる) がそれに依存するオブジェクト (オブザーバー) のリストを維持し、状態の変更を自動的に通知するデザイン パターンです。


function Subject() {
  this.observers = [] // array of observer functions
}

Subject.prototype = {
  subscribe: function (fn) {
    this.observers.push(fn)
  },
  unsubscribe: function (fnToRemove) {
    this.observers = this.observers.filter((fn) => {
      if (fn != fnToRemove) return fn
    })
  },
  fire: function () {
    this.observers.forEach((fn) => {
      fn.call()
    })
  }
}

const subject = new Subject()

function Observer1() {
  console.log('Observer 1 Firing!')
}

function Observer2() {
  console.log('Observer 2 Firing!')
}

subject.subscribe(Observer1)
subject.subscribe(Observer2)
subject.fire()

subject.unsubscribe(Observer1)
subject.fire()

// 結果
// 
// Observer 1 Firing!
// Observer 2 Firing!
// Observer 2 Firing!

オブザーバーパターンはメディエーターのように中央に権限を集中させるのとは異なり、SubjectとObserverと呼ばれるオブジェクトに処理を分担します。
Subjectは、subjectにObserverを登録(subscribe)、または登録の解除(unsubscribe)、登録されているObserverへ通知を担当。
Obseverは、通知を受けた後の機能を担当。
機能を明確に切り分けることで、コード管理と再利用の可能性を改善できます。

参考文献

Learning JavaScript Design Patterns

関連記事

JavaScriptにおけるデザインパターン1(作成系)
JavaScriptにおけるデザインパターン2(構造系)

## おわりに
Javascriptにおけるデザインパターンを学ぶためには、O'Reillyの Learning JavaScript Design Patternsを読むのがいいと思うのですが、理解するのが難しく感じました。
なので今回、Learning JavaScript Design Patternsを読む前準備として、いくつかのデザインパターンに絞って、個人的に概念の理解にしっくりきた時の文言やコードを記事にしました。
新しい概念に出会った時、毎回理解するのにとても苦労しますが、だからこそやりがいを感じ、飽きることなく続けることができるのだと思います。
人に伝える技術も磨いてさらに良い記事も書けるように頑張っていきたいです。

11
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?