6️⃣

[読書メモ]オブジェクト設計スタイルガイド 7章1~2節 with TypeScript

2024/03/06に公開

オブジェクト設計スタイルガイドを読みながら、TypeScriptでやるならどうやるかを考えながら書きました。
要約的に読める内容になっていると思うので、サクッと3分ぐらいで読める記事となっています。
https://www.oreilly.co.jp/books/9784814400331/

7 タスクの実行

7.1 コマンドメソッドには、命令形の名前を付ける

命令形で、短く、明確な名前を付けることで、コマンドメソッドを使う人が、そのメソッドを使いやすくなります。
良い名前の例: sendReminderEmail

7.2 コマンドメソッドでやることを限定し、イベントを使用して二次的なタスクを実行する

NG

function changeUserPassword(userID: string, newPassword: string) {
  // ユーザーの取得
  const user = userRepository.find(userID)

  // パスワードの変更
  user.password = newPassword

  // パスワード変更後の処理
  sendPasswordChangeEmail(user)

  // パスワード変更後の処理
  logPasswordChange(user)
}

OK:インベントを使用してタスクを複数に分ける

class UserPasswordChanged {
  private UserID: string

  constructor(userID: string) {
    this.UserID = userID
  }

  get userID() {
    return this.UserID
  }
}

function changeUserPassword(userID: string, newPassword: string) {
  // ユーザーの取得
  const user = userRepository.find(userID)

  // パスワードの変更
  user.password = newPassword

  // イベントの発行
  eventDispatcher.dispatch(new UserPasswordChanged(userID))
}

class SendEmail {
  whenUserPasswordChanged(event: UserPasswordChanged) {
    this.mailer.sendPasswordChangeEmail(event.userID)
  }
}

// EventDispatcherの実装
class EventDispatcher {
  private listeners: any[] = []

  constructor(listeners: any[]) {
    this.listeners = listeners
  }

  dispatch(event: any) {
    this.listeners.forEach(listener => {
      if (listener.whenUserPasswordChanged) {
        listener.whenUserPasswordChanged(event)
      }
    })
  }

  addListener(listener: any) {
    this.listeners.push(listener)
  }
}

const listener = new SendEmail()
const eventDispatcher = new EventDispatcher(
  [
    UserPasswordChanged => [listener, 'whenUserPasswordChanged']
  ]
)
eventDispatcher.dispatch(new UserPasswordChanged('user-id'))

メリットとして、コマンドメソッドがシンプルになり、コマンドメソッドの責務が明確になる。
また、コマンドメソッドが複数のリスナーに通知することができる。

デメリットとして、イベントの発行とイベントのリスナーの登録が分かれているため、コードが分散してしまう。
また、イベントの発行とリスナーの登録が分かれているため、リスナーの登録を忘れる可能性がある。

Discussion