typescript ポリモーフィズム

ポリモーフィズムを使う場合と使わない場合を例に、
typescriptでポリモーフィズムを使う方法を紹介します。
学校クラスのメソッド内で生徒クラスと先生クラスのパラメータを使うメソッドを使って説明します。

# ポリモーフィズムを使わない場合
1.悪いデザインパターン

schoolクラスにstudentClaimとteacherClaimを実装しています。
これだと、新しいクラスが増える度に、新たにxxxClaimというメソッドを実装する必要があります。

```typescript:index.ts
class Student {
  constructor(public name: string, public age: number) {}
}

class Teacher {
  constructor(public name: string, public age: number) {}
}

class School {
  studentClaim(student: Student): string {
    return `I am a student. name: ${student.name}, age: ${student.age}`
  }

  teacherClaim(teacher: Teacher): string {
    return `I am a teacher. name: ${teacher.name}, age: ${teacher.age}`
  }
}

const student = new Student('tatsumi', 16)
const teacher = new Teacher('ogawa', 47)

const school = new School()
console.log(school.studentClaim(student))
console.log(school.teacherClaim(teacher))
// => I am a student. name: tatsumi, age: 16
// => I am a teacher. name: ogawa, age: 47
```


2.良いデザインパターン

schoolクラスのclaimメソッドの引数にStudentクラスかTeacherクラスが渡るように設定しました。
この方法だと、悪いデザインパターンで説明したように、新しいクラスが増えても新たにxxxClaimというメソッドを実装する必要はなくなりました。

```typescript:index.ts
class Student {
  constructor(public name: string, public age: number) {}
  claim() {
    return `I am a student. name: ${this.name}, age: ${this.age}`
  }
}

class Teacher {
  constructor(public name: string, public age: number) {}
  claim() {
    return `I am a teacher. name: ${this.name}, age: ${this.age}`
  }
}

class School {
  claim(roleable: Student | Teacher): string {
    return roleable.claim()
  }
}

const student = new Student('tatsumi', 16)
const teacher = new Teacher('ogawa', 47)

const school = new School()
console.log(school.claim(student))
console.log(school.claim(teacher))
// => I am a student. name: tatsumi, age: 16
// => I am a teacher. name: ogawa, age: 47
```

しかし、まだ問題は残っています。
新しいクラスが増えた場合にclaimメソッドの引数を更新する必要があります。
例えば、committeeクラスを新たに実装した場合、claimメソッドの引数を
`claim(roleable: Student | Teacher | Committee)`に更新する必要があります。
つまり、新しいクラスを追加する度に既存のclaimメソッドを更新する必要があるということです。
これでは、今後修正が加わる度に、保守やテストが大変になっていきます。

# ポリモーフィズムを使う場合

ポリモーフィズムを使わない場合で説明した問題を解決するために、インターフェイスを使います。
claimメソッドの引数の型をroleインターフェイスに変更することで、roleインターフェイスのメンバ(name, age, claim())を満たすクラスを引数に渡すことができます。
これで、新たにクラスを追加してもそのクラスがroleインターフェイスのメンバを満たしている限り、schoolクラスのclaimメソッドを修正する必要はありません。

```typescript:index.ts
interface Role {
  name: string
  age: number
  claim(): string
}

class Student {
  constructor(public name: string, public age: number) {}
  claim() {
    return `I am a student. name: ${this.name}, age: ${this.age}`
  }
}

class Teacher {
  constructor(public name: string, public age: number) {}
  claim() {
    return `I am a teacher. name: ${this.name}, age: ${this.age}`
  }
}

class School {
  claim(roleable: Role): string {
    return roleable.claim()
  }
}

const student = new Student('tatsumi', 16)
const teacher = new Teacher('ogawa', 47)

const school = new School()
console.log(school.claim(student))
console.log(school.claim(teacher))
// => I am a student. name: tatsumi, age: 16
// => I am a teacher. name: ogawa, age: 47
```

# おまけ
ポリモーフィックを使って、新たにcommitteeクラスを追加した場合は以下のようになります。

```typescript:index.ts
interface Role {
  name: string
  age: number
  claim(): string
}

class Student {
  constructor(public name: string, public age: number) {}
  claim() {
    return `I am a student. name: ${this.name}, age: ${this.age}`
  }
}

class Teacher {
  constructor(public name: string, public age: number) {}
  claim() {
    return `I am a teacher. name: ${this.name}, age: ${this.age}`
  }
}

class Committee {
  constructor(public name: string, public age: number) {}
  claim() {
    return `I am a commitee. name: ${this.name}, age: ${this.age}`
  }
}

class School {
  claim(roleable: Role): string {
    return roleable.claim()
  }
}

const student = new Student('tatsumi', 16)
const teacher = new Teacher('ogawa', 47)
const committee = new Committee('ito', 40)

const school = new School()
console.log(school.claim(student))
console.log(school.claim(teacher))
console.log(school.claim(committee))
// => I am a student. name: tatsumi, age: 16
// => I am a teacher. name: ogawa, age: 47
// => I am a commitee. name: ito, age: 40
```