はじめに
iimonでフロントエンジニアをしてますさいとうです。
今回は業務で見かけたabstract
について気になったので調べてみました
abstract classとはなにか
abstract
は抽象クラスを作成する時に宣言する修飾子になります。
抽象クラスとは抽象メソッドを1つ以上持つクラスのことです。
特徴としては以下の点が挙げられます
1.インスタンス化できない
抽象クラス自体は直接インスタンス化できず、インスタンスを作成するには その抽象クラスを継承した具体的なサブクラスを使用する必要があります。
2.抽象メソッドを含むことができる
抽象クラスは抽象メソッドを含むことができ、これらのメソッドは具体的な実装を持たず、サブクラスで必ずオーバーライドされなければなりません。
3.具体的なメソッドやプロパティも持つことができる
抽象メソッドだけでなく、具体的な実装を持つメソッドやプロパティも定義できます。これにより、共通の動作や状態をサブクラスに提供できます。
抽象クラスを使うメリット
オーバーライドしないといけないことにより、メソッドの実装忘れやメソッド名の間違いがあった時にコンパイルエラーが起きてコーディングミスを減らせる。
抽象クラスのままだとインスタンス化できないので、必要なメソッドを必ず実装してからでないと利用できません。
そのため、必ずすべてのメソッドの動作を定義して、未確定のメソッドがない状態にしなければなりません。
このようにチーム開発で実装レベルのルールを作ることができます。
実際にabstractを使ってみる
下記のコードでabstract
を適用してみます。
担当楽器を説明するメソッド(describeInstrument
)を追加したいです。
そしてDescribe
メソッドで使えるようにしてみたいと思います。
class BandMember { constructor(public name: string, public age: number) {} // 自己紹介する Describe(this: BandMember): void { console.log(`My name is${this.name}. I am ${this.age} years old.`); } } class Performer extends BandMember { constructor(name: string, age: number, public instrument: string) { super(name, age); } }
ただサブクラスにdescribeInstrument
を追加します
一見、this
は継承先のクラスなので実装できそうですがご覧のとおりエラーになってしまいます
ここでabstract
メソッドを作成します
そうすることで必ずBandMember
を継承した先では、describeInstrument
というメソッドがあるという保証になり、describeInstrument
を使えるようになります
ただ単にabstract
メソッドを適用すると下記のようなエラーになりますので、クラスもabstract
にしてあげる必要があります
これでdescribeInstrument
がBandMember
クラスで使用できるようになりました。
こうすることで継承先のクラスで必ずdescribeInstrument
メソッドを実装する必要があります。
abstract class BandMember { constructor(public name: string, public age: number) {} // 自己紹介する describe(this: BandMember): void { console.log(`My name is ${this.name}. I am ${this.age} years old.`); this.describeInstrument(); } abstract describeInstrument(): void; } class Performer extends BandMember { constructor(name: string, age: number, public instrument: string) { super(name, age); } // 担当楽器について紹介する describeInstrument(): void { console.log(`I play the ${this.instrument}.`); } } const saxophonist = new Performer('hiroshi', 19, 'saxophone'); saxophonist.describe();
結果
My name is hiroshi. I am 19 years old. I play the saxophone.
先述のとおりabstract
クラスはインスタンスを作成することはできません。
作成しようとすると、、エラーになります。
これはabstract class BandMember
でthis.describeInstrument
がdescribe
メソッド内で
使われてますが、abstract class BandMember
内にthis.describeInstrument
がないためです。
abstractの欠点
抽象クラスには具体的なメソッドの実装を含むことができます。つまり、共通の動作やデフォルトの動作を抽象クラスで提供することができます。それにより実装すべきメンバの型だけを定義し、実装の詳細を含めたくないケースではabstract
は適してません。
抽象クラスに具体的な実装を含めると、抽象クラスの設計意図が曖昧になります。抽象クラスは共通のインターフェースを定義するために使用されるべきですが、具体的な実装が含まれることでその役割が混在してしまいます。
そこでinterface
が使えます。
interface
ではプロパティとメソッドの型定義することができ、具体的な実装は含まれません。
もちろんクラスに継承することができます。(複数のクラスにも)
interface
で先程のコードを書き直し
interface BandMember { name: string; age: number; describe(): void; describeInstrument(): void; } class Performer implements BandMember { constructor(public name: string, public age: number, public instrument: string) {} // 自己紹介する describe(): void { console.log(`My name is ${this.name}. I am ${this.age} years old.`); this.describeInstrument(); } // 担当楽器について紹介する describeInstrument(): void { console.log(`I play the ${this.instrument}.`); } } const saxophonist = new Performer('hiroshi', 19, 'saxophone'); saxophonist.describe();
これは省略記法を使って書いてますが、本来は子クラスでプロパティを宣言する必要があります。
abstractとinterface
個人的に通常のクラス定義はinterface
を使用して、複数のサブクラスに共通のロジックを持たせたい場合にabstract
を使用していけばいいんじゃないかと思いました。
終わりに
abstract
を通じて抽象クラスの概念が学ぶことができました。今個人的にオブジェクト指向を少しずつ学んでいてこちらも大切な概念のため知ることができてよかったです。
弊社ではエンジニアを募集しております。
ぜひカジュアル面談でお話ししましょう!
ご興味ありましたら、ご応募ください!