JavaScriptのデザインパターン

JavaScriptでの20以上のデザインパターンの説明

JavaScriptのES6クラスを使ってデザインパターンの実装について議論します。


参考資料

UdemyのDmitri NesterukによるJavaScriptのデザインパターン


🚀 デザインパターンとは何か?

デザインパターンは、ソフトウェア設計で一般的に発生する問題の解決策です。これらのパターンは簡単に再利用可能であり、表現力があります。

Wikipediaによると

ソフトウェアエンジニアリングにおいて、ソフトウェアデザインパターンは、ソフトウェア設計における一般的な問題への再利用可能な解決策です。それは直接ソースコードやマシンコードに変換できる完成されたデザインではありません。それは多くの異なる状況で使用できる問題解決方法の記述またはテンプレートです。

デザインパターンの種類

生成デザインパターン

生成デザインパターンは、オブジェクトを直接インスタンス化する代わりに、オブジェクトを作成します。

Wikipediaによると

ソフトウェアエンジニアリングにおいて、生成デザインパターンは、オブジェクト作成メカニズムを扱うデザインパターンです。基本的なオブジェクト作成形式は、設計の問題を引き起こしたり、設計の複雑さを増加させる可能性があります。生成デザインパターンは、何らかの方法でこのオブジェクト作成をコントロールすることによって、この問題を解決します。

ファクトリメソッド

単一オブジェクトの作成用インターフェースを定義し、どのクラスをインスタンス化するかは子クラスに任せます。

Wikipediaによると:

クラスベースのプログラミングにおいて、ファクトリメソッドパターンは、生成されるオブジェクトの正確なクラスを指定することなくオブジェクトを作成する問題を扱う、生成パターンです。これは、コンストラクターを呼び出すのではなく、ファクトリメソッドを呼び出すことによってオブジェクトを作成することで行われます。これはインターフェースで指定され、子クラスによって実装されるか、基底クラスで実装され、派生クラスによってオプションでオーバーライドされます。

ポイントの例を考えてみましょう。ポイントクラスがあり、カーテシアンポイントとポーラーポイントを作成する必要があります。これを行うためのポイントファクトリを定義します。

CoordinateSystem = {
  CARTESIAN: 0,
  POLAR: 1
};

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  static get factory() {
    return new PointFactory();
  }
}

次にポイントファクトリーを作成します。

class PointFactory {

  static newCartesianPoint(x, y) {
    return new Point(x, y);
  }

  static newPolarPoint(rho, theta) {
    return new Point(rho * Math.cos(theta), rho * Math.sin(theta));
  }
}

ファクトリを使ってみましょう。

let point = PointFactory.newPolarPoint(5, Math.PI/2);
let point2 = PointFactory.newCartesianPoint(5, 6);
console.log(point);
console.log(point2);

抽象ファクトリ

それは具体的なクラスを指定せずに、共通のオブジェクトまたはオブジェクトのグループを作成します。

Wikipediaによると

抽象ファクトリパターンは、具体的なクラスを指定することなく、共通のテーマを持つ個々の工場のグループをカプセル化する方法を提供します。

ドリンクとドリンク作り機の例を使います。

class Drink {
  consume() {}
}

class Tea extends Drink {
  consume() {
    console.log('This is Tea');
  }
}

class Coffee extends Drink {
  consume() {
    console.log(`This is Coffee`);
  }
}

ドリンクファクトリを作ります。

class DrinkFactory {
  prepare(amount) {}
}

class TeaFactory extends DrinkFactory {
  makeTea() {
    console.log(`Tea Created`);
    return new Tea();
  }
}

class CoffeeFactory extends DrinkFactory {
  makeCoffee() {
    console.log(`Coffee Created`);
    return new Coffee();
  }
}

ファクトリを使ってみましょう。

let teaDrinkFactory = new TeaFactory();
let tea = teaDrinkFactory.makeTea();
tea.consume();

ビルダー

それは単純なオブジェクトから複雑なオブジェクトを構築します。

Wikipediaによると

ビルダーパターンは、オブジェクト指向プログラミングにおけるオブジェクト作成のさまざまな問題に柔軟な解決策を提供するために設計されたデザインパターンです。

人間のクラスを使って例を取りあげます。これは人の情報を記録します。

class Person {
  constructor() {
    // 省略:コードブロックが非常に長く、本回答の空間が限られているため、実装の例の一部を省略しています。
    // 以下のコードブロックでも同様です。
  }
  toString() {
    // ...
  }
}

人物のビルダーを作ります。

class PersonBuilder {
  constructor(person = new Person()) {
    this.person = person;
  }
  // ...
}

人物の仕事に関する情報を取るPersonJobBuilderを作ります。

class PersonJobBuilder extends PersonBuilder {
  // ...
}

PersonAddressBuilderは人物の住所情報を保持します。

class PersonAddressBuilder extends PersonBuilder {
  // ...
}

今度はビルダーを使ってみましょう。

let personBuilder = new PersonBuilder();
let person = personBuilder.lives
  // ...
  .build();
console.log(person.toString());

プロトタイプ

それは既存のオブジェクトから新しいオブジェクトを作成します。

Wikipediaによると

プロトタイプパターンはソフトウェア開発における生成デザインパターンです。新しく作成するオブジェクトのタイプが、プロトタイプとしてクローンされて新しいオブジェクトを生産するものによって決定されます。

車の例を使います。

class Car {
  // ...
  clone() {
    return new Car(this.name, this.model);
  }
}

これが使い方です。

let car = new Car();
car.SetName('Audi');

let car2 = car.clone();
car2.SetName('BMW');

シングルトン

それは特定のクラスに対して一つのオブジェクトしか作成されないように確保します。

Wikipediaによると

ソフトウェアエンジニアリングにおいて、シングルトンパターンはクラスのインスタンスが"ただ一つ"に制限されるソフトウェアデザインパターンです。システム全体で行動を調整するために正確に1つのオブジェクトが必要な場合に便利です。

シングルトンクラスを作成します。

class Singleton {
  // ...
}

これが使い方です。

let s1 = new Singleton();
let s2 = new Singleton();
console.log('Are they same? ' + (s1 === s2));
s1.say();

構造デザインパターン

これらのパターンはクラスとオブジェクトの合成に関係し、インターフェースを合成するために継承を使用します。

Wikipediaによると

ソフトウェアエンジニアリングにおいて、構造デザインパターンは、エンティティ間の関係を実現するためのシンプルな方法を特定することで、デザインを容易にするデザインパターンです。

アダプター

このパターンは、互換性のないインターフェースを持つクラスが一緒に動作することを可能にするため、既存のクラスの周りに自分自身のインターフェースをラッピングします。

Wikipediaによると

ソフトウェア工学において、アダプターパターンはソフトウェアデザインパターンであり、既存のクラスのインターフェースを別のインターフェースとして使用することができるようにします。それはしばしば、既存のクラスをソースコードを変更せずに他のものとともに動作させるために使用されます。

電卓の例を使います。Calculator1は古いインターフェースで、Calculator2は新しいインターフェイスです。新しいインターフェースをラップアップし、新しいメソッドを使って結果を得るアダプタを構築します。

class Calculator1 {
  // ...
}

class Calculator2 {
  // ...
}

アダプタクラスを作成します。

class CalcAdapter {
  // ...
}

これが使い方です。

const adaptedCalc = new CalcAdapter();
console.log(adaptedCalc.operations(10, 55, 'sub'));

ブリッジ

それは抽象化から実装を分離し、それぞれが独立して変化することができます。

Wikipediaによると

ブリッジは、大きなクラスまたは密接に関連するクラスのセットを、抽象化と実装の2つの別々の階層に分割する構造デザインパターンです。これらは、互いに独立して開発することができます。

複数の形状のレンダリングクラスを作成します。

class VectorRenderer {
  // ...
}

class RasterRenderer {
  // ...
}

class Shape {
  // ...
}

class Circle extends Shape {
  // ...
}

これが使い方です。

let raster = new RasterRenderer();
let vector = new VectorRenderer();
let circle = new Circle(vector, 5);
circle.draw();
circle.resize(2);
circle.draw();

コンポジット

単一オブジェクトとして操作できるようにオブジェクトを構成します。

Wikipediaによると

コンポジットパターンは、同じオブジェクトタイプとして同じ方法で扱われる一連のオブジェクトについて説明します。

仕事の例を使います。

class Employer{
  // ...
}

GroupEmployerを作成します。

class EmployerGroup{
  // ...
}

これが使い方です。

let zee= new Employer("zee","developer");
let shan= new Employer("shan","developer");

let groupDevelopers = new EmployerGroup("Developers", [zee,shan]);

デコレータ

それはオブジェクトに動的に動作を追加またはオーバーライドします。

Wikipediaによると

デコレータパターンは、同じクラスからの他のオブジェクトの動作に影響を与えることなく、個々のオブジェクトに動作を動的に追加することを可能にするデザインパターンです。

色と形の例を取り上げます。サークルを描くときにメソッドを作成し、サークルを描きます。赤いサークルを描くときには、オブジェクトに動作が追加されます。デコレータパターンはこれを手助けします。

class Shape {
  // ...
}

class Circle extends Shape {
  // ...
}

ColoredShapeクラスを作成します。

class ColoredShape extends Shape {
  // ...
}

これが使い方です。

let circle = new Circle(2);
console.log(circle);

let redCircle = new ColoredShape(circle, "red");
console.log(redCircle.toString());

ファサード

それは複雑なコードに簡素化されたインターフェースを提供します。

Wikipediaによると

ファサードパターン(ファサードもアピール)は、オブジェクト指向プログラミングで一般的に使用されるソフトウェアデザインパターンです。建築におけるファサードに相当するファサードは、より複雑な下層のコードや構造を隠蔽するためにマスキングするためのフロントフェイスインターフェースとして機能するオブジェクトです。



こちらの記事はdev.toの良い記事を日本人向けに翻訳しています。
https://dev.to/zeeshanhshaheen/design-patterns-in-javascript-1pgm