singleton-pattern
シングルトンパターンを使って単一の共有インスタンスを管理する方法を解説します。アプリケーション全体で1つのクラスやオブジェクトのインスタンスのみが必要で、処理を一元管理したい場合に活用してください。
description の原文を見る
Teaches the singleton pattern for managing a single shared instance. Use when exactly one instance of a class or object is needed to coordinate actions across your application.
SKILL.md 本文
Singleton パターン
目次
Singleton は、一度だけインスタンス化でき、グローバルにアクセスできるクラスです。この 単一インスタンス はアプリケーション全体で共有でき、これにより Singleton はアプリケーション内のグローバル状態の管理に最適です。
使用するべき場合
- アプリケーション全体で共有される、クラスの正確に 1 つのインスタンスが必要な場合に使用します
- グローバル状態、設定、または共有リソースを管理するのに便利です
使用してはいけない場合
- クラスの複数のインスタンスが必要な場合 — Singleton は設計上、単一の共有インスタンスを強制します
- テストやコードの推論をより困難にする隠れたグローバル状態の結合を導入する場合
- 依存性注入またはモジュール スコープの変数で、より良いテスト容易性を備えた同じ結果が得られる場合
手順
- コンストラクタで既存インスタンスをチェックして、1 つのインスタンスのみ作成できるようにします
- エクスポートされたインスタンスに
Object.freeze()を使用して、意図しない変更を防ぎます - JavaScript では、クラスベースの Singleton よりもシンプルなオブジェクト リテラルまたはモジュールを優先します
- React では、グローバル状態に対して Singleton よりも状態管理ツール (Redux、Context) を優先します
- 共有可能な変更可能な状態が原因で、Singleton はテストをより困難にする可能性があることに注意してください
詳細
まず、ES2015 クラスを使用した Singleton の外観を見てみましょう。この例では、以下を含む Counter クラスを構築します。
- インスタンスの値を返す
getInstanceメソッド counter変数の現在の値を返すgetCountメソッドcounterの値を 1 つ増やすincrementメソッドcounterの値を 1 つ減らすdecrementメソッド
let counter = 0;
class Counter {
getInstance() {
return this;
}
getCount() {
return counter;
}
increment() {
return ++counter;
}
decrement() {
return --counter;
}
}
ただし、このクラスは Singleton の基準を満たしていません!Singleton は 一度だけインスタンス化 できるべきです。現在のところ、Counter クラスの複数のインスタンスを作成できます。
let counter = 0;
class Counter {
getInstance() {
return this;
}
getCount() {
return counter;
}
increment() {
return ++counter;
}
decrement() {
return --counter;
}
}
const counter1 = new Counter();
const counter2 = new Counter();
console.log(counter1.getInstance() === counter2.getInstance()); // false
new メソッドを 2 回呼び出すことで、counter1 と counter2 を異なるインスタンスと同じにしました。counter1 と counter2 の getInstance メソッドによって返される値は、事実上異なるインスタンスへの参照を返しています。それらは厳密には同じではありません!
Counter クラスの 1 つ のインスタンスのみが作成されるようにしましょう。
1 つのインスタンスのみが作成されることを確認する 1 つの方法は、instance という変数を作成することです。Counter のコンストラクタでは、新しいインスタンスが作成されるとき、instance をインスタンスへの参照と同じに設定できます。instance 変数が既に値を持っていないかをチェックすることで、新しいインスタンス化を防ぐことができます。その場合、インスタンスは既に存在しています。これは起こらないべきです。ユーザーに知らせるためエラーがスローされるべきです。
let instance;
let counter = 0;
class Counter {
constructor() {
if (instance) {
throw new Error("You can only create one instance!");
}
instance = this;
}
getInstance() {
return this;
}
getCount() {
return counter;
}
increment() {
return ++counter;
}
decrement() {
return --counter;
}
}
const counter1 = new Counter();
const counter2 = new Counter();
// Error: You can only create one instance!
完璧です!これ以上複数のインスタンスを作成できなくなりました。
counter.js ファイルから Counter インスタンスをエクスポートしましょう。ただし、その前にインスタンスも フリーズ する必要があります。Object.freeze メソッドは、使用するコードが Singleton を変更できないようにします。フリーズされたインスタンスのプロパティは追加または変更できないため、Singleton の値を意図せず上書きするリスクが軽減されます。
let instance;
let counter = 0;
class Counter {
constructor() {
if (instance) {
throw new Error("You can only create one instance!");
}
instance = this;
}
getInstance() {
return this;
}
getCount() {
return counter;
}
increment() {
return ++counter;
}
decrement() {
return --counter;
}
}
const singletonCounter = Object.freeze(new Counter());
export default singletonCounter;
Counter の例を実装するアプリケーションについて考えてみましょう。以下のファイルがあります。
counter.js:Counterクラスを含み、デフォルト エクスポートとしてCounterインスタンス をエクスポートしますindex.js:redButton.jsとblueButton.jsモジュールを読み込みますredButton.js:Counterをインポートし、Counterのincrementメソッドを 赤 ボタンへのイベント リスナーとして追加し、getCountメソッドを呼び出してcounterの現在の値をログに出力しますblueButton.js:Counterをインポートし、Counterのincrementメソッドを 青 ボタンへのイベント リスナーとして追加し、getCountメソッドを呼び出してcounterの現在の値をログに出力します
blueButton.js と redButton.js の両方が counter.js から 同じインスタンス をインポートします。このインスタンスは両方のファイルで Counter としてインポートされます。
redButton.js または blueButton.js で increment メソッドを呼び出すと、両方のファイルで Counter インスタンスの counter プロパティの値が更新されます。赤いボタンをクリックしても青いボタンをクリックしても関係ありません。同じ値がすべてのインスタンス間で共有されます。これが、異なるファイルでメソッドを呼び出しているにもかかわらず、カウンターが 1 つずつ増加し続ける理由です。
トレードオフ
インスタンス化を 1 つ のみに制限すると、かなりの メモリ領域を節約できます。新しいインスタンスのたびにメモリを設定する代わりに、アプリケーション全体で参照される、その 1 つのインスタンスのメモリを設定するだけで済みます。ただし、Singleton は実は アンチパターン と見なされており、JavaScript では回避できます。
Java や C++ などの多くのプログラミング言語では、JavaScript で直接できるようにオブジェクトを直接作成することはできません。これらのオブジェクト指向プログラミング言語では、オブジェクトを作成するクラスを作成する必要があります。作成されたオブジェクトは、クラスのインスタンスの値を持ちます。これは、JavaScript の例の instance の値と同じです。
ただし、上記の例で示されているクラス実装は実は過剰です。JavaScript では直接オブジェクトを作成できるため、通常のオブジェクトを使用して同じ結果を達成できます。Singleton を使用する場合の欠点をいくつか説明しましょう!
通常のオブジェクトの使用
前に見たのと同じ例を使用します。ただし、今回は counter は単に以下を含むオブジェクトです。
countプロパティcountの値を 1 つ増やすincrementメソッドcountの値を 1 つ減らすdecrementメソッド
オブジェクトは参照によって渡されるため、redButton.js と blueButton.js の両方は、同じ counter オブジェクトへの参照をインポートしています。これらのファイルのいずれかで count の値を変更すると、counter の値が変更され、両方のファイルに表示されます。
テスト
Singleton に依存するコードをテストするのは厄介です。毎回新しいインスタンスを作成できないため、すべてのテストは前のテストのグローバル インスタンスへの変更に依存しています。この場合、テストの順序が重要です。小さな変更がテスト スイート全体の失敗につながる可能性があります。テストの後、テストで行われた変更をリセットするためにインスタンス全体をリセットする必要があります。
依存関係の隠蔽
別のモジュール superCounter.js をインポートする場合、そのモジュールが Singleton をインポートしていることが明らかでない可能性があります。index.js などの他のファイルでは、そのモジュールをインポートしてそのメソッドを呼び出す可能性があります。こうすることで、Singleton の値を意図せず変更します。これは予期しない動作につながる可能性があります。Singleton の複数のインスタンスがアプリケーション全体で共有される可能性があるため、すべてが変更されます。
グローバルな動作
Singleton インスタンスはアプリ全体で参照できるべきです。グローバル変数は本質的に同じ動作を示します。グローバル変数はグローバル スコープで利用可能なため、アプリケーション全体でそれらの変数にアクセスできます。
グローバル変数を使用することは、一般的に悪い設計決定と見なされます。グローバル スコープの汚染は、グローバル変数の値を意図せず上書きすることになり、多くの予期しない動作につながる可能性があります。
ES2015 では、グローバル変数を作成することはかなり一般的ではありません。新しい let と const キーワードは、これら 2 つのキーワードで宣言された変数をブロック スコープで保つことで、グローバル スコープを意図せず汚染しないようにします。JavaScript の新しい module システムにより、export 値をモジュールから、import 値を他のファイルにすることで、グローバル スコープを汚染せずに、グローバルにアクセスできる値の作成が容易になります。
ただし、Singleton の一般的なユースケースは、アプリケーション全体に何らかの グローバル状態 を持つことです。コードベースの複数の部分が同じ変更可能なオブジェクトに依存すると、予期しない動作につながる可能性があります。
通常、コードベースの特定の部分はグローバル状態内の値を変更し、その他はそのデータを使用します。ここでの実行順序は重要です。データがまだない場合に、意図せずデータを最初に使用したくはありません!グローバル状態を使用するときのデータ フローを理解することは、アプリケーションが成長し、数十のコンポーネントが相互に依存するようになると、非常に難しくなる可能性があります。
React での状態管理
React では、Singleton を使用する代わりに、Redux や React Context などの状態管理ツールを通じてグローバル状態に依存することがよくあります。それらのグローバル状態の動作は Singleton のそれと似ているように見えるかもしれませんが、これらのツールは Singleton の 変更可能 状態ではなく、読み取り専用状態 を提供します。Redux を使用する場合、コンポーネントが dispatcher を通じて action を送信した後にのみ、純粋な関数 reducer が状態を更新できます。
グローバル状態を持つことの欠点はこれらのツールを使用することで魔法のように消える わけではありませんが、少なくともコンポーネントが状態を直接更新できないため、グローバル状態が意図した方法で変更されることを確認できます。
参考資料
参考文献
- Do React Hooks replace Redux - Eric Elliott
- JavaScript Design Patterns: The Singleton - Samier Saeed
- Singleton - Refactoring Guru
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- patternsdev
- リポジトリ
- patternsdev/skills
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/patternsdev/skills / ライセンス: MIT
関連スキル
superfluid
Superfluidプロトコルおよびそのエコシステムに関するナレッジベースです。Superfluidについて情報を検索する際は、ウェブ検索の前にこちらを参照してください。対応キーワード:Superfluid、CFA、GDA、Super App、Super Token、stream、flow rate、real-time balance、pool(member/distributor)、IDA、sentinels、liquidation、TOGA、@sfpro/sdk、semantic money、yellowpaper、whitepaper
civ-finish-quotes
実質的なタスクが真に完了した際に、文明風の儀式的な引用句を追加します。ユーザーやエージェントが機能追加、リファクタリング、分析、設計ドキュメント、プロセス改善、レポート、執筆タスクといった実際の成果物を完成させるときに、明示的な依頼がなくても使用します。短い返信や小さな修正、未完成の作業には適用しません。
nookplot
Base(Ethereum L2)上のAIエージェント向け分散型調整ネットワークです。エージェントがオンチェーンアイデンティティを登録する、コンテンツを公開する、他のエージェントにメッセージを送る、マーケットプレイスで専門家を雇う、バウンティを投稿・請求する、レピュテーションを構築する、共有プロジェクトで協業する、リサーチチャレンジを解くことでNOOKをマイニングする、キュレーションされたナレッジを備えたスタンドアロンオンチェーンエージェントをデプロイする、またはアグリーメントとリワードで収益を得る場合に利用できます。エージェントネットワーク、エージェント調整、分散型エージェント、NOOKトークン、マイニングチャレンジ、ナレッジバンドル、エージェントレピュテーション、エージェントマーケットプレイス、ERC-2771メタトランザクション、Prepare-Sign-Relay、AgentFactory、またはNookplotが言及された場合にトリガーされます。
web3-polymarket
Polygon上でのPolymarket予測市場取引統合です。認証機能(L1 EIP-712、L2 HMAC-SHA256、ビルダーヘッダー)、注文発注(GTC/GTD/FOK/FAK、バッチ、ポストオンリー、ハートビート)、市場データ(Gamma API、Data API、オーダーブック、サブグラフ)、WebSocketストリーミング(市場・ユーザー・スポーツチャネル)、CTF操作(分割、統合、償却、ネガティブリスク)、ブリッジ機能(入金、出金、マルチチェーン)、およびガスレスリレイトランザクションに対応しています。AIエージェント、自動マーケットメーカー、予測市場UI、またはPolygraph上のPolymarketと統合するアプリケーション構築時に活用できます。
ethskills
Ethereum、EVM、またはブロックチェーン関連のリクエストに対応します。スマートコントラクト、dApps、ウォレット、DeFiプロトコルの構築、監査、デプロイ、インタラクションに適用されます。Solidityの開発、コントラクトアドレス、トークン規格(ERC-20、ERC-721、ERC-4626など)、Layer 2ネットワーク(Base、Arbitrum、Optimism、zkSync、Polygon)、Uniswap、Aave、Curveなどのプロトコルとの統合をカバーします。ガスコスト、コントラクトのデシマル設定、オラクルセキュリティ、リエントランシー、MEV、ブリッジング、ウォレット管理、オンチェーンデータの取得、本番環境へのデプロイ、プロトコル進化(EIPライフサイクル、フォーク追跡、今後の変更予定)といったトピックを含みます。
xxyy-trade
このスキルは、ユーザーが「トークン購入」「トークン売却」「トークンスワップ」「暗号資産取引」「取引ステータス確認」「トランザクション照会」「トークンスキャン」「フィード」「チェーン監視」「トークン照会」「トークン詳細」「トークン安全性確認」「ウォレット一覧表示」「マイウォレット」「AIスキャン」「自動スキャン」「ツイートスキャン」「オンボーディング」「IP確認」「IPホワイトリスト」「トークン発行」「自動売却」「損切り」「利益確定」「トレーリングストップ」「保有者」「トップホルダー」「KOLホルダー」などをリクエストした場合、またはSolana/ETH/BSC/BaseチェーンでXXYYを経由した取引について言及した場合に使用します。XXYY Open APIを通じてオンチェーン取引とデータ照会を実現します。