LoginSignup
1
0

More than 3 years have passed since last update.

デザインパターン Typescript & Angular 勉強会 #7

Last updated at Posted at 2020-09-24
1 / 9

演習問題解説

//TypeScriptではInterfaceを用いる(抽象クラスでは継承関係がややこしくなり実装が難しくなる)
interface A {
    readonly message: string
}

class B implements A {
    get message() {
        return "HogeFuga"
    }
}

type CConstructor = new (...args: any[]) => A

function C<T extends CConstructor>(Class: T){
    return class extends Class {
        constructor(...args: any[]) {
            super(...args)
        }

        get loudMessage(): string{
            return super.message.toUpperCase()
        }       
    }
}

const D = C(B)
const d = new D()

デザインパターン

T ypeScript でデザインパターンの実装を 1 つか 2 つやらないのであれば、これはオブジェクト指向プログラミングに関する章とは言えません。そうでしょう?
プログラミング TypeScript 5章

  • 広義にはパターン言語の形式に則って定められた各種の設計パターン。クラス設計のパターンからクラウドアーキテクチャのパターン、テスト設計パターンまで種々のパターンが存在する。
    • 興味ある人はこれを読もう image.png
  • 狭義には、GoFの23のデザインパターンを指す

GoFのデザインパターン

  • 23の再利用可能な設計パターン集
    • こういうときにこうする、みたいなものの集合です
    • もう30年くらい前の設計パターンなので古い話も多いです
    • とはいえ、一通り知っておくととてもよいです
    • ちょうぜつエンジニアメモリーちゃんの記事が非常にまとまっていて良いです

こういう本がありますが、まあ表紙だけ覚えておきましょう(一応これが原典です)
image.png

今回はファクトリパターンビルダーパターンをやります。

(一番使いがちなやつです)


ファクトリパターン

  • 複雑なオブジェクトの生成を別クラスに委譲することで、オブジェクトの生成と利用とを明確に分離する設計パターン。
  • 今回は、Factoryそのものは単一だが、メソッドの引数によって生成されるオブジェクトを切り替えるパターン
  • 利用者は 実際に何が作られたのかを意識させないのがポイント

image.png


ファクトリパターン

class User {}

interface IAPIHandler {
    getUser: (id: string) => User
}

class APIHandler implements IAPIHandler {
    getUser(id: string){
        //TODO: fetch
        return new User()
    }
}

class MockAPIHandler implements IAPIHandler {
    getUser(id: string){
        return new User()
    }
}

class APIFactory {
    create(type: "mock" | "http") {
        switch (type) {
            case "mock" : {
                return new MockAPIHandler()
            }
            case "http" : {
                return new APIHandler()
            }
        }
    }
}

ビルダーパターン

  • 複雑なオブジェクトの作成を専用のオブジェクト(ビルダー)に委譲するパターン
  • クラスの初期化が複雑化している場合などに、そのロジック自体を外だしするために使う
    • 典型例: SQLやREST APIなどのクエリビルダ


ビルダーパターン

class APIBuilder {
    private header?: Headers
    private method?: "post" | "get" | "put"
    private uri?: string

    setHeader(header: Headers): APIBuilder{
        this.header = header
        return this
    }

    setMethod(method: "post" | "get" | "put"): APIBuilder{
        this.method = method
        return this
    }

    setUrl(url: string): APIBuilder{
        this.url = url
        return this
    }

    build(): APIHandler {
        return new APIHandler()
    }
}

演習問題

以下にビルダーパターンのサンプルコードを示します。

type Header = {
    name: string,
    value: string
}

type Method = "post" | "get" | "put" | "delete" | "patch"

class APIHandler {}

class APIBuilder {
    private header?: Header[]
    private method?: Method
    private body?: object
    private url?: string

    setHeader(header: Header[]): APIBuilder{
        this.header = header
        return {...this}
    }

    setMethod(method: Method): APIBuilder{
        this.method = method
        return {...this}
    }

    setBody(body: object){
        this.body = body
        return {...this}
    }

    setUrl(url: string): APIBuilder{
        this.url = url
        return {...this}
    }

    build(): APIHandler {
        //TODO
        return new APIHandler()
    }
}

const api = new APIBuilder()
                .setUrl("https://api.github.com/users/octocat")
                .setMethod("get")
                .build()

このAPIビルダーには、「未設定の項目がある状態でビルドできてしまう」とあいう弱点があります。
build メソッドで実行時エラーを発生させてもいいのですが、本来ならば未設定の項目がある場合にコンパイルが通らないほうが好ましいです。

そこで、以下の動作を実現させてみてください。

最低限MethodとUrlが指定されていないと、buildメソッドが呼べないようにする。言い換えると、setMethodとsetUrlが呼ばれていない場合、buildを呼んでもコンパイルエラーとなるようにする。

[応用]メソッドがpostに設定された場合、bodyが設定されていないとbuildメソッドが呼べないようにする

実現のためには、メソッドにおける隠されたthisパラメータ を利用するのが重要になります。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0