JavaScriptで学ぶクラスを用いたオブジェクト指向プログラミングの実践ガイド

JavaScriptのクラス構文は、オブジェクト指向プログラミング(OOP)の強力なツールです。OOPは、プログラムをオブジェクトの集合として構成し、それぞれのオブジェクトがデータと機能を持つ手法です。JavaScriptでは、クラスを用いることで、このOOPの概念を直感的に実現できます。本記事では、JavaScriptのクラス構文を使ったOOPの基本から応用までを詳細に解説します。これにより、複雑なアプリケーションを効率的に開発し、コードの再利用性とメンテナンス性を向上させることができます。クラスの定義方法、継承、メソッドやプロパティの利用方法など、具体例を交えながら学んでいきましょう。

目次

オブジェクト指向プログラミングとは

オブジェクト指向プログラミング(OOP)は、プログラムをオブジェクトの集合として設計・実装する手法です。オブジェクトは、データ(プロパティ)とそれを操作する関数(メソッド)を持つ独立した存在として扱われます。

OOPの基本概念

OOPには以下の基本概念があります:

カプセル化

データとメソッドを一つのオブジェクトにまとめ、外部から直接アクセスできないようにすることで、データの保護と管理が容易になります。

継承

既存のクラスを基に新しいクラスを作成し、コードの再利用を促進します。継承により、共通の機能を持つクラス間でコードの重複を避けることができます。

ポリモーフィズム

異なるクラスのオブジェクトが、同じメソッド名で異なる実装を持つことができる仕組みです。これにより、柔軟で拡張性の高いコードを書くことが可能になります。

OOPの重要性

OOPを用いることで、以下の利点があります:

再利用性

クラスを再利用することで、同じコードを何度も書く手間を省き、開発効率を向上させます。

メンテナンス性

コードの構造が明確になるため、バグの修正や機能追加が容易になります。

拡張性

新しい機能を追加する際に、既存のコードに影響を与えずに拡張できるため、柔軟な開発が可能です。

JavaScriptでOOPを実践することで、複雑なアプリケーションの設計・実装が効率的に行え、品質の高いソフトウェアを開発することができます。

JavaScriptにおけるクラスの定義方法

JavaScriptでは、ES6(ECMAScript 2015)以降、クラスを定義するための新しい構文が導入されました。このクラス構文を使うことで、オブジェクト指向プログラミングがより直感的かつ効率的に行えるようになります。

クラスの基本構文

クラスを定義するための基本的な構文は以下の通りです:

class クラス名 {
  constructor(パラメータ1, パラメータ2, ...) {
    // コンストラクタの中でプロパティを初期化
    this.プロパティ1 = パラメータ1;
    this.プロパティ2 = パラメータ2;
  }

  メソッド名() {
    // メソッドの内容
  }
}

例:シンプルなクラスの定義

以下に、Personという名前のクラスを定義する例を示します。このクラスは、名前と年齢というプロパティを持ち、それらを初期化するコンストラクタと、自己紹介を行うメソッドを含んでいます。

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  introduce() {
    console.log(`こんにちは、私の名前は${this.name}です。${this.age}歳です。`);
  }
}

// クラスのインスタンスを作成
const person1 = new Person('太郎', 30);
person1.introduce(); // 出力: こんにちは、私の名前は太郎です。30歳です。

コンストラクタ

コンストラクタは、クラスの新しいインスタンスが作成されるときに呼び出される特殊なメソッドです。上記の例では、Personクラスのコンストラクタがnameageの2つの引数を受け取り、それをインスタンスのプロパティとして初期化しています。

メソッドの定義

クラス内で定義されたメソッドは、インスタンスメソッドとして扱われます。上記の例では、introduceというメソッドが定義されており、これを通じてインスタンスの情報をコンソールに出力することができます。

このように、JavaScriptのクラス構文を使うことで、オブジェクト指向の基本概念を簡潔に実現できます。次に、クラス内のコンストラクタやメソッドの詳細についてさらに掘り下げていきます。

クラスとコンストラクタ

クラスとコンストラクタは、JavaScriptにおけるオブジェクト指向プログラミングの基礎です。ここでは、クラス内のコンストラクタの役割と使い方について詳しく説明します。

コンストラクタの役割

コンストラクタは、新しいインスタンスが生成されるときに自動的に呼び出されるメソッドです。主な役割は、インスタンスの初期化と、必要なプロパティの設定です。

基本的なコンストラクタの定義

以下は、Personクラスのコンストラクタを定義する例です:

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

この例では、nameageの2つのパラメータを受け取り、これらをインスタンスのプロパティとして設定しています。

インスタンスの作成と初期化

コンストラクタを使ってクラスのインスタンスを作成する方法を見てみましょう。

const person1 = new Person('太郎', 30);
console.log(person1.name); // 出力: 太郎
console.log(person1.age);  // 出力: 30

このコードでは、newキーワードを使ってPersonクラスの新しいインスタンスを作成し、nameageのプロパティを初期化しています。

複数のコンストラクタを持つクラス

JavaScriptでは、一つのクラスに複数のコンストラクタを定義することはできません。しかし、コンストラクタ内で条件分岐を使用して、引数に応じた異なる初期化を行うことが可能です。

例:異なる引数に対応するコンストラクタ

以下は、引数の数や型に応じて異なる初期化を行うコンストラクタの例です:

class Person {
  constructor(name, age, gender) {
    this.name = name || '名無し';
    this.age = age || 0;
    this.gender = gender || '不明';
  }
}

const person1 = new Person('太郎', 30, '男性');
const person2 = new Person('花子');

console.log(person1); // 出力: Person { name: '太郎', age: 30, gender: '男性' }
console.log(person2); // 出力: Person { name: '花子', age: 0, gender: '不明' }

この例では、nameagegenderの各パラメータにデフォルト値を設定しています。これにより、引数が不足している場合でも、インスタンスは適切に初期化されます。

まとめ

コンストラクタは、クラスの新しいインスタンスを初期化するための重要なメソッドです。コンストラクタを使うことで、インスタンスのプロパティを設定し、オブジェクト指向プログラミングの基本概念を効率的に実現できます。次に、クラス内でのメソッドとプロパティの定義と使用方法について詳しく見ていきます。

メソッドとプロパティ

JavaScriptのクラス内では、メソッドとプロパティを使ってオブジェクトの動作や状態を定義します。ここでは、クラス内でのメソッドとプロパティの定義とその使用方法について説明します。

プロパティの定義と使用

プロパティはクラスのインスタンスの状態を表す変数です。前回の例で見たように、コンストラクタ内でプロパティを定義し、初期化します。

例:プロパティの定義と使用

以下に、Carクラスのプロパティを定義する例を示します:

class Car {
  constructor(brand, model, year) {
    this.brand = brand;
    this.model = model;
    this.year = year;
  }
}

const myCar = new Car('Toyota', 'Corolla', 2020);
console.log(myCar.brand); // 出力: Toyota
console.log(myCar.model); // 出力: Corolla
console.log(myCar.year);  // 出力: 2020

この例では、brandmodelyearというプロパティを持つCarクラスを定義し、インスタンスを作成しています。

メソッドの定義と使用

メソッドは、クラスのインスタンスが持つ機能や動作を定義する関数です。クラス内でメソッドを定義することで、インスタンスに対する操作をカプセル化できます。

例:メソッドの定義と使用

以下に、Carクラスにメソッドを追加する例を示します:

class Car {
  constructor(brand, model, year) {
    this.brand = brand;
    this.model = model;
    this.year = year;
  }

  startEngine() {
    console.log(`${this.brand} ${this.model}のエンジンを始動します。`);
  }

  displayInfo() {
    console.log(`車の情報: ${this.brand} ${this.model} (${this.year}年式)`);
  }
}

const myCar = new Car('Toyota', 'Corolla', 2020);
myCar.startEngine(); // 出力: Toyota Corollaのエンジンを始動します。
myCar.displayInfo(); // 出力: 車の情報: Toyota Corolla (2020年式)

この例では、startEnginedisplayInfoという2つのメソッドを定義しています。これらのメソッドを使って、インスタンスの動作を定義し、情報を表示しています。

アクセサメソッド

アクセサメソッドは、プロパティの値を取得(ゲッター)または設定(セッター)するためのメソッドです。JavaScriptではgetsetキーワードを使って定義します。

例:ゲッターとセッターの定義

以下に、Personクラスにゲッターとセッターを追加する例を示します:

class Person {
  constructor(name, age) {
    this._name = name;
    this._age = age;
  }

  get name() {
    return this._name;
  }

  set name(newName) {
    this._name = newName;
  }

  get age() {
    return this._age;
  }

  set age(newAge) {
    if (newAge > 0) {
      this._age = newAge;
    } else {
      console.log('年齢は正の数でなければなりません。');
    }
  }
}

const person = new Person('太郎', 30);
console.log(person.name); // 出力: 太郎
person.name = '次郎';
console.log(person.name); // 出力: 次郎
person.age = -5; // 出力: 年齢は正の数でなければなりません。

この例では、_name_ageというプライベートプロパティを定義し、それに対応するゲッターとセッターを使って値の取得と設定を行っています。

まとめ

プロパティとメソッドは、クラスのインスタンスの状態と動作を定義する重要な要素です。ゲッターとセッターを使うことで、プロパティのアクセス制御やデータの整合性を保つことができます。次に、継承とサブクラスについて詳しく見ていきます。

継承とサブクラス

継承は、既存のクラス(親クラスまたはスーパークラス)の機能を引き継ぎ、新しいクラス(子クラスまたはサブクラス)を作成する手法です。これにより、コードの再利用性が向上し、共通の機能を複数のクラスで共有することができます。

継承の基本概念

継承を使うことで、共通のプロパティやメソッドを持つクラス間でコードを共有しやすくなります。これにより、重複コードを減らし、メンテナンス性を高めることができます。

JavaScriptでの継承の実装

JavaScriptでは、extendsキーワードを使ってクラスを継承します。以下は、継承を使用して新しいクラスを定義する例です:

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name}が音を出します。`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }

  speak() {
    console.log(`${this.name}が吠えます。`);
  }

  displayBreed() {
    console.log(`${this.name}は${this.breed}という品種です。`);
  }
}

const myDog = new Dog('ポチ', '柴犬');
myDog.speak();         // 出力: ポチが吠えます。
myDog.displayBreed();  // 出力: ポチは柴犬という品種です。

この例では、Animalクラスを継承してDogクラスを定義しています。Dogクラスは、親クラスのAnimalのプロパティとメソッドを引き継ぎつつ、新しいプロパティbreedとメソッドdisplayBreedを追加しています。

superキーワード

superキーワードは、親クラスのコンストラクタやメソッドを呼び出すために使用されます。子クラスのコンストラクタ内で親クラスのコンストラクタを呼び出す際には、必ずsuperを最初に呼び出す必要があります。

例:superの使用

以下に、superを使って親クラスのメソッドを呼び出す例を示します:

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name}が音を出します。`);
  }
}

class Bird extends Animal {
  constructor(name, color) {
    super(name);
    this.color = color;
  }

  speak() {
    super.speak(); // 親クラスのspeakメソッドを呼び出す
    console.log(`${this.name}が鳴きます。`);
  }
}

const myBird = new Bird('ピーちゃん', '青');
myBird.speak();
// 出力:
// ピーちゃんが音を出します。
// ピーちゃんが鳴きます。

この例では、Birdクラスのsuper.speak()が親クラスAnimalspeakメソッドを呼び出し、その後に独自のメッセージを追加しています。

まとめ

継承とサブクラスを使うことで、コードの再利用性を高め、共通の機能を効率的に共有できます。extendsキーワードを使って継承を実装し、superキーワードを使って親クラスのコンストラクタやメソッドを呼び出すことができます。次に、スーパークラスのメソッドをサブクラスから呼び出す方法について詳しく見ていきます。

スーパークラスのメソッド呼び出し

サブクラスからスーパークラスのメソッドを呼び出すことで、スーパークラスの機能を拡張したり、再利用することができます。これにより、コードの重複を避け、メンテナンスを容易にすることができます。

superキーワードの利用

superキーワードを使うことで、サブクラスからスーパークラスのメソッドを呼び出すことができます。特に、サブクラスのメソッド内でスーパークラスの同名メソッドを呼び出す場合に便利です。

例:スーパークラスのメソッドを呼び出す

以下に、スーパークラスのメソッドをサブクラスから呼び出す例を示します:

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name}が音を出します。`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }

  speak() {
    super.speak(); // スーパークラスのspeakメソッドを呼び出す
    console.log(`${this.name}が吠えます。`);
  }
}

const myDog = new Dog('ポチ', '柴犬');
myDog.speak();
// 出力:
// ポチが音を出します。
// ポチが吠えます。

この例では、Dogクラスのspeakメソッド内でsuper.speak()を呼び出し、スーパークラスであるAnimalspeakメソッドを実行した後に、Dogクラス独自の処理を行っています。

メソッドオーバーライドとsuper

メソッドオーバーライドは、スーパークラスのメソッドをサブクラスで再定義することを指します。オーバーライドされたメソッド内でsuperを使うことで、スーパークラスのメソッドを呼び出しつつ、追加の処理を行うことができます。

例:メソッドオーバーライドとsuperの使用

以下に、メソッドオーバーライドとsuperの使用例を示します:

class Vehicle {
  constructor(type) {
    this.type = type;
  }

  describe() {
    console.log(`これは${this.type}です。`);
  }
}

class Car extends Vehicle {
  constructor(type, brand) {
    super(type);
    this.brand = brand;
  }

  describe() {
    super.describe(); // スーパークラスのdescribeメソッドを呼び出す
    console.log(`ブランドは${this.brand}です。`);
  }
}

const myCar = new Car('車', 'トヨタ');
myCar.describe();
// 出力:
// これは車です。
// ブランドはトヨタです。

この例では、Carクラスのdescribeメソッド内でsuper.describe()を呼び出し、スーパークラスであるVehicledescribeメソッドを実行した後に、Carクラス独自の処理を追加しています。

まとめ

superキーワードを使うことで、サブクラスからスーパークラスのメソッドを呼び出すことができます。これにより、コードの再利用と拡張が容易になり、オブジェクト指向プログラミングの利点を最大限に活用できます。次に、プライベートメソッドとプロパティについて詳しく見ていきます。

プライベートメソッドとプロパティ

プライベートメソッドとプロパティは、クラスの外部からアクセスできないようにすることで、データの保護とクラスの設計の一貫性を保つ役割を果たします。JavaScriptでは、ES6以降、プライベートメソッドとプロパティを定義するための新しい構文が導入されました。

プライベートプロパティ

プライベートプロパティは、クラスの外部から直接アクセスできないようにするプロパティです。JavaScriptでは、プロパティ名の前に#を付けることでプライベートプロパティを定義します。

例:プライベートプロパティの定義

以下に、プライベートプロパティを使用したPersonクラスの例を示します:

class Person {
  #name;
  #age;

  constructor(name, age) {
    this.#name = name;
    this.#age = age;
  }

  getName() {
    return this.#name;
  }

  getAge() {
    return this.#age;
  }

  setName(name) {
    this.#name = name;
  }

  setAge(age) {
    if (age > 0) {
      this.#age = age;
    } else {
      console.log('年齢は正の数でなければなりません。');
    }
  }
}

const person = new Person('太郎', 30);
console.log(person.getName()); // 出力: 太郎
console.log(person.getAge());  // 出力: 30
person.setName('次郎');
person.setAge(25);
console.log(person.getName()); // 出力: 次郎
console.log(person.getAge());  // 出力: 25

この例では、#name#ageというプライベートプロパティを定義し、それにアクセスするためのゲッターとセッターを用意しています。これにより、プロパティへの不正アクセスを防ぎつつ、適切にデータを管理できます。

プライベートメソッド

プライベートメソッドは、クラスの外部から呼び出せないメソッドです。プライベートプロパティと同様に、メソッド名の前に#を付けることで定義します。

例:プライベートメソッドの定義

以下に、プライベートメソッドを使用したBankAccountクラスの例を示します:

class BankAccount {
  #balance;

  constructor(initialBalance) {
    this.#balance = initialBalance;
  }

  #validateAmount(amount) {
    return amount > 0;
  }

  deposit(amount) {
    if (this.#validateAmount(amount)) {
      this.#balance += amount;
      console.log(`${amount}円が預金されました。`);
    } else {
      console.log('無効な金額です。');
    }
  }

  withdraw(amount) {
    if (this.#validateAmount(amount) && amount <= this.#balance) {
      this.#balance -= amount;
      console.log(`${amount}円が引き出されました。`);
    } else {
      console.log('無効な金額または残高不足です。');
    }
  }

  getBalance() {
    return this.#balance;
  }
}

const account = new BankAccount(1000);
account.deposit(500);      // 出力: 500円が預金されました。
account.withdraw(200);     // 出力: 200円が引き出されました。
console.log(account.getBalance()); // 出力: 1300

この例では、#balanceというプライベートプロパティと、#validateAmountというプライベートメソッドを定義しています。#validateAmountメソッドは、金額の妥当性をチェックするために使用され、外部からは直接呼び出せません。

まとめ

プライベートメソッドとプロパティを使うことで、クラス内部のデータを保護し、クラスの設計をより安全かつ一貫性のあるものにできます。これにより、複雑なアプリケーションの開発においても、コードの信頼性と保守性が向上します。次に、静的メソッドとプロパティについて詳しく見ていきます。

静的メソッドとプロパティ

静的メソッドとプロパティは、クラスのインスタンスではなく、クラス自体に属するメンバーです。これにより、インスタンスに依存しない機能を提供することができます。JavaScriptでは、staticキーワードを使って静的メソッドとプロパティを定義します。

静的メソッド

静的メソッドは、クラスのインスタンスを作成しなくても呼び出すことができるメソッドです。これにより、ユーティリティ関数やクラス全体に関連する処理を定義するのに適しています。

例:静的メソッドの定義と使用

以下に、MathUtilクラスに静的メソッドを定義する例を示します:

class MathUtil {
  static add(a, b) {
    return a + b;
  }

  static subtract(a, b) {
    return a - b;
  }
}

console.log(MathUtil.add(5, 3));       // 出力: 8
console.log(MathUtil.subtract(10, 7)); // 出力: 3

この例では、MathUtilクラスにaddsubtractという静的メソッドを定義しています。これらのメソッドは、クラスのインスタンスを作成せずに直接呼び出すことができます。

静的プロパティ

静的プロパティは、クラス自体に属する変数です。これにより、インスタンス間で共有される定数や設定値などを保持するのに便利です。

例:静的プロパティの定義と使用

以下に、Configurationクラスに静的プロパティを定義する例を示します:

class Configuration {
  static appName = 'My Application';
  static version = '1.0.0';

  static getInfo() {
    return `${this.appName} - バージョン: ${this.version}`;
  }
}

console.log(Configuration.appName);     // 出力: My Application
console.log(Configuration.version);     // 出力: 1.0.0
console.log(Configuration.getInfo());   // 出力: My Application - バージョン: 1.0.0

この例では、ConfigurationクラスにappNameversionという静的プロパティを定義しています。これらのプロパティは、クラス名を使って直接アクセスできます。

インスタンスメソッドとの違い

静的メソッドとプロパティは、クラスのインスタンスではなく、クラス自体に関連付けられています。以下に、インスタンスメソッドとの違いを示します:

class Example {
  static staticMethod() {
    console.log('これは静的メソッドです。');
  }

  instanceMethod() {
    console.log('これはインスタンスメソッドです。');
  }
}

Example.staticMethod(); // 出力: これは静的メソッドです。

const exampleInstance = new Example();
exampleInstance.instanceMethod(); // 出力: これはインスタンスメソッドです。

この例では、staticMethodはクラス自体に関連付けられているため、クラス名を使って呼び出します。一方、instanceMethodはクラスのインスタンスに関連付けられているため、インスタンスを使って呼び出します。

まとめ

静的メソッドとプロパティを使うことで、クラスのインスタンスに依存しない機能やデータを提供することができます。これにより、ユーティリティ関数や共通の設定値を効率的に管理することができます。次に、実践例として、シンプルなクラスを使ったアプリケーションの作成について見ていきます。

実践例: シンプルなクラスの作成

ここでは、これまで学んだクラスの概念を応用して、シンプルなクラスを使ったアプリケーションを作成してみましょう。具体的には、図形の面積を計算するクラスを作成します。

図形クラスの定義

まずは、基本的な図形クラスを定義します。このクラスは、面積を計算するための共通メソッドを持ちます。

class Shape {
  constructor(name) {
    this.name = name;
  }

  getArea() {
    console.log('面積計算のメソッドは各サブクラスで実装してください。');
  }

  display() {
    console.log(`${this.name}の面積は${this.getArea()}平方単位です。`);
  }
}

このShapeクラスは、すべての図形の基本となるクラスで、getAreaメソッドを持ちます。getAreaメソッドは、具体的な図形クラスでオーバーライドされるべき抽象メソッドとして定義しています。

長方形クラスの作成

次に、Shapeクラスを継承して、長方形を表すクラスを作成します。

class Rectangle extends Shape {
  constructor(width, height) {
    super('長方形');
    this.width = width;
    this.height = height;
  }

  getArea() {
    return this.width * this.height;
  }
}

const myRectangle = new Rectangle(5, 3);
myRectangle.display(); // 出力: 長方形の面積は15平方単位です。

この例では、RectangleクラスがShapeクラスを継承し、getAreaメソッドをオーバーライドして長方形の面積を計算しています。

円クラスの作成

同様に、円を表すクラスを作成します。

class Circle extends Shape {
  constructor(radius) {
    super('円');
    this.radius = radius;
  }

  getArea() {
    return Math.PI * this.radius * this.radius;
  }
}

const myCircle = new Circle(4);
myCircle.display(); // 出力: 円の面積は50.26548245743669平方単位です。

この例では、CircleクラスがShapeクラスを継承し、getAreaメソッドをオーバーライドして円の面積を計算しています。

三角形クラスの作成

最後に、三角形を表すクラスを作成します。

class Triangle extends Shape {
  constructor(base, height) {
    super('三角形');
    this.base = base;
    this.height = height;
  }

  getArea() {
    return (this.base * this.height) / 2;
  }
}

const myTriangle = new Triangle(6, 4);
myTriangle.display(); // 出力: 三角形の面積は12平方単位です。

この例では、TriangleクラスがShapeクラスを継承し、getAreaメソッドをオーバーライドして三角形の面積を計算しています。

まとめ

この実践例では、Shapeクラスを基本として、RectangleCircleTriangleの各クラスを作成しました。これにより、継承とメソッドオーバーライドの概念を実際に適用し、異なる図形の面積を計算するアプリケーションを構築しました。次に、これまでの理解を深めるための演習問題を紹介します。

演習問題: クラスを使った開発

ここでは、JavaScriptのクラスを使ったオブジェクト指向プログラミングの理解を深めるための演習問題を提供します。これらの問題を通じて、クラスの定義、継承、メソッドのオーバーライドなどの技術を実際に練習してみましょう。

演習1: 四角形クラスの作成

次の仕様に従って、Squareクラスを作成してください。このクラスはShapeクラスを継承し、四角形の面積を計算するメソッドを持ちます。

仕様

  • クラス名: Square
  • プロパティ: side(四角形の一辺の長さ)
  • メソッド: getArea(四角形の面積を返す)
class Square extends Shape {
  constructor(side) {
    super('四角形');
    this.side = side;
  }

  getArea() {
    return this.side * this.side;
  }
}

// テストコード
const mySquare = new Square(4);
mySquare.display(); // 出力: 四角形の面積は16平方単位です。

演習2: 多角形クラスの作成

次の仕様に従って、Polygonクラスを作成してください。このクラスはShapeクラスを継承し、与えられた頂点の座標に基づいて多角形の面積を計算します。

仕様

  • クラス名: Polygon
  • プロパティ: vertices(頂点の座標を格納する配列)
  • メソッド: getArea(多角形の面積を返す)

ヒント: 多角形の面積は座標の配列を使って計算できます。以下はその計算方法の一例です。

class Polygon extends Shape {
  constructor(vertices) {
    super('多角形');
    this.vertices = vertices;
  }

  getArea() {
    let area = 0;
    const n = this.vertices.length;

    for (let i = 0; i < n; i++) {
      const [x1, y1] = this.vertices[i];
      const [x2, y2] = this.vertices[(i + 1) % n];
      area += (x1 * y2) - (y1 * x2);
    }

    return Math.abs(area) / 2;
  }
}

// テストコード
const myPolygon = new Polygon([[0, 0], [4, 0], [4, 3], [0, 3]]);
myPolygon.display(); // 出力: 多角形の面積は12平方単位です。

演習3: 静的メソッドとプロパティの追加

次の仕様に従って、Shapeクラスに静的メソッドと静的プロパティを追加してください。

仕様

  • 静的プロパティ: count(作成されたShapeインスタンスの数)
  • 静的メソッド: getCount(現在のcountの値を返す)
class Shape {
  static count = 0;

  constructor(name) {
    this.name = name;
    Shape.count++;
  }

  getArea() {
    console.log('面積計算のメソッドは各サブクラスで実装してください。');
  }

  display() {
    console.log(`${this.name}の面積は${this.getArea()}平方単位です。`);
  }

  static getCount() {
    return Shape.count;
  }
}

// テストコード
const shape1 = new Shape('形状1');
const shape2 = new Shape('形状2');
console.log(Shape.getCount()); // 出力: 2

まとめ

これらの演習問題を通じて、JavaScriptのクラス、継承、メソッドオーバーライド、静的メソッドとプロパティの使用方法を実践的に学ぶことができます。これにより、オブジェクト指向プログラミングの理解を深め、より複雑なアプリケーションを効率的に開発できるようになります。次に、これまでの内容を総括してまとめます。

まとめ

本記事では、JavaScriptのクラスを使ったオブジェクト指向プログラミングの基本概念から応用までを学びました。まず、オブジェクト指向プログラミングの基本概念であるカプセル化、継承、ポリモーフィズムについて説明しました。次に、JavaScriptにおけるクラスの定義方法、コンストラクタ、メソッドとプロパティ、継承とサブクラス、スーパークラスのメソッド呼び出し、プライベートメソッドとプロパティ、静的メソッドとプロパティについて詳細に解説しました。

実践例では、図形の面積を計算するシンプルなクラスを作成し、具体的な実装方法を示しました。演習問題を通じて、クラスを使った開発の理解を深める機会も提供しました。

JavaScriptのクラスを使ったオブジェクト指向プログラミングをマスターすることで、コードの再利用性、メンテナンス性、拡張性を向上させることができます。これにより、より複雑で高度なアプリケーションの開発が可能になります。今後も実践的なコードを書きながら、オブジェクト指向の原則を深く理解し、効果的に活用していきましょう。

コメント

コメントする

目次