5

プロシージャ (または関数、モジュールなど) の設計における一般的なベスト プラクティスは何ですか?

より具体的に言うと、getEmployeePhoneNbr(employeeId) というプロシージャをコーディングするとします。内部的には、このプロシージャは、employeeId をキーとするデータベース テーブルをクエリすることによって実装されます。これらの実装の詳細を非表示にしたいのですが、手順が外部ファイルに依存するようになり、環境が変わるとその使用が妨げられます。

プロシージャが外部リソース (ファイル、データベースなど) を使用するときはいつでも、同じ状況が発生します。プロシージャ内でそのリソースの使用をハードコードするのは、どういうわけか間違っているように感じますが、代替手段が何であるかはわかりません。

私はオブジェクト指向言語で作業していないことに注意してください。可能な限り、あらゆる種類の言語に広く適用できる回答に最も関心があります。

ありがとう、マット

4

5 に答える 5

3

あなたが抱えている問題の種類は、通常、依存性逆転の原則(別名 DIP) を使用することによって解決されます。元の記事はここにあります。

この記事は主に OO ですが、命令型言語でも適用できます (命令型言語で OO を行うことはできますが、それは難しいだけです)。

原則として、必要な処理 (データベース アクセスなど) を行うオブジェクトへの参照をクライアント オブジェクトに与える方が、このオブジェクトをクライアント オブジェクトにコーディングまたは集約するよりも優れています。

関数レベルでは、それを翻訳して、高レベルの関数に低レベルのデータ/関数を与えることができます。

非 OO 言語での最善の方法は、上位レベルの関数で使用されるデータ/関数を定義する構造体または関数ポインターを渡すことです。

于 2009-08-11T17:43:42.953 に答える
1

これは、実装言語がオブジェクト指向であるかどうかにかかわらず、解決するのが非常に難しい問題です (いずれにせよ、オブジェクト方法論は通常、プログラミング言語が言語構造としてサポートしているかどうかに関係なく適用できます。そのため、私の解決策を言葉で説明しました)オブジェクトの)

あなたができるようにしたいのは、すべてのデータストレージを同等に扱うことです. 実際には、これはほとんど不可能であり、パラダイムを選択してその限界を受け入れる必要があります。たとえば、抽象化の設計を RDBMS パラダイム (接続/クエリ/フェッチ) に基づいて行い、同じインターフェイス内でファイルへのアクセスをカプセル化することを試みることができます。

私が成功して使用したアプローチは、(あなたの場合)従業員の「オブジェクト」内にデータの取得を埋め込むことを避けることです。これにより、プログラム内の従業員の抽象化と保存と取得の間を閉じる結合が作成されます。それはデータです。

代わりに、データを取得して Employee オブジェクトを作成し、そのデータから Employee オブジェクトを作成する別のオブジェクトを作成します。データを適切な汎用構造に変換できれば、任意のデータ ソースから Employee を作成できるようになりました。(連想配列の言語サポートの利点があります。これにより、タプルを渡すプロセスが大幅に簡素化されます。開発言語でこれが困難または不可能になっている場合は、問題が発生する可能性があります)。

これにより、アプリケーションのテストも容易になります。これは、データ ソースの作成 (または、前回そこにあったデータがまだ存在するかどうか) について心配することなく、単体テスト内で直接 Employee "オブジェクト" を作成できるためです。複雑な設計では、このセットアップと破棄がテスト コードの大部分を占める場合があります。さらに、1000 個の従業員「オブジェクト」を作成する必要が生じた場合、データソース (ファイル、データベース、カード インデックスなど) を 1000 回クエリすることなくコードを再利用できます (つまり、有名な ORM N+ をきちんと解決します)。 1クエリの問題)。

要約すると、あなたが説明する隠された依存関係には非常に厄介な落とし穴があるため、データの取得をビジネスロジックから完全に分離してください。IMHO「オブジェクト」の構築内または関数内で特定のデータの取得をカプセル化して、保存されたデータからプロパティを取得することはアンチパターンです。

于 2009-08-10T20:28:13.907 に答える
0

ここでは3層のアプローチを使用することをお勧めします。最初の層はクライアントであり、getEmployeePhoneNbr(employeeId)を消費する層です。2番目の層はデータアクセス層であり、3番目の層はデータ実装層になります。データアクセス層が具体的な情報源にアクセスするために使用します。

データ実装層。

このレイヤーには次のものが含まれます。

  1. データ層がアクセスできるリソースの場所を表すデータ構造。
  2. 新しい構造を作成するためのAPIと、それを構成するための対応する関数。

データアクセス層

含まれています:

  1. データのソースとして使用されるデータ構造へのポインター。
  2. getEmployeePhoneNbr(employeeId)、getEmployeeName(employeeId)など、データにアクセスするために必要なすべての呼び出しを含むパブリックシンプルAPI ....このすべての呼び出しは、特定のデータにアクセスするためにデータ構造へのポインターを内部的に使用します

このアプローチを使用すると、データアクセス層に適切なデータ実装構造を提供するだけで済みます。したがって、変更された場合は、1か所で変更するだけで済みます。

于 2009-08-11T13:03:44.553 に答える
0

ある種のコンテキスト/環境オブジェクトを提供できます。言う:

type Environment = record
      DatabaseHandle: ...;
      ...
   end;

   Employee = record
      ID: integer;
      Name: string;
      ...
   end;


function OpenEnvironment (var Env: Environment): boolean;
begin
   ...
end;

procedure CloseEnvironment (var Env: Environment);
begin
   ...
end;

function GetEmployeeById (var Env: Environment; ID: integer; var Employee: Employee): boolean;
begin
   ... load employee using the data source contained in environment ...
end;

(疑似パスカル)。利点は、Environment構造を使用して、拡張エラー情報やその他のグローバル状態も保存できることです。これにより、 UnixisherrnoまたはWindowのGetLastErrorであるPITAを回避できます。このアプローチのもう1つの利点は、すべてのAPIが再入可能になり、スレッドごとに専用の環境を使用することで、結果としてスレッドセーフになることです。

このアプローチの欠点は、すべてのAPIに追加の引数を渡す必要があることです。

于 2009-08-10T19:55:58.730 に答える
0

リソースの依存関係をルックアップ関数に入れます。多数のリソースが関連している場合、それらを取得するための単純な関数を持つモジュールを作成します。私は個人的に、回避できる場合はそのような参照を渡すことを避けています。途中のコードには、それらを知ったり使用したりするビジネスはありません。

それ以外の:

getEmployeePhoneNbr(employeeId)
    dbName = "employeedb"
    ... SQL, logic, etc.

または:

getEmployeePhoneNbr(employeeId, dbName)
    ... SQL, logic, etc.

私は次のことをします:

getEmployeePhoneNbr(employeeId)
    dbName = getEmployeeDbName()
    ... SQL, logic, etc.

このように getEmployeeDbName() を変更すると、依存するすべての関数とモジュールが恩恵を受けます。

于 2009-08-11T17:43:45.747 に答える