2022

1

16

GoFデザインパターンを復習してやんよ!!!(振る舞いパターン①)

タグ: |

Pocket
LINEで送る
Facebook にシェア
reddit にシェア
LinkedIn にシェア

現在、GoFデザインパターンを復習中。
前回までは生成パターンと構造パターンを掘り下げ

今回からは振る舞いパターンの6つ(Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Mement)を掘り下げ

Chain of Responsibilityパターン

  • ハンドラーのチェーンに沿ってリクエストを渡すことができる動作設計パターン
  • 要求の受取り対象となる複数のオブジェクトに鎖状の関係を構築し、要求を処理する事が可能なオブジェクトに渡るまで、順次、構築した鎖状の関係に沿って要求を受流させる役割

推奨されるケース

  • 要求が多数のパターンになることが予想されるが、正確な種類の要求とそのシーケンスが事前に不明な場合
  • 複数のハンドラーを特定の順序で実行させたい場合

サンプル

受信メールのアドレスごとの受信箱フィルターレベル設定の例

from __future__ import annotations from abc import ABC, abstractmethod from typing import Any, Optional # 処理ハンドラークラス class Handler(ABC): @abstractmethod def set_next(self, handler: Handler) -> Handler: pass @abstractmethod def handle(self, request) -> Optional[str]: pass # ハンドラーチェーンの管理クラス class AbstractHandler(Handler): _next_handler: Handler = None def set_next(self, handler: Handler) -> Handler: self._next_handler = handler return handler # 優先順位振り分け @abstractmethod def handle(self, request: Any) -> str: if self._next_handler: return self._next_handler.handle(request) return None # 仕事用アドレス管理クラス class BusinessHandler(AbstractHandler): def handle(self, request: Any) -> str: if request == "仕事": return f"└ ○ 上司からのメールを「{request}フォルダ」に移動しました" else: return super().handle(request) # プライベート用アドレス管理クラス class PrivateHandler(AbstractHandler): def handle(self, request: Any) -> str: if request == "プライベート": return f"└ ○ 友人からのメールを「{request}フォルダ」に移動しました" else: return super().handle(request) # 身元不明アドレス管理クラス class UnknownHandler(AbstractHandler): def handle(self, request: Any) -> str: if request == "その他": return f"└ ○ 登録済みのメールを「{request}フォルダ」に移動しました" else: return super().handle(request) # クライアント側のメールボックス def client_code(handler: Handler) -> None: # アドレス帳から受信メールを選別 for folder in ["仕事", "プライベート", "その他", "スパム"]: print(f"\n {folder}フォルダ") result = handler.handle(folder) if result: print(f" {result}", end="") # print(f" └ △ 「迷惑メール」フォルダに入れました", end="") else: print(f" └ ✕ スパムメールを「迷惑メール」フォルダに入れました", end="") if __name__ == "__main__": business = BusinessHandler() # 仕事用アドレス private = PrivateHandler() # プライベート用アドレス unknown = UnknownHandler() # その他アドレス # メールフィルター優先度設定(仕事 > プライベート > 不明) unknown.set_next(private).set_next(business) print("【①メインフィルター】: 仕事とプライベート以外はスパム扱い") client_code(private) print("\n-------------------------------------------------------------\n") print("【②サブフィルター】: 不明アドレスでも受信許可リストにあれば受信") client_code(unknown)
  • Handlerクラスでhandle(ハンドラ)とset_next(優先順位)を管理
  • AbstractHandlerクラスで各アドレス管理クラスとチェーンの関係性とを管理
  • 実行時に各アドレス管理のインスタンスに優先度を設定
  • client_codeクラスで指定した優先度(インスタンス)条件に従って届いたメールのアドレスを受信箱or迷惑メールに振り分ける
【①メインフィルター】: 仕事とプライベート以外はスパム扱い

 仕事フォルダ
  └ ○ 上司からのメールを「仕事フォルダ」に移動しました
 プライベートフォルダ
  └ ○ 友人からのメールを「プライベートフォルダ」に移動しました
 その他フォルダ
   └ ✕  スパムメールを「迷惑メール」フォルダに入れました
 スパムフォルダ
   └ ✕  スパムメールを「迷惑メール」フォルダに入れました
-------------------------------------------------------------

【②サブフィルター】: 不明アドレスでも受信許可リストにあれば受信

 仕事フォルダ
  └ ○ 上司からのメールを「仕事フォルダ」に移動しました
 プライベートフォルダ
  └ ○ 友人からのメールを「プライベートフォルダ」に移動しました
 その他フォルダ
  └ ○ 登録済みのメールを「その他フォルダ」に移動しました
 スパムフォルダ
  └ ✕  スパムメールを「迷惑メール」フォルダに入れました

メリット

  • 要求を自身で処理できる場合は処理し、できない場合は、次のハンドラに渡す役割分担ができるので単一責任の原則を保持できる
  • 既存コードを変更することなく新しいハンドラーを導入できるのでオープン/クローズド原則を保てる

デメリット

  • チェーンが非常に長い場合、問題がどこにあるかをデバッグおよび検証することは困難になる
  • 呼び出しスタックも大きくなり、パフォーマンスの問題が発生する可能性がある
  • 構造によってはハンドラー間でコードが重複し、メンテナンスや特定パターンのデバッグが増える可能性がある

Commandパターン

  • リクエストに関するすべての情報を含むスタンドアロンオブジェクトに変換する動作設計パターン
  • 実行されるタスクの再利用と、状態維持で元に戻せない操作をサポートする役割

推奨されるケース

  • トリガー実行時の結果に伴う付加情報やサポートの機能をまとめて実装したい
  • リクエスト状態の追跡が必要で、追加or削除実行or取り消しの結果が対になっている実装が必要

サンプル

加算or減算の実行コマンドに実行内容と現在の値のパラメータ表示をカプセル化実装した場合

import abc

# 現在値表示クラス
class data:
    _data = 0
    def __init__(self, value=0) -> None:
        data._data += value
    @abc.abstractmethod
    def add(self):
        pass
    @abc.abstractmethod
    def subtract(self):
        pass
    def __str__(self) -> str:
        return f"現在の値: {data._data}"

# 加算処理クラス
class Addition(data):
    def __init__(self, value=0) -> None:
        super().__init__(value)
    def add(self, value):
        data._data += value
    def __str__(self) -> str:
        return super().__str__()

# 減算処理クラス
class Subtraction(data):
    def __init__(self, value=0) -> None:
        super().__init__(value)
    def subtract(self, value):
        data._data -= value
    def __str__(self) -> str:
        return super().__str__()

# コマンド管理クラス
class Command():
    @abc.abstractmethod
    def execute(self):
        pass
    @abc.abstractmethod
    def display(self):
        pass
    @abc.abstractmethod
    def undo(self):
        pass

# 加算コマンド
class AdditionCommand(Command):
    def __init__(self, obj, value):
        self.obj = obj
        self.value = value
    def execute(self):
        self.obj.add(self.value)
    def display(self):
        print(f"-> 値を{self.value}増やしました")
        print(f"-> {obj}")
        print("----------------------------------------------------------------------------")
    def undo(self, commandObj):
        commandObj.set_command(SubtractionCommand(Subtraction(), self.value))
        commandObj.invoke()

# 減算コマンド
class SubtractionCommand(Command):
    def __init__(self, obj, value):
        self.obj = obj
        self.value = value
    def execute(self):
        self.obj.subtract(self.value)
    def display(self):
        print(f"-> 値を{self.value}減らしました")
        print(f"-> {obj}")
        print("----------------------------------------------------------------------------")
    def undo(self, commandObj):
        commandObj.set_command(AdditionCommand(Addition(), self.value))
        commandObj.invoke()

# リクエスト管理
class ActionInvoker:
    def __init__(self, command):
        self.command = command
    def set_command(self, command):
        self.command = command
    def invoke(self):
        self.command.execute()
        self.command.display()
    def undo(self):
        print("└操作をやり直し\n", end="")
        self.command.undo(self)

if __name__ == '__main__':
	  obj = data(100)
    command_addition = AdditionCommand(Addition(), 10)    # 加算インスタンス
    command_subtraction = SubtractionCommand(Subtraction(), 10)   # 減算インスタンス
 

    print(f"初期値:{obj}")    
    print("①値を10増やす")
    action_invoker = ActionInvoker(command_addition)
    action_invoker.invoke()
    print("②値を10減らす")
    action_invoker.set_command(command_subtraction)
    action_invoker.invoke()
    print("③②の操作を取消し")
    action_invoker.undo()
    print("④③の操作を取消し")
    action_invoker.undo()
    print("⑤④の操作を取消し")
    action_invoker.undo()
  • 命令インターフェースのcommandクラスと、実際の指令役のAdditionCommandSubtractionCommandにexecute(実行)、display(表示)、undo(やり直し)を用意
  • 起動者役のActionInvokerクラスでset_command(指令)、invoke(起動&結果表示)、undo(やり直し)の具体的実行処理を管理
  • undo(やり直し)時にはリクエストの指令と反対の指令コマンド処理を呼び出す
初期値:現在の値: 100
----------------------------------------------------------------------------
①値を10増やす
-> 値を10増やしました
-> 現在の値: 110
----------------------------------------------------------------------------
②値を10減らす
-> 値を10減らしました
-> 現在の値: 100
----------------------------------------------------------------------------
③②の操作を取消し
└操作をやり直し
-> 値を10増やしました
-> 現在の値: 110
----------------------------------------------------------------------------
④③の操作を取消し
└操作をやり直し
-> 値を10減らしました
-> 現在の値: 100
----------------------------------------------------------------------------
⑤④の操作を取消し
└操作をやり直し
-> 値を10増やしました
-> 現在の値: 110

メリット

  • 操作を呼び出すクラスを、これらの操作を実行するクラスから切り離すことで単一責任の原則が保てる
  • 既存のクライアントコードを壊すことなく、アプリに新しいコマンドを導入できるのでオープン/クローズド原則が保てる
  • アクションの実行またはイベントのトリガーに必要なすべての情報をいつでもカプセル化できるので、ビジネスロジックを分散化しやすい

デメリット

  • あまりにこまかな機能をカプセル化した場合にアプリケーションを過度に複雑にする可能性がある
  • 複雑化したスタックによって即時結果を求められる場合に問題が出る可能性がある
  • 送信者と受信者の間が単方向接続である事が基本となるので、分散データベースのような同期を取るタイミングが様々なパターンのインフラの場合に結果整合性に問題が出る可能性がある

Interpreterパターン

  • 言語が与えられたら、その文法の表現を、その表現を使用してその言語の文を解釈するインタプリタとともに定義する動作設計パターン
  • 1つの規則を1つのクラスで表す役割

サンプル

# 抽象クラス
class AbstractExpression():
    @staticmethod
    def interpret():
        pass

# 数値化クラス
class Number(AbstractExpression):
    def __init__(self, value):
        self.value = int(value)
    def interpret(self):
        return self.value
    def __repr__(self):
        return str(self.value)

# 結合クラス
class Add(AbstractExpression):
    def __init__(self, left, right):
        self.left = left
        self.right = right
    def interpret(self):
        return self.left.interpret() + self.right.interpret()
    def __repr__(self):
        return f"({self.left} + {self.right})"

# 減算クラス
class Subtract(AbstractExpression):
    "Non-Terminal Expression"
    def __init__(self, left, right):
        self.left = left
        self.right = right
    def interpret(self):
        return self.left.interpret() - self.right.interpret()
    def __repr__(self):
        return f"({self.left} - {self.right})"


if __name__ == '__main__':
    SENTENCE = "5 + 4 - 3 + 7 - 2"
    print(f"文字列出力:{SENTENCE}")

    # スペースごとに
    splits = SENTENCE.split(" ")
    print(f"①分割出力:{splits}")

    # 単語から抽象構文木(Abstract Syntax Tree)を手動で作成
    AST = []
    AST.append(Add(Number(splits[0]), Number(splits[2])))  # 5 + 4
    AST.append(Subtract(AST[0], Number(splits[4])))        # ^ - 3
    AST.append(Add(AST[1], Number(splits[6])))             # ^ + 7
    AST.append(Subtract(AST[2], Number(splits[8])))        # ^ - 2
    print(f"②抽象構文木を探索:{AST}")

    # 最後のAST行をルートノードとして使用
    AST_ROOT = AST.pop()

    #ルートから開始する完全なASTを再帰的に解釈
    print(f"③数値として結合出力:{AST_ROOT.interpret()}")

    # AST_ROOTの表現を印刷します
    print(f"④抽象構文木を結合出力:{AST_ROOT}")
文字列出力:5 + 4 - 3 + 7 - 2
①分割出力:['5', '+', '4', '-', '3', '+', '7', '-', '2']
②抽象構文木を探索:[(5 + 4), ((5 + 4) - 3), (((5 + 4) - 3) + 7), ((((5 + 4) - 3) + 7) - 2)]
③数値として結合出力:11
④抽象構文木を結合出力:((((5 + 4) - 3) + 7) - 2)
Interpreter図

②の部分で抽象構文木の構造を手動で書いたが、ここをCompositeパターンで再帰的に処理する構造にすると最適化されると思われる

メリット

  • 独自の文法の実装を容易にする

デメリット

  • 文法が複雑になると、メンテナンスのコストが増える
  • 適用できる領域が限られている

Iteratorパターン

  • 何かがたくさん集まっているときに、それを順番に指し示していき、全体をスキャンしていく処理を行う動作設計パターン
  • 複数の要素を管理するクラスの全ての要素に対して、順次アクセスさせる役割

推奨されるケース

  • アプリケーション固有なデータ、構造を持ったオブジェクトにアクセスをする場合
  • 集合の要素に順次にアクセスする必要がある場合

サンプル

音楽プレイヤーのプレイリストの変更例

from __future__ import annotations
from collections.abc import Iterable, Iterator
from typing import Any, List

# オーダークラス
class OrderIterator(Iterator):
    _position: int = None
    _reverse: bool = False

    def __init__(self, collection: MusicCollection, reverse: bool = False) -> None:
        self._collection = collection
        self._reverse = reverse
        self._position = -1 if reverse else 0

    def __next__(self):
        try:
            value = self._collection[self._position]
            self._position += -1 if self._reverse else 1
        except IndexError:
            raise StopIteration()

        return value

# 楽曲オブジェクトコレクションクラス
class MusicCollection(Iterable):
    def __init__(self, collection: List[Any] = []) -> None:
        self._collection = collection

    def __iter__(self) -> OrderIterator:
        return OrderIterator(self._collection)

    def get_reverse_iterator(self) -> OrderIterator:
        return OrderIterator(self._collection, True)

    def add_item(self, item: Any):
        self._collection.append(item)


if __name__ == "__main__":
    collection = MusicCollection()
    collection.add_item("1曲目")
    collection.add_item("2曲目")
    collection.add_item("3曲目")

    print("通常プレイリスト")
    print("\n".join(collection))
    print("")

    print("逆順プレイリスト")
    print("\n".join(collection.get_reverse_iterator()), end="")
通常プレイリスト
1曲目
2曲目
3曲目

逆順プレイリスト
3曲目
2曲目
1曲目

メリット

  • 新しいタイプのコレクションとイテレータを実装し、何も壊さずに既存のコードに渡すことができるのでオープン/クローズド原則を保てる
  • アルゴリズムを個別のクラスに抽出することで、クライアントコードとコレクションを分離できるので単一責任の原則を保てる

デメリット

  • 一部の特殊なコレクションの要素を直接調べるよりも効率が低下する場合がある

Mediatorパターン

  • オブジェクト間の無秩序な依存関係を減らすことができる動作設計パターン
  • 仲介者を通して各クラスを操作する事によって、クラス同士の結合度を下げる役割

推奨されるケース

  • オブジェクトが大量にあり、処理を集約したいとき
  • 複雑かつ複数の条件処理の蜜結合を避けたいとき

サンプル

スマートスピーカーでたくさんのオブジェクトを仲裁クラスで連動させる例

from __future__ import annotations
from abc import ABC

# 仲裁クラス
class Mediator(ABC):
    def notify(self, sender: object, event: str) -> None:
        pass

# 仲裁具象クラス
class ConcreteMediator(Mediator):
    def __init__(self, component1: Component1, component2: Component2, component3: Component3) -> None:
        self._component1 = component1
        self._component1.mediator = self
        self._component2 = component2
        self._component2.mediator = self
        self._component3 = component3
        self._component3.mediator = self
    def notify(self, sender: object, event: str) -> None:
        if event == "照明の電源OFF":
            print("\n●照明が消えたので「消灯」イベントを連動")
            self._component1.do_b()
         
        elif event == "スマートロック":
            print("\n●スマートロックが起動したので「お出かけ」イベントを連動")
            self._component1.do_a()
            self._component2.do_d()
            self._component3.do_e()

        elif event == "ペットカメラ録画":
            print("\n●ライブ録画が起動したので「留守中」イベントを連動")
            self._component3.do_f()

# コンポーネント管理クラス
class BaseComponent:
    def __init__(self, mediator: Mediator = None) -> None:
        self._mediator = mediator

    @property
    def mediator(self) -> Mediator:
        return self._mediator

    @mediator.setter
    def mediator(self, mediator: Mediator) -> None:
        self._mediator = mediator


# 消灯コンポーネントクラス
class Component1(BaseComponent):
    def do_a(self) -> None:
        print('└消灯コンポーネントが【照明の電源OFF】を開始')
        self.mediator.notify(self, "照明の電源OFF")

    def do_b(self) -> None:
        print('└消灯コンポーネントが【テレビの電源OFF】を実行')
        self.mediator.notify(self, "テレビの電源OFF")


# 留守中コンポーネントクラス
class Component2(BaseComponent):
    def do_c(self) -> None:
        print('└留守中コンポーネントが【スマートロック】を実行')
        self.mediator.notify(self, "スマートロック")        

    def do_d(self) -> None:
        print('└留守中コンポーネントが【ロボット掃除機】を実行')
        self.mediator.notify(self, "ロボット掃除機")


# ペットコンポーネントクラス
class Component3(BaseComponent):
    def do_e(self) -> None:
        print('└ペットコンポーネントが【ペットカメラ録画】を実行')
        self.mediator.notify(self, "ペットカメラ録画")

    def do_f(self) -> None:
        print('└ペットコンポーネントが【餌やり機】を実行')
        self.mediator.notify(self, "餌やり機")


if __name__ == "__main__":
    c1 = Component1()   # 消灯コンポーネント
    c2 = Component2()   # 留守中コンポーネント
    c3 = Component3()   # ペットコンポーネント
    mediator = ConcreteMediator(c1, c2, c3)

    print('①音声「おやすみ」をリクエスト')
    c1.do_a()   # 照明OFFをトリガー

    print("\n------------------------------------------------------------------\n\n", end="")
    print('②音声「行ってきます」をリクエスト')
    c2.do_c()   # スマートロックをトリガー
①音声「おやすみ」をリクエスト
└消灯コンポーネントが【照明の電源OFF】を開始

●照明が消えたので「消灯」イベントを連動
└消灯コンポーネントが【テレビの電源OFF】を実行

------------------------------------------------------------------

②音声「行ってきます」をリクエスト
└留守中コンポーネントが【スマートロック】を実行

●スマートロックが起動したので「お出かけ」イベントを連動
└消灯コンポーネントが【照明の電源OFF】を開始

●照明が消えたので「消灯」イベントを連動
└消灯コンポーネントが【テレビの電源OFF】を実行
└留守中コンポーネントが【ロボット掃除機】を実行
└ペットコンポーネントが【ペットカメラ録画】を実行

●ライブ録画が起動したので「留守中」イベントを連動
└ペットコンポーネントが【餌やり機】を実行

メリット

  • さまざまなコンポーネント間の通信を1つの場所に抽出できるため単一責任の原則を保てる
  • 実際のコンポーネントを変更することなく、新しいメディエーターを導入できるのでオープン/クローズド原則を保てる
  • メディエーターがシステムに必要な変更を担当するようになるためサブクラス化が削減できる

デメリット

  • メディエーター自体がすべての異なるクラスと密結合になるため、複雑になった際に保守が困難になる可能性がある

Mementパターン

  • 実装の詳細を明らかにすることなく、オブジェクトの以前の状態を保存および復元できる動作設計パターン
  • データ構造に対する操作を記録し、以前の状態に復帰もしくは操作再現を行う役割

推奨されるケース

  • デザインツールなどにあるアクション履歴のに戻れる機能を実装したいとき

サンプル

テキストをランダム文字列に変更した履歴からバックアップする例

from __future__ import annotations
from abc import ABC, abstractmethod
from datetime import datetime
from random import sample
from string import ascii_letters, digits

# オリジネータークラス
class Originator():
    _state = None
    def __init__(self, state: str) -> None:
        self._state = state
        print(f"オリジネーター: 初期状態: {self._state}")

    def do_something(self) -> None:
        print("オリジネーター: 作業中")
        self._state = self._generate_random_string(30)
        print(f"オリジネーター: 作業状態を変更しました: {self._state}")

    def _generate_random_string(self, length: int = 10) -> None:
        return "".join(sample(ascii_letters, length))

    def save(self) -> Memento:
        return ConcreteMemento(self._state)

    def restore(self, memento: Memento) -> None:
        self._state = memento.get_state()
        print(f"オリジネーター: 状態を変更しました: {self._state}")

# 発信者クラス
class Memento(ABC):
    @abstractmethod
    def get_name(self) -> str:
        pass

    @abstractmethod
    def get_date(self) -> str:
        pass

# 発信者具象クラス
class ConcreteMemento(Memento):
    def __init__(self, state: str) -> None:
        self._state = state
        self._date = str(datetime.now())[:19]

    def get_state(self) -> str:
        return self._state

    def get_name(self) -> str:
        return f"{self._date} / ({self._state}...)"

    def get_date(self) -> str:
        return self._date

# 世話人クラス
class Caretaker():
    def __init__(self, originator: Originator) -> None:
        self._mementos = []
        self._originator = originator

    def backup(self) -> None:
        print("世話人: 現在の状態をバックアップ...\n")
        self._mementos.append(self._originator.save())

    def undo(self) -> None:
        if not len(self._mementos):
            return
        memento = self._mementos.pop()
        print(f"世話人: 状態を復元: {memento.get_name()}")
        try:
            self._originator.restore(memento)
        except Exception:
            self.undo()

    def show_history(self) -> None:
        print("世話人: 現在の変更履歴:")
        for memento in self._mementos:
            print(f"└{memento.get_name()}")


if __name__ == "__main__":
    originator = Originator("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
    caretaker = Caretaker(originator)
    print()

    originator.do_something()   # 1回目の編集
    caretaker.backup()          # 状態をバックアップ   

    originator.do_something()   # 2回目の編集
    caretaker.backup()          # 状態をバックアップ

    originator.do_something()   # 3回目の編集
    caretaker.backup()          # 状態をバックアップ

    print("\n----------変更履歴------------")
    caretaker.show_history()

    print("\n----------復元リクエスト------------")
    caretaker.undo()

    print("\n----------再度復元リクエスト----------")
    caretaker.undo()
オリジネーター: 初期状態: ABCDEFGHIJKLMNOPQRSTUVWXYZ

オリジネーター: 作業中
オリジネーター: 作業状態を変更しました: cjXpNGUAVbWJykzfKBsaZQMIhSYFOv
世話人: 現在の状態をバックアップ...

オリジネーター: 作業中
オリジネーター: 作業状態を変更しました: cAwSBCuEfblIJMrNYvGeiOxVWnXqyj
世話人: 現在の状態をバックアップ...

オリジネーター: 作業中
オリジネーター: 作業状態を変更しました: OqsAfEJTXyWStGlzUdKMChRjZcnrYm
世話人: 現在の状態をバックアップ...


----------変更履歴------------
世話人: 現在の変更履歴:
└2022-01-01 15:06:53 / (cjXpNGUAVbWJykzfKBsaZQMIhSYFOv...)
└2022-01-01 15:06:53 / (cAwSBCuEfblIJMrNYvGeiOxVWnXqyj...)
└2022-01-01 15:06:53 / (OqsAfEJTXyWStGlzUdKMChRjZcnrYm...)

----------復元リクエスト------------
世話人: 状態を復元: 2022-01-01 15:06:53 / (OqsAfEJTXyWStGlzUdKMChRjZcnrYm...)
オリジネーター: 状態を変更しました: OqsAfEJTXyWStGlzUdKMChRjZcnrYm

----------再度復元リクエスト----------
世話人: 状態を復元: 2022-01-01 15:06:53 / (cAwSBCuEfblIJMrNYvGeiOxVWnXqyj...)
オリジネーター: 状態を変更しました: cAwSBCuEfblIJMrNYvGeiOxVWnXqyj

メリット

  • カプセル化に違反することなく、オブジェクトの状態のスナップショットを作成できる
  • 世話人にオリジネーターの状態の履歴を維持させることにより、オリジネーターのコードを単純化することができる

デメリット

  • クライアントがメモリを頻繁に作成する場合、アプリは大量のRAMを消費する可能性がある
  • 状態を保存するためにアプリケーションの全体的なパフォーマンスを低下させる可能性がある

Pocket
LINEで送る
Facebook にシェア
reddit にシェア
LinkedIn にシェア

トップへ