ポリモーフィズムを使う場合と使わない場合を例に、
typescriptでポリモーフィズムを使う方法を紹介します。
学校クラスのメソッド内で生徒クラスと先生クラスのパラメータを使うメソッドを使って説明します。
ポリモーフィズムを使わない場合
1.悪いデザインパターン
schoolクラスにstudentClaimとteacherClaimを実装しています。
これだと、新しいクラスが増える度に、新たにxxxClaimというメソッドを実装する必要があります。
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というメソッドを実装する必要はなくなりました。
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メソッドを修正する必要はありません。
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クラスを追加した場合は以下のようになります。
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