タグ付け: API設計
公開インターフェースと公開されたインターフェース
多くの現代的な言語では、モジュール内の公開機能と非公開機能を区別しています。しかし、あまり区別されないのは、公開機能と公開された機能の違いです。そして、それはより重要な違いかもしれません。
モジュール依存関係のリファクタリング
プログラムのサイズが大きくなるにつれて、小さな変更を加えるためにすべてを理解する必要がないように、プログラムをモジュールに分割することが重要になります。これらのモジュールは、多くの場合、異なるチームによって提供され、動的に結合されます。このリファクタリングのエッセイでは、プレゼンテーション-ドメイン-データのレイヤーを使用した小さなプログラムを分割します。次に、これらのモジュール間の依存関係をリファクタリングして、サービスロケーターと依存性注入のパターンを導入します。これらは異なる言語で適用されますが、見た目も異なります。そのため、JavaとクラスレスなJavaScriptスタイルの両方でこれらのリファクタリングを示します。
コレクションパイプライン
コレクションパイプラインは、ある操作の出力をコレクションとして受け取り、次の操作に供給することで構成される一連の操作として計算を整理するプログラミングパターンです。(一般的な操作は、フィルタ、マップ、リデュースです。)このパターンは関数型プログラミングで一般的であり、ラムダを持つオブジェクト指向言語でも一般的です。この記事では、このパターンを説明し、パイプラインを形成する方法のいくつかの例を示すことで、このパターンに慣れていない人にパターンを紹介し、コア概念を理解できるようにすることで、異なる言語間のアイデアの容易な移行を支援します。
マイクロサービスと分散オブジェクトの第一法則
P of EAAでは「オブジェクトを分散しないでください」と言いました。このアドバイスは、私がマイクロサービスに関心を持っていることと矛盾していますか?
APIは著作権の対象となるべきではない
プログラマーがテストや相互運用性をサポートするためのインターフェースを再実装し、競争を促進するために、APIは著作権の対象となるべきではありません。
バイテンポラル履歴
あるプロパティの履歴値にアクセスする必要があることはよくあります。しかし、時には、この履歴自体を遡及的な更新に対応して変更する必要があります。バイテンポラル履歴は、時間を2次元として扱います。実際の履歴は、情報の完全な伝達を考えると履歴がどうなるべきかを記録する一方、記録履歴は、履歴に関する私たちの知識がどのように変化するかを捉えます。
CQRS
CQRSは、Command Query Responsibility Segregationの略です。これは、グレッグ・ヤングによって最初に説明されたパターンです。その中心となるのは、情報の更新に使用されるモデルと、情報の読み取りに使用されるモデルを異なるものにすることができるという考え方です。状況によっては、この分離は価値のあるものですが、ほとんどのシステムではCQRSが危険な複雑さを加えることに注意してください。
コマンド指向インターフェース
モジュールへのインターフェースの最も一般的なスタイルは、プロシージャまたはオブジェクトメソッドを使用することです。そのため、モジュールに契約の料金を計算させたい場合、次のように呼び出す計算を行うメソッドを使用して、BillingServiceクラスを使用するかもしれません。
aBillingService.calculateCharges(aContract)
コマンド指向インターフェースには、操作ごとにコマンドクラスがあり、次のような呼び出しが行われます。
CalculateChargeCommand.new(aContract).run()
コマンドクエリ分離
「コマンドクエリ分離」という用語は、Bertrand Meyerの著書「オブジェクト指向ソフトウェア構築」で造語されました。これは、オブジェクト指向の初期において最も影響力のあるオブジェクト指向の書籍の1つです。(影響力があったのは初版です。第2版も良いですが、持ち上げるには数ヶ月ジムに通う必要があります。)
コンストラクタによる初期化
コンストラクタによる初期化とは、オブジェクトが必要とするすべての協調者をオブジェクトの作成メソッドで渡すアプローチです。SetterInitializationの代替手段です。
便宜的な実装
クラスを作成する場合、ほとんどの場合、そのクラスの機能がそのクラスにとって意味のあるものであることを保証しようとします。しかし、クラスに機能を追加して、自然にはそうではないはずのより豊富なインターフェースに準拠させることが理にかなう場合があります。
装飾されたコマンド
これは非常に一般的なパターンであり、非常にシンプルでもあります。これは、コマンドに適用されるデコレータパターンにすぎません。CommandOrientedInterfaceでよく使用されているのを見ました。これはインターセプター、アスペクト指向プログラミングの一種とも呼ばれます。
設計された継承
オブジェクト指向のコミュニティで最も長く続いている議論の1つは、OpenInheritanceと設計された継承の議論です。設計された継承の原則は、おそらくJosh Blochの言葉「継承のために設計して文書化するか、そうでなければ禁止する」によって最もよく要約されています。このアプローチでは、継承できるメソッドを決定し、他のメソッドをSealしてオーバーライドされないようにします。
ダックインターフェース
おそらく私はナイーブでしたが、HumaneInterfaceに関する私の投稿がどれだけの議論を巻き起こすとは予想していませんでした。悲しいことに、そのほとんどは、私が伝えようとしていた根本的な点ではなく、RubyのArrayとJavaのListの相対的なメリットに関する議論に終わりましたが、それにもかかわらず、いくつかの素晴らしい会話の流れが現れたと思います。
これらの会話の流れの1つは、ArrayとListの違いが、人間的/最小限の哲学以外の理由にもあることを明らかにしました。これらの理由の1つは、類似した機能が2つの言語で異なる役割を果たす方法に関するものです。
フラグ引数
フラグ引数とは、その値に応じて異なる操作を実行するように関数に指示する関数引数の一種です。コンサートの予約をしたいとしましょう。これには、通常とプレミアムの2つの方法があります。ここでフラグ引数を使用すると、次のようなメソッド宣言になります。
Fluentインターフェース
数ヶ月前、Eric Evansとのワークショップに参加し、Fluentインターフェースと名付けることにした特定のスタイルのインターフェースについて話しました。これは一般的なスタイルではありませんが、より広く知られるべきだと考えています。おそらく、それを説明する最良の方法は例です。
基盤プラットフォーム
基盤プラットフォームとは、その上に構築されるアプリケーションよりも前に構築されるプラットフォームです。その考え方は、プラットフォームを必要とするさまざまなアプリケーションのニーズを分析してから、プラットフォームを構築することです。プラットフォームが完成したら、その上にアプリケーションを構築します。ポイントは、アプリケーションの作業を開始する前に、プラットフォームには本当に安定したAPIが必要であり、そうでなければ、アプリケーションへの波及効果のためにプラットフォームの変更を管理することが困難になるということです。
ゲッター除去者
ゲッターメソッドを見ると、口の左側がぴくぴくする様子でわかります。戦斧を素早く引っ張り、もう1つのゲッターが容赦なくクラスから切り離されると満足のいく叫びが上がり、クラスはゲッター除去者の足元で感謝の恍惚状態にすぐに気絶します。
収穫されたプラットフォーム
収穫によってプラットフォームを構築するには、プラットフォームの構築を試みるのではなく、アプリケーションの構築から始めます。アプリケーションを構築する際に、汎用的なコードを開発しようとしませんが、よくファクタリングされ、よく設計されたアプリケーションを構築するために努力します。
ヘッダーインターフェース
ヘッダーインターフェースとは、クラスの暗黙的な公開インターフェースを模倣する明示的なインターフェースです。基本的に、クラスのすべての公開メソッドを取得してインターフェースに宣言します。次に、クラスの代替実装を提供できます。これはRoleInterfaceの反対です。詳細と長所と短所については、そこで詳しく説明します。
人間的なインターフェース
しばらくRubyのコミュニティに関わってきて、「ヒューマンインターフェース」という用語を何度も目にしました。これは、Rubyistがクラスインターフェースを作成する際の姿勢の一部を表しており、API設計における二つの考え方(もう一つはMinimalInterface)の興味深い対比を示していると思います。
暗黙的なインターフェース実装
JavaとC#はどちらも純粋なインターフェース型の同じモデルを共有しています。`interface Mailable`と記述することで純粋なインターフェースを宣言し、`class Customer implements Mailable`(Javaの場合)と記述することで、その実装を宣言できます。クラスは任意の数の純粋なインターフェースを実装できます。このモデルでは、クラスが存在する時は常に暗黙的なインターフェースが存在することを無視している点が一つあります。
インターフェース実装ペア
すべてのクラスを取り、インターフェースとペアにするという慣習です。その結果、ICustomerとCustomer、あるいはCustomerとCustomerImplなど、ペアになったものが見られます。多くの点で、これはC/C++における各クラスのヘッダーファイルの習慣を反映していますが、この場合、インターフェースと実装は実際には別々の型です。
制御の反転
制御の反転は、フレームワークを拡張する際に遭遇する一般的な現象です。実際、それはしばしばフレームワークの特徴として考えられています。
最小限のインターフェース
最小限のインターフェースとは、ここでHumaneInterfaceと対比されるAPI設計のスタイルです。最小限のインターフェースの考え方は、クライアントが必要なすべての操作を実行できるようにするAPIを設計することですが、その機能を仕事を行うために最小限の合理的なメソッドのセットに絞り込むことです。(違いの良い例についてはHumaneInterfaceを参照してください。)
オープン継承
これはDesignedInheritanceとは正反対の考え方です。オープン継承を支持する人は、クラスをSealすることで継承を許可しない、あるいは人々がクラスを継承することを妨げるようなことはしません。
オーバーロードされたゲッターセッター
最近JavaScriptを調べていて、ゲッターとセッターに同じ関数名を使用する習慣に気づきました。例えば、jQueryでバナーの高さを知りたい場合は`$("#banner").height()`を使用し、高さを変更したい場合は`$("#banner").height(100)`を使用します。
この規約はSmalltalkで使用されていたため、馴染みがあります。Smalltalkでは`banner height`で値を取得し、`banner height: 100`で変更できます。Smalltalkの規約であると知っていれば、その言語に対して遠くまで及ぶ変わらない愛情を持っているため、好きになることを期待できます。しかし、どんな良いものでも欠点があり、このコーディングスタイルに対する嫌悪感を隠すことはできません。
並列変更
すべてのコンシューマーに影響を与えるインターフェースへの変更を行うには、変更自体の実装と、そのすべての使用方法の更新という2つの思考モードが必要です。特に、複数のクライアントや外部クライアントを持つPublishedInterfaceで変更を行う場合、両方を同時に実行しようとすると困難になる可能性があります。
並列変更は、拡張と縮小とも呼ばれ、拡張、移行、縮小という3つの明確なフェーズに変化を分割することで、インターフェースへの後方互換性のない変更を安全な方法で実装するためのパターンです。
公開インターフェース
公開インターフェースとは、私が(最初にRefactoringで)使用した用語で、定義されているコードベースの外で使用されるクラスインターフェースを指します。そのため、Javaのpublicよりも、そしてC#の内部ではないpublicよりも意味が広いと言えます。IEEE Softwareの私のコラムでは、公開とpublicの違いは、publicとprivateの違いよりも実際には重要であると主張しました。
必須インターフェース
必須インターフェースとは、インタラクションのクライアントによって定義されたインターフェースであり、サプライヤーコンポーネントがそのインタラクションで使用できるようにするために実行する必要があることを指定します。
ロールインターフェース
ロールインターフェースは、サプライヤーとコンシューマー間の特定のインタラクションを見ることで定義されます。サプライヤーコンポーネントは通常、これらのインタラクションのパターンのそれぞれに対して、複数のロールインターフェースを実装します。これはHeaderInterfaceとは対照的で、サプライヤーは単一のインターフェースしか持ちません。
ルールエンジン
ルールエンジンを使うべきか?
シール
メソッドまたはクラスをシールすると、サブクラスがそれをオーバーライドできなくなります。
セッター初期化
セッター初期化では、空のオブジェクトを作成し、その後、セッターメソッドを使用して様々なプロパティを設定していきます。(ConstructorInitializationの代替手段です。)
ソフトウェア開発の姿勢
ソフトウェア開発における多くの議論は、話者がDirectingAttitudeを持っているかEnablingAttitudeを持っているかに基づいています。これらの異なる姿勢は、言語、設計、ツール、プロセスなど、多くの選択肢に影響を与えます。
伝えるのではなく、尋ねるな
「伝えるのではなく、尋ねるな」は、オブジェクト指向はデータを操作する関数とデータをバンドルすることであることを人々に思い出させるための原則です。データを取得してそのデータに対して操作を行うのではなく、オブジェクトに何をすべきかを伝えることを思い出させてくれます。これは、データと共にオブジェクトに振る舞いを移動することを促します。
二つの難しいこと
コンピュータサイエンスには、二つの難しいことしかありません。キャッシュの無効化と、名前付けです。
-- フィル・カールトン
型付きコレクション
特に強く型付けされた言語でオブジェクトを使い始めるとき、人々は異なるドメイン型に対して特定のコレクションクラスを持つべきかどうかというよくある質問をします。従業員の集合を格納する会社クラスがある場合、ライブラリの通常の集合クラスを使用するべきか、特定の`EmployeeList`クラス(型付きコレクション)を作成するべきかということです。
統一アクセス原則
モジュールによって提供されるすべてのサービスは、それらがストレージによって実装されているか計算によって実装されているかを明らかにしない統一的な表記を通じて利用可能でなければなりません。
-- ベルトラン・メイヤー
ベルトラン・メイヤーはこの原則を、非常に影響力のある著書オブジェクト指向ソフトウェア構築で提唱しました。
この原則の本質的な点は、人物オブジェクトがあり、その年齢を尋ねた場合、年齢がオブジェクトの格納されたフィールドであるか計算された値であるかにかかわらず、同じ表記を使用する必要があるということです。これは事実上、人物のクライアントは、年齢が格納されているか計算されているかを知らなくても、気にする必要もないことを意味します。
ユーザー定義フィールド
ソフトウェアシステムの一般的な機能として、ユーザーがデータ構造に独自のフィールドを定義できるようにすることがあります。アドレス帳を考えてみましょう。追加したいものはたくさんあります。毎日新しいソーシャルネットワークが登場しているため、ユーザーは連絡先にBunglr IDの新しいフィールドを追加したいと思うかもしれません。
値オブジェクト
プログラミングをしていると、物事を複合体として表現することが役立つことがよくあります。2D座標はx値とy値で構成されます。金額は数値と通貨で構成されます。日付範囲は開始日と終了日で構成され、それ自体が年、月、日の複合体になります。
このようにすると、2つの複合オブジェクトが同じかどうかという疑問に遭遇します。両方とも(2,3)のデカルト座標を表す2つの点オブジェクトがある場合、それらを等しいものとして扱うのが理にかなっています。この場合、そのx座標とy座標などのプロパティの値のために等しいオブジェクトは、値オブジェクトと呼ばれます。