Factory Method: オブジェクトの生成を簡単にする方法
ポルトガル語版: https://dev.to/ianito/design-patterns-factory-method-pt-br-21a3
こんにちは、
私はイアン・メスキータです。ソフトウェアエンジニアとして活動しています。今日からソフトウェアエンジニアリングやソフトウェアアーキテクチャについて話す記事シリーズを始めます。
でも、なぜデザインパターンが重要なのでしょうか?それは、私たちソフトウェアエンジニアが取り扱う一般的な問題に対する解決策を提供してくれるからです。デザインパターンを使うと、ソフトウェアを理解しやすく(常にそうとは限りませんが)、拡張しやすく、メンテナンスしやすくなります。
この記事から読み始めることをお勧めします。なぜなら、今後の記事にとって基礎となる概念が紹介されているからです。冗長性を避けるために、これらの基本に馴染んでおくことを勧めます。
注記: 特定のデザインパターンの説明をする際、ベストプラクティスに常に従うとは限りません(私の目的はその概念に焦点を当てることにあります)。あなたの提案がパターンに不慣れな人々にとって物事を簡素化するかどうかを検討してください。すべてのフィードバックを歓迎します。
それと、オブジェクト指向プログラミング(OOP)とSOLID原則について理解しておくと良いです。
目次
デザインパターンについて議論する前に、いくつかの概念を見てみましょう:
可読性と書きやすさ
可読性
他の開発者がコードを読んで理解することのしやすさを意味します。
書きやすさ
コードを書きやすいことを意味します。これは、タイピングの速度に関するものではなく、ソフトウェアエンジニアとして私たちのアイデアを表現するしやすさについてです。C言語で作業したことがあれば、文字列操作を思い出して、他の言語と比較することで、この概念を理解できるでしょう。
これら2つの概念は、ソフトウェアエンジニアリング分野(だけではなく)で非常に重要です。一般的に、新しいものを作るのではなく、既存のものを強化することが多いです。そのため、可読性と書きやすさは長期的に私たちのソフトウェアに影響を与える可能性があります。
もしあなたが、簡単に読み書きできるソフトウェアと、それぞれの開発者が自分のパターンに従うソフトウェアのどちらかを選ぶことができるとしたら、どちらを選びますか?
自分を試したいのでなければ、答えはわかっていますよね。xD
ただし、これは、すべての一般的なパターンが最適な書きやすさや可読性を提供するとは限らないことを意味しません。時には、拡張性を高めるなどの理由から、どちらか一方を優先する必要があります。また、特定のプロジェクト/チームの制約によるものもあります。
それにもかかわらず、これらの概念を理解しておくことは、いつそしてどのように適用するかを決定する際に不可欠です。
依存性の注入
依存性の注入(DI)は、コンポーネントまたはオブジェクトが依存性を取得する方法に関するソフトウェアのデザインパターンです。オブジェクトが独自の依存性を生成するのではなく(依存性をインスタンス化するような)、またはグローバルインスタンスを使用する代わりに、依存性は通常、コンストラクタ、メソッド、またはプロパティを通じてオブジェクトに「注入」されます。これにより、ソフトウェア設計の柔軟性、テスト可能性、および保守性が向上します。
将来、このパターンについて話すことができます。
生成パターン、構造パターン、振る舞いパターン
生成パターン は、オブジェクトを最適な方法でインスタンス化することに焦点を当てています。これらのパターンは、コンストラクタを使用する方法以外に、柔軟性と保守可能性を確保するさまざまなテクニックを提供します。
構造パターン は、オブジェクトやクラスをどのように組み合わせるかについて指導し、スケーリング性と保守性を狙っています。
振る舞いパターン は、オブジェクトがどのように相互作用し、通信するかに対処し、オブジェクトの責任と協力に重点を置いています。
今日は ファクトリーメソッド - (生成パターン) について話し合います。
問題
あなたがInstagramアカウントを管理するアプリケーションを作成しているとしましょう。スタートアップとして、あなたはアイデアを実証し、アプリの最初で唯一の機能はInstagramに投稿する機能です。
今、お客様はあなたのアプリケーションを気に入り、Facebook、BlueSky、その他のソーシャルネットワークにも対応してほしいと感じています。どのソーシャルAPIを使用するかはまだわかりませんが、APIであることはわかっています。
しかし、各ソーシャルネットワークには独自の投稿方法、異なる依存性と検証があることを知っていますが、動作は同じであることもわかっています:ソーシャルネットワークに投稿します。
あなたは同意しますよね?ソーシャルネットワークに投稿する必要があるアプリケーション内のすべての場所が、実装について知る必要はなく、期待通りに動作して投稿だけすればいい、と。
この場合、依存性をコンストラクタを通じて渡すという方法でアプリケーションを設計するとしましょう。依存性をコンストラクタに注入する必要があるのです。以下のように:
new TwitterAPI(messageValidator, twitterSDK);
//または
new FacebookAPI(messageValidator, userIdHash, facebookSDK);
//または
new InstagramAPI(instagramSDK);
詳細についてあまり考えず、主な考えを掴んでください。つまり、クラスが必要とする依存性を渡しているのです。
FacebookAPI/TwitterAPIを多くの場所で使う必要があると想像してみてください。そして、そのクラスのメソッドを使いたいたびに、それをインスタンス化して、コンストラクタを通じてその依存性を渡さなければなりません。
これだけでなく、Facebookがプラットフォーム内で投稿する前にロボットでないことを確認するように決定した場合はどうでしょう?その結果、送信する前にこれを検証する必要があります。そして今、あることが変わったために、これを変える場所がたくさんあるからです。
そんな問題への一つの解決策は、工場を使うことです。
ファクトリーメソッド
ファクトリーメソッド は、オブジェクトの作成に関するインターフェースを提供する生成デザインパターンですが、サブクラスが作成されるオブジェクトのタイプを変更できるようにします。そして、ファクトリーメソッドによって返されるオブジェクトはしばしば 製品 と呼ばれます。
ファクトリーメソッドの主な考え方は、作成コードの実装をそのサブクラスまたはそれを実装するクラスに委任することです。
ただし、制限があります。サブクラスは、これらの製品が共通の基本クラスまたはインターフェース(APIインテグレーション)を持っている場合にのみ、異なるタイプの製品を返すことができます。また、ベースクラスのファクトリーメソッドの戻り値(APIインテグレーション)は、このインターフェースとして宣言されるべきです。
以下の例を使用して、ファクトリーメソッドについていくつかの概念を理解しましょう。
- 製品:これは、ファクトリーメソッドが作成するオブジェクトのタイプを定義するインターフェースまたは抽象クラスです。
私たちの例では: インターフェース APIIntegration
クリエーター: このクラスはファクトリーメソッド自体を宣言します。このメソッドは、製品タイプのオブジェクトを返します。一部の実装では、クリエーターはインターフェース、抽象クラス、または具体的なクラスかもしれません。
私たちの例では: APIFactory インターフェースと API実装型の製品を返す createApi メソッド。
具体的なクリエーター: クリエーターを実装または拡張し、具体的なConcreteProductのインスタンスを返すように工場方法をオーバーライドするクラス。
私たちの例では:TwitterAPIFactory, FacebookAPIFactory, InstagramAPIFactory
具体的な製品: これは、製品の特定の実装です。これは、具体的なクリエーターがインスタンス化して返すものです。
私たちの例では: TwitterAPI, FacebookAPI, InstagramAPI
では、コードを見てみましょう。
私がどのようにして APIS に接続したかの詳細や構文にはあまり注意を払わないでください。すべての例は偽物であり、主要な文書に基づいているわけではありません。アイデアだけを吸収してください。
具体的な製品のクラスの依存関係は、コンストラクタを通じて渡されます。(DI)
製品
interface APIIntegration {
post(message: string): void;
}
クリエーター
export interface APIFactory {
createAPI(): APIIntegration;
}
具体的な製品
class TwitterAPI implements APIIntegration {
constructor(private readonly messageValidator: MessageValidator, private readonly twitterSdk: TwitterSDK) {}
post(message: string) {
if (!this.messageValidator.validate(message))
throw new Error("invalid validation");
if (!this.twitterSdk.isConnected) throw new Error("connection refused");
this.twitterSdk.post(message);
console.log(`Twitterに投稿する: ${message}`);
}
}
class FacebookAPI implements APIIntegration {
constructor(
private readonly messageValidator: MessageValidator,
private readonly userIdHash: string,
private readonly facebookSDK: FacebookSDK
) {}
post(message: string) {
if (!this.messageValidator.validate(message))
throw new Error("invalid validation");
if (!this.facebookSDK.isConnected) throw new Error("connection refused");
if (!this.facebookSDK.findUser(userIdHash))
throw new Error("user not found");
this.facebookSDK.post(message);
console.log(`Facebookに投稿する: ${message}`);
}
}
class InstagramAPI implements APIIntegration {
constructor(private readonly instagramSDK: InstagramSDK) {}
post(message: string) {
this.instagramSDK.post(message);
console.log(`Instagramに投稿する: ${message}`);
}
}
具体的なクリエーター
class TwitterAPIFactory implements APIFactory {
createAPI(): APIIntegration {
const messageValidator = new MessageValidator()
const twitterSDK = new TwitterSDK()
return new TwitterAPI(messageValidator,twitterSDK);
}
}
class FacebookAPIFactory implements APIFactory {
createAPI(): APIIntegration {
const messageValidator = new MessageValidator()
const facebookSDK = new FacebookSDK()
const userIdHash = "123"
return new FacebookAPI(messageValidator,userIdHash,facebookSDK);
}
}
class InstagramAPIFactory implements APIFactory {
createAPI(): APIIntegration {
const instagramSDK = new InstagramSDK()
return new InstagramAPI(instagramSDK);
}
}
クライアントコード
const allSocialNetworks: APIFactory[] = [new InstagramAPIFactory(), new FacebookAPIFactory(),<br><br>こちらの記事はdev.toの良い記事を日本人向けに翻訳しています。<br>[https://dev.to/ianito/design-patterns-factory-method-22if](https://dev.to/ianito/design-patterns-factory-method-22if)