JavaScriptの高度なデザインパターン

JavaScriptの高度なデザインパターンの表紙画像

DhiWise

DhiWise

2021年11月24日に投稿 • 2022年2月18日に更新

概要

20種類以上のデザインパターンをJavaScriptにて説明しました。これらのデザインパターンについて、JavaScript es6のクラスを使って話し合います。

自己紹介

私はDhiWiseでReact.jsデベロッパーをしています。これはNode.js(オープンソース)、Android(Kotlin)、iOS、Laravel、Flutter、Reactのクリーンでスケーラブル、カスタマイズ可能なコードを構築するのを助けるProCodeプラットフォームです。プログラマーとして重要なことに集中しながら、DhiWiseが残りの作業を手伝ってくれます。
https://www.dhiwise.com/?utm_campaign=Dev.to&utm_source=Dev.to
また、学びたい方のために

  • 高度なreactコンポーネントパターンについてはこちらをご覧ください。
  • 高度なReactロードマップについてはこちらをご覧ください。

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

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

Wikipediaによると

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

デザインパターンの種類

  • 生成(Creational)
  • 構造(Structural)
  • 振る舞い(Behavioral)

生成デザインパターン

生成デザインパターンは、直接オブジェクトをインスタンス化するのではなく、オブジェクトを生成します。

Wikipediaによると

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

  • ファクトリーメソッド(Factory Method)
  • 抽象ファクトリー(Abstract Factory)
  • ビルダー(Builder)
  • プロトタイプ(Prototype)
  • シングルトン(Singleton)

ファクトリーメソッド
シングルオブジェクトを作成するためのインターフェースを定義し、どのクラスをインスタンス化するかを子クラスに決定させます。

Wikipediaによると:

クラスベースのプログラミングにおいて、ファクトリーメソッドパターンは、オブジェクトを作成する問題に対処するためにファクトリーメソッドを使用するパターンです。これは、コンストラクタを呼び出すのではなく、工場メソッドを呼び出すことによってオブジェクトを作成するために行われます。工場メソッドは、インターフェースで指定され、子クラスによって実装されるか、またはベースクラスに実装され、必要に応じて派生クラスによってオーバーライドされます。

ポイントの例を取りましょう。ポイントクラスがあり、カルテシアンポイントと極座標のポイントを作成する必要があります。ポイントファクトリーを定義し、この作業を行います。

ファクトリーメソッドの例:

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));
    }
}

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

抽象ファクトリー

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

Wikipediaによると

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

飲み物と飲み物製造機の例を使います。

抽象ファクトリーの例:

class HotDrinkFactory {
    prepare(amount) { /* abstract */ }
}

class TeaFactory extends HotDrinkFactory {
    prepare(amount) {
        console.log(`Put in tea bag, boil water, pour ${amount}ml`);
        return new Tea();
    }
}

class CoffeeFactory extends HotDrinkFactory {
    prepare(amount) {
        console.log(`Grind some beans, boil water, pour ${amount}ml`);
        return new Coffee();
    }
}

ビルダー

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

Wikipediaによると

ビルダーパターンは、オブジェクト指向プログラミングのさまざまなオブジェクト作成問題に柔軟なソリューションを提供するように設計されたデザインパターンです。

人物の情報を格納する人物クラスの例を用います。

ビルダーの例:

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

    setAge(age) {
        this.person.age = age;
        return this;
    }

    setPhone(phone) {
        this.person.phone = phone;
        return this;
    }

    build() {
        return this.person;
    }
}

class Person {
    constructor(name) {
        this.name = name;
        // ageとphoneはオプション
    }
}

プロトタイプ

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

Wikipediaによると

プロトタイプパターンは、ソフトウェア開発において使用される生成デザインパターンです。プロトタイプインスタンスによって作成するオブジェクトのタイプが決定され、その後に新しいオブジェクトを生成するためにクローンが作成されます。

車の例を使います。

プロトタイプの例:

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

    clone() {
        return new Car(this.model, this.price);
    }
}

シングルトン

あるクラスのために、一つのオブジェクトのみが作成されることを確実にします。

Wikipediaによると

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

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

シングルトンの例:

class Singleton {
    constructor() {
        if (!Singleton.instance) {
            Singleton.instance = this;
        }
        return Singleton.instance;
    }

    someMethod() {
        // ...
    }
}

const instance = new Singleton();
Object.freeze(instance);

構造デザインパターン

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

Wikipediaによると

ソフトウェアエンジニアリングにおける構造デザインパターンは、エンティティ間の関係を認識する簡単な方法を識別することによって設計を容易にするデザインパターンです。

  • アダプタ(Adapter)
  • ブリッジ(Bridge)
  • コンポジット(Composite)
  • デコレータ(Decorator)
  • ファサード(Facade)
  • フライウェイト(Flyweight)
  • プロキシ(Proxy)

アダプタ

このパターンは、互換性のないインターフェースを持つクラスが、独自のインターフェースを既存のクラスの周囲にラッピングすることで協力することを可能にします。

Wikipediaによると

ソフトウェアエンジニアリングにおけるアダプターパターンは、既存のクラスのインターフェースを別のインターフェースとして使用できるようにするソフトウェアデザインパターンです。それはしばしば、既存のクラスをそのソースコードを変更することなく他のクラスと一緒に機能させるために使用されます。

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

アダプタの例:

class Calculator1 {
    constructor() {
        this.operations = function(value1, value2, operation) {
            switch (operation) {
                case 'add':
                    return value1 + value2;
                case 'sub':
                    return value1 - value2;
                default:
                    return NaN;
            }
        };
    }
}

class Calculator2 {
    constructor() {
        this.add = function(value1, value2) {
            return value1 + value2;
        };
        this.sub = function(value1, value2) {
            return value1 - value2;
        };
    }
}

class CalculatorAdapter {
    constructor() {
        const calc2 = new Calculator2();

        this.operations = function(value1, value2, operation) {
            switch (operation) {
                case 'add':
                    // using the new implementation under the hood
                    return calc2.add(value1, value2);
                case 'sub':
                    return calc2.sub(value1, value2);
                default:
                    return NaN;
            }
        };
    }
}

ブリッジ

抽象化を実装から分離し、両者を独立して変更できるようにします。

Wikipediaによると

ブリッジは構造的デザインパターンであり、大きなクラスまたは一連の密接に関連するクラスを、抽象化と実装という二つの別々の階層に分けることを可能にします。それらはそれぞれ独立して開発することができます。

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

ブリッジの例:

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

    draw() {
        this.renderer.renderShape(this);
    }
}

class Circle extends Shape {
    constructor(renderer, radius) {
        super(renderer);
        this.radius = radius;
    }
}

class Square extends Shape {
    constructor(renderer, side) {
        super(renderer);
        this.side = side;
    }
}

class VectorRenderer {
    renderShape(shape) {
        console.log(`Drawing ${shape.constructor.name} as lines`);
    }
}

class RasterRenderer {
    renderShape(shape) {
        console.log(`Drawing ${shape.constructor.name} as pixels`);
    }
}

コンポジット

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

Wikipediaによると

コンポジットパターンは、同じ種類のオブジェクトの一群を同じ方法で扱うことを記述するパターンです。

仕事の例を使います。

コンポジットの例:

class Employee {
    constructor(name, role) {
        this.name = name;
        this.role = role;
    }
}

class Department {
    constructor() {
        this.employees = [];
    }

    addEmployee(employee) {
        this.employees.push(employee);
    }
}

デコレータ

オブジェクトの動作を動

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