ソフトウェアの世界では、時期尚早なリファクタリングと偽の再利用可能性の追求が蔓延しています。開発者、特に開発を始めたばかりの開発者は、「再利用可能性」が究極の目標だと教えられることがよくあります。しかし、どんな犠牲を払ってでも再利用性を追求すると、多くの場合、過度に設計されたソリューションが生成され、汎用的すぎたり、厳格すぎたり、当面のプロジェクトの特定のニーズからかけ離れすぎたりします。実際、これは私たちがよく「抽象化地獄」と呼ぶ事態につながる可能性があります。これは、システムのすべての部分がどのように、そしてなぜ汎用インターフェイスに適合するように抽象化されたのかを完全に理解しない限り、実際には何も機能しないシナリオです。
私たちはパラダイム シフトを提案します。再利用性にこだわるのではなく、適応性、拡張性、オーバーライド性に焦点を当てましょう。
この文脈において、私たちはコードベースの将来のニーズを予測しようとすることから(占い師が未来を予測するように)、その代わりに、まだ成長の余地がある今日の強固で柔軟な基盤を作成することに焦点を当てています。未来の展開に合わせて進化します。
時期尚早なリファクタリングの問題は、作成したものはすべて再利用可能であるべきだという信念から来ていることです。これは崇高な目標のように思えるかもしれません。ただし、再利用性は多くの場合、不必要な複雑さと不必要な抽象化をもたらします。たとえば、すべてのモデルで機能するユニバーサル API アダプター を作成するという概念を考えてみましょう。理想的なのは、このアダプターがあらゆる API エンドポイント、あらゆるデータ形式、あらゆるネットワーク条件を処理できることです。しかし実際には、これは、今日の問題を効果的に解決するのではなく、不確実な将来に向けてフレームワークを構築していることを意味します。
例:
以前の BaseAdapter クラスと APIAdapter クラスを見てみましょう:
export class BaseAdapter { constructor(modelClass) { this.modelClass = modelClass; } async get(id) { throw new Error("Method 'get' must be implemented."); } async *all() { throw new Error("Method 'all' must be implemented."); } async query(params = {}) { throw new Error("Method 'query' must be implemented."); } async create(payload) { throw new Error("Method 'create' must be implemented."); } async update(payload) { throw new Error("Method 'update' must be implemented."); } async delete(id) { throw new Error("Method 'delete' must be implemented."); } }
上記のコードでは、BaseAdapter は考えられるすべてのメソッドを定義しており、それらを特定のサブクラス (APIAdapter、LocalStorageAdapter など) に実装する必要があります。これは、さまざまなアダプターのテンプレートです。理論的には良さそうですよね?ある日、新しいサービスに接続する必要がある場合、または新しいストレージ ソリューションと統合する必要がある場合は、別のサブクラスを作成するだけで済みます。
しかし、本当のことを考えてみましょう: それは本当に再利用可能でしょうか? それとも、単に複雑さが大きくなり、システムの保守、理解、拡張が困難になるだけでしょうか? 現実の世界で再利用できるものを本当に構築しているのでしょうか? それとも単に未来について推測しているだけですか?
時期尚早の再利用性を追求するのではなく、適応性と拡張性に焦点を当てることを提案します。それはどういう意味ですか?
これは、今日のあらゆるエッジケースに機能する完全に再利用可能なコードを作成することではありません。代わりに、時間をかけて構築、追加、変更できる強固な基盤を構築することに重点を置いています。重要なのは柔軟性であり、時期尚早な最適化ではありません。
Java (および他の多くの静的型付け言語) の昔は、インターフェース を作成し、コードを「将来も使える」ものにすることに重点が置かれていました。そのアイデアは、あらゆるシナリオを事前に予測し、それに基づいて設計することでした。
しかし、このアプローチはしばしばオーバーエンジニアリングにつながる可能性があります。つまり、決して起こらないかもしれないことを想定して設計したり、まだ表面化していない問題を中心に抽象的なフレームワークを構築したりすることです。あなたは、作業中のシステムの具体的なニーズを理解せずに、事実上、「普遍的」であるはずのコードを書いていることになります。
Java では、インターフェースを使用してコントラクトを定義しました。しかし、この考え方を「契約の定義」から単に現在に対する期待を設定するに変えたらどうなるでしょうか?将来何が起こるかを想定せずに、当面の状況に関して明確かつ信頼できる約束。
私たちの新しいアプローチでは、神秘的な占い師のようにアプリケーションの将来について約束することはありません。代わりに、私たちは今日の明確で信頼できる約束を設定し、必要が生じたときにこれらの約束を簡単に拡張および適応できるようにします。
次のように考えてください。私たちは 5 年後に世界がどうなるかを予測しているわけではありません。私たちは、今日作成するコードが世界の変化に合わせて進化し、適応できるようにしています。それは、建物の強固な基礎を築き、どんな変化が起こっても耐えられるように頑丈であることを確認するようなものです。
私たちが行う「約束」は、適応性と拡張性への取り組みです。目標は、未来を予測することではなく、将来の開発者 (または将来のあなた自身) が必要に応じて機能を簡単に追加、変更、拡張できるツールを作成することです。
BaseAdapter と APIAdapter を使用した例をもう一度見てみましょう。あらゆる状況に対処しようとする超汎用メソッドを作成する代わりに、コードを適応性と簡単に拡張可能にすることに重点を置きます。
APIAdapter の簡単な再アーキテクチャを次に示します。
export class BaseAdapter { constructor(modelClass) { this.modelClass = modelClass; } async get(id) { throw new Error("Method 'get' must be implemented."); } async *all() { throw new Error("Method 'all' must be implemented."); } async query(params = {}) { throw new Error("Method 'query' must be implemented."); } async create(payload) { throw new Error("Method 'create' must be implemented."); } async update(payload) { throw new Error("Method 'update' must be implemented."); } async delete(id) { throw new Error("Method 'delete' must be implemented."); } }
今回は、新しいタイプのアダプターごとにまったく新しい BaseAdapter を作成する代わりに、将来のニーズに合わせて簡単に拡張および適応できる基盤を作成しました。
新しい API エンドポイント用に拡張する例:
export class APIAdapter extends BaseAdapter { static baseURL; static headers; static endpoint; async *all(params = {}) { // Custom logic, but easily extensible if needed const url = `${this.baseURL}/${this.endpoint}`; const response = await API.get(url, { params, headers: this.headers }); return response.data; } async query(params = {}) { // Simplified for illustration const url = `${this.baseURL}/${this.endpoint}/search`; const response = await API.get(url, { params }); return response.data; } // Easily extendable for specific cases async customRequest(method, endpoint, params = {}) { const url = `${this.baseURL}/${endpoint}`; const response = await API[method](url, { params }); return response.data; } }
このシナリオでは、1 つの API エンドポイントに特定の動作 (注文のカスタム エラー処理など) を追加する必要がある場合、APIAdapter を オーバーライド または 拡張 して、API エンドポイントに適合させることができます。システム全体をリファクタリングすることなく必要となります。
この新しいパラダイムでは、将来のニーズや問題をすべて予測しようとしているわけではありません。代わりに、要件の変化や新たな課題の発生に適応する強力で柔軟な基盤を構築することに重点を置いています。私たちは、仮説的な問題に基づいて解決策を時期尚早に抽象化したりしたり、過剰に設計したりしません。代わりに、新しいニーズが発生したときに進化し、簡単に適応できるツールを作成します。
鍵となるのは、占い師のように将来性を保証することではなく、たとえ世界が変わっても時の試練に確実に耐えられる基盤を構築することです。これは、将来の自分に対する約束です。コードは堅牢で、適応性があり、新しい要件が適用された場合に拡張できるようになっています。
以上がパラダイムの変化: 時期尚早なリファクタリングと偽りの「再利用性」から適応性、拡張性、信頼性への詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。