最小驚きの原理(POLA)とインターフェース


17

四半世紀前に私がC ++を学んでいたとき、インターフェースは寛容であるべきだと教えられました。消費者は代わりにソースやドキュメントにアクセスできないかもしれないので、メソッドが呼び出される順番を気にしないでくださいこの。

しかし、私がジュニアプログラマーや上級開発者を指導したときはいつでも、彼らは驚いたことに反応し、これは本当にこれが本当のことなのか、それとも流行から外れたのかと疑問に思いました。

泥のように明確ですか?

これらのメソッド(データファイルを作成するための)とのインターフェイスを検討してください。

OpenFile
SetHeaderString
WriteDataLine
SetTrailerString
CloseFile

もちろん、これらを順番に実行することもできますが、ファイル名(考えてみてくださいa.out)や、含まれているヘッダーとトレーラーの文字列については気にしないと言って、単に呼び出すことができますAddDataLine

それほど極端ではない例として、ヘッダーとトレーラーを省略する場合があります。

さらに、ファイルを開く前にヘッダーとトレーラーの文字列を設定することもできます。

これは認識されているインターフェイス設計の原則ですか、それとも名前が与えられる前のPOLAの方法ですか?

NBは、このインターフェイスの詳細に動揺することはありません。これは、この質問のための単なる例です。


10
「最小限の驚き」の原則は、「アプリケーションプログラマーインターフェース」の設計よりもユーザーインターフェースの設計で広く普及しています。その理由は、ウェブサイトやプログラムのユーザーが読むことを期待することができないということである任意のプログラマはそれらを使用してプログラミングする前に、APIドキュメントを読むために、少なくとも原理的には、期待されながら、それを使用する前に、すべての指示を。
キリアンフォス


7
@KilianFoth:ウィキペディアはこれについて間違っていると確信しています-POLAはユーザーインターフェイスの設計だけではなく、「驚きの原則」という用語(まったく同じです)もボブマーティンによって彼の関数とクラス設計に使用されています「クリーンコード」ブック。
Doc Brown

2
多くの場合、不変のインターフェイスの方が優れています。構築時に設定するすべてのデータを指定できます。あいまいさはなく、クラスの記述はより簡単になります。(もちろん、このスキームは不可能な場合があります。)
usr

4
POLAがAPIに適用されないことについては完全に同意しません。人間が他の人間のために作成するものすべてに適用されます。物事が期待どおりに機能する場合、概念化が容易になり、したがって認知負荷が低くなり、人々はより少ない労力でより多くのことを行うことができます。
ロボットをゲット

回答:


25

最小限の驚きの原則に固執できる1つの方法は、ISPSRP、またはDRYなどの他の原則を考慮することです。

あなたが与えた特定の例では、ファイルを操作するための順序付けに特定の依存関係があることが示唆されているようです。ただし、APIはファイルアクセスとデータ形式の両方を制御します。これは、SRPの違反に少し似ています。

編集/更新:また、APIを使用するたびに同じ手順を繰り返す必要があるため、API自体がDRYに違反するようユーザーに求めていることを示唆しています

IO操作がデータ操作とは別の代替APIを検討してください。API自体が順序を「所有」している場合:

ContentBuilder

SetHeader( ... )
AddLine( ... )
SetTrailer ( ... )

FileWriter

Open(filename) 
Write(content) throws InvalidContentException
Close()

上記の分離により、ContentBuilderライン/ヘッダー/トレーラーを保存する以外に実際に何かを「行う」必要はありません(おそらくContentBuilder.Serialize()、順序を知っているメソッドでもあります)。他SOLID原則に従うことにより、あなたが前または行を追加した後、ヘッダーやトレーラーを設定するかどうか、それはもはや問題で何もするのでContentBuilder、実際にその渡されたまで、ファイルに書き込まれませんFileWriter.Write

また、もう少し柔軟性が高いという利点もあります。たとえば、コンテンツを診断ロガーに書き出すことや、ファイルに直接書き込むのではなく、ネットワークを介して渡すことが役立つ場合があります。

APIを設計する際には、状態、戻り値、例外、コールバックなどのエラーレポートも考慮する必要があります。APIのユーザーは、おそらくプログラムの契約違反や、ファイルI / Oエラーなど制御できないその他のエラーをプログラムで検出できることを期待するでしょう。


まさに私が探していたもの-ありがとう!ISPの記事から:「(ISP)は、クライアントが使用しないメソッドに依存することを強制すべきではないと述べている」
ロビーディー

5
これは悪い答えではありませんが、それでもなお、コンテンツビルダーは、呼び出しの順序SetHeaderAddLine重要な方法で実装される可能性があります。この順序の依存関係を排除するのはISPでもSRPでもないため、単なるPOLAです。
ドク・ブラウン

順序が重要な場合でも、後のステップを実行するには前のステップから返された値が必要になるように操作を定義することにより、POLAを満たすことができます。 FileWriter次にContentBuilderWriteメソッドの最後のステップの値を要求して、すべての入力コンテンツが完全になり、InvalidContentException不要になるようにします。
ダンライオンズ

@DanLyonsそれは、質問者が回避しようとしている状況にかなり近いと思います。API のユーザーが注文を知ったり、気にしたりする必要がある場合。理想的には、API自体が順序を強制する必要があります。そうしないと、潜在的にDRYに違反するようにユーザーに要求することになります。それが、その知識の断片を分割してカプセル化ContentBuilderできる理由ですFileWriter.Write。例外は、コンテンツに問題がある場合(たとえば、ヘッダーの欠落など)に必要です。復帰も機能する可能性がありますが、私は例外を復帰コードに変えることは好きではありません。
ベンコットレル

しかし、DRYに関するより多くのメモを追加し、答えに順序付けすることは間違いなく価値があります。
ベンコットレル

12

これは、POLAだけでなく、バ​​グの原因となる可能性のある無効な状態を防ぐことでもあります。

具体的な実装を提供せずに、例にいくつかの制約を提供する方法を見てみましょう。

最初のステップ:ファイルを開く前に、何も呼び出さないでください。

CreateDataFileInterface
  + OpenFile(filename : string) : DataFileInterface

DataFileInterface
  + SetHeaderString(header : string) : void
  + WriteDataLine(data : string) : void
  + SetTrailerString(trailer : string) : void
  + Close() : void

これで、実際のデータを書き込むことができるインスタンスCreateDataFileInterface.OpenFileを取得するために呼び出す必要があることは明らかDataFileInterfaceです。

2番目のステップ:ヘッダーとトレーラーが常に設定されていることを確認してください。

CreateDataFileInterface
  + OpenFile(filename : string, header: string, trailer : string) : DataFileInterface

DataFileInterface
  + WriteDataLine(data : string) : void
  + Close() : void

ここで、DataFileInterfaceファイル名、ヘッダー、およびトレーラーを取得するために、必要なすべてのパラメーターを事前に提供する必要があります。すべての行が書き込まれるまでトレーラー文字列を使用できない場合は、このパラメーターをClose()(おそらくメソッドの名前を変更してWriteTrailerAndClose())に移動して、少なくともトレーラー文字列なしでファイルを終了できないようにすることもできます。


コメントに返信するには:

インターフェースの分離が好きです。しかし、私はあなたの施行についての提案(例えばWriteTrailerAndClose())がSRPの違反に迫っていると思う傾向があります。(これは私が何度も苦労してきたことですが、あなたの提案は可能性のある例のようです。)どのように対応しますか?

本当です。私が主張するのに必要以上に例に集中したくはありませんでしたが、それは良い質問です。この場合、私はそれを呼び出してFinalize(trailer)、それはあまり役に立たないと主張すると思います。トレーラーの作成と終了は、実装の詳細にすぎません。しかし、あなたが同意しないか、それとは異なる状況を持っている場合、考えられる解決策は次のとおりです。

CreateDataFileInterface
  + OpenFile(filename : string, header : string) : IncompleteDataFileInterface

IncompleteDataFileInterface
  + WriteDataLine(data : string) : void
  + FinalizeWithTrailer(trailer : string) : CompleteDataFileInterface

CompleteDataFileInterface
  + Close()

この例では実際にそれを行いませんが、結果としてテクニックを実行する方法を示しています。

ちなみに、たとえば、多くの行を順番に書き込むために、実際にはメソッドをこの順序で呼び出す必要があると想定しました。これが必要でない場合は、Ben Cottrelが示唆するように、常にビルダーを好むでしょう。


1
あなたは悲しいかなトラップに陥りました。最初から避けるよう明示的に警告しました。ファイル名は必要ありません-ヘッダーもトレーラーもありません。しかし、インターフェースを分割するという一般的なテーマは良いものですので、+ 1 :-)
ロビーディー

ああ、それから私はあなたを誤解しました、これは実装ではなくユーザーの意図を説明していると思いました。
ファビアンシュメングラー

インターフェースの分離が好きです。しかし、私はあなたの施行に関する提案(例えばWriteTrailerAndClose())がSRPの違反に迫っていると思う傾向があります。(これは私が何度も苦労してきたことですが、あなたの提案は可能性のある例のようです。)どのように対応しますか?
kmote

1
@kmoteの回答は長すぎてコメントできませんでした。更新を参照してください
ファビアンシュメングラー

1
ファイル名がオプションの場合は、ファイル名を必要OpenFileとしないオーバーロードを提供できます。
5gon12eder
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.