次の方法で共有


拡張可能なメソッドの書き込み

コミュニティの関心グループが Yammer から Microsoft Viva Engage に移行されました。 Viva Engage コミュニティに参加し、最新のディスカッションに参加するには、「 Finance and Operations Viva Engage Community へのアクセスを要求する 」フォームに入力し、参加するコミュニティを選択します。

メソッドを拡張可能にする前に、メソッドの公開されている機能と、拡張機能がメソッドを使用するシナリオに与える可能性がある影響を評価します。 たとえば、ビジネス シナリオによっては、拡張機能を有効にしてテーブル レコードを初期化する場合はリスクが低くなりますが、拡張機能で特定の検証をスキップできるようにする場合はリスクが高くなります。 メソッドが他の拡張機能と並行して拡張される場合は、影響を考慮してください。

拡張可能なメソッドを作成すると、メソッドシグネチャまたはロジックが変更された場合にユーザーに影響を与える可能性があるため、メソッドに対する将来の変更が制限されます。

拡張可能なコードを記述するときは、次のガイドラインに従ってください。

  • 短くて簡潔なメソッドを記述: メソッドの責任は 1 つだけにしてください。 これによりでは、メソッドの拡張が簡単になり、拡張がメソッドの特定の責任でのみ機能します。 単純な例として、クラス オブジェクトの構築および初期化を 2 つの異なるメソッドに保持します。

  • 必要なもののみを公開 する - プライベートに追加する新しいクラス メンバーまたはメソッドを保持して、それらに最小限のアクセスを許可します。

  • プライベート、保護、パブリック、および最後を明示的に使用 する – メソッドとクラス フィールドの場合、このアプローチではコードのエクステンダーを拡張ポイントに誘導しますが、エクステンダーが気にしたり依存したりしてはならない部分を完全に制御できます。

  • メソッド パラメーター

    • このメソッドは、多くの場合長くなるため、リファクタリングする必要があります。 メソッド全体をクラスにリファクタリングするか、必要なパラメーターが少ないより小さいメソッドにメソッドを分割するかを検討してください。
    • また、複数のパラメーターが必要な場合、多くの場合、パラメーターはクラスが表現できる一貫性を持ちます。 クラスでこれらのパラメーターをカプセル化することにより、アプリケーション プログラミング インターフェイス (API) を後で中断せずに、拡張担当者が基準メソッドにパラメーターを追加しやすくなります。
  • ブロックの切り替え

    • メソッドの途中でブロックを切り替えないでください。 切り替えブロックは、拡張可能になるにはそれ自身のメソッドになければなりません。
    • 長いケース ブロック は、各ケース ブロックのサブクラスを持つクラスまたはクラス階層にリファクタリングする場合に適しています。 例については、SalesLineCopyFromSource クラス階層を参照してください。
    • 切り替えステートメントでは既定のブロックを回避してください。切り替えブロックを拡張不能にするメソッドを作成するためです。
    • スイッチ文のデフォルトブロックでthrow文を使用することを避けてください。これは、スイッチ文を拡張不可にするためです。 既定のケースでのスローを処理する 1 つの方法は、拡張可能な個別のメソッドに切り替えブロックをリファクタリングすることです。 または、メソッド全体を交換可能にすることができます。

次の例では、findOrderHeaderDefault が交換可能です。

private Common findOrderHeader(boolean _forUpdate)
{
    switch (this.InventTransType)
    {
        case InventTransType::Sales:
            return this.salesTable(_forUpdate);
        default: 
            return this.findOrderHeaderDefault(_forUpdate);
    }
}

[Replaceable]
protected Common findOrderHeaderDefault(boolean _forUpdate)
{
    throw error(Error::wrongUseOfFunction(funcName()));
}
  • While – メソッドの途中では while ブロックを回避してください。while ブロックを拡張しにくくなるからです。 理想的には、while ブロックのロジックは、拡張機能を有効にする別のメソッドに置く必要があります。

    while ループ内でのリファクタリング ロジック

    while ブロックをリファクタリングする前のスクリーンショット。

    リファクタリング後の拡張可能メソッド

    while ブロックをリファクタリングした後のスクリーンショット。

  • If..else ステートメント

    • if ステートメントで条件を拡張できるようにするには、if 条件のロジックを別のメソッドに抽出します。
    • 入れ子になった if..else ブロックは回避してください。いずれかのロジックでの変更がむずかしくなるためです。 この問題を解決するための 1 つの方法は、各条件および各ブロックのロジックを個別のメソッドにリファクタリングすることです。 この方法では、条件または各ブロックのロジックを拡張することができます。
    • if..else ブロックが特殊化を処理する場合、ロジックをクラス階層に移動することを検討してください。 例については、SalesLineCopyFromSource を参照してください。
    • 一部のシナリオでは、メソッドの "else" ブロックでのスロー (メソッドに if.. else のみある場合) によってメソッドが拡張不能になります。 else ブロック内のthrow文を処理する1つの方法は、throwの条件を個別のメソッドにリファクタリングすることです。
  • PrmIsDefault の使用を避ける – メソッドがオーバーライドされるか折り返し可能な場合、super() または next() の呼び出し元がすべてのパラメーターを提供します。 したがって、prmIsDefault() は常に false を返します。

  • enumCnt の使用を避ける – コンパイル時、このメソッドは列挙が持つ値の数の数値リテラルを使用します。 列挙型が拡張されるか、後で拡張可能になる場合は、コードを再コンパイルする必要があります。 代わりに DictEnum.values() を使用します。

  • 構築メソッド

    • 拡張を簡単にするには、SysExtension フレームワークを使用します。
    • ファクトリ メソッドでのスローを回避します。 この問題を解決するための 1 つの方法は、スローの条件を拡張可能な別のメソッドに抽出することです。 詳細については、このリストの後方にあるスロー ステートメントのガイドラインを参照してください。
  • 静的メソッド: 余分な状態によって静的メソッド拡張することはできません。 たとえば、メソッド拡張担当者は、パラメーター メソッドを使用して設定できるプロパティを導入できます。 この方法が可能な場合は必ず、代わりにインスタンス メソッドを使用します。

  • 長いメソッドでロジックの一部を拡張する機能: メソッド全体をリファクタリングすることができないが、目標がメソッドの一部を拡張可能にすることである場合、抽出メソッド リファクタリングを適用します。 新しい保護されたメソッドには単一の責任が必要であり、その責任を概念的かつ正確に説明する名前も必要です。 これにより、オーナーおよびすべての拡張機能の利用者は互いに干渉することなくメソッドを使用できます。 たとえば、初期化、挿入、テーブル レコードの更新、またはクラスのインスタンス化および初期化は、小さいメソッドに抽出でき、それらの小さい各メソッドで拡張を有効にできます。 その後、元のメソッドがこれらの個々のメソッドを呼び出します。 したがって、このメソッドの呼び出し元に問題はありません。

  • Throw ステートメント – 拡張可能な既存のメソッドに追加するthrowは、拡張機能に影響を与える可能性があります。 拡張可能なメソッドで例外処理の条件を追加することを検討してください。 これにより、拡張担当者はメソッドを利用でき、スローを取り除くことができます。

    条件が保護されているメソッドにリファクタリングされる場合

    リファクタリング前の throw ステートメントのスクリーンショット。

    リファクタリング後の拡張可能メソッド

    リファクタリング後の throw ステートメントのスクリーンショット。

  • 作成、読み取り、更新、および削除 (CRUD) ステートメント

    • クエリを拡張可能にするシナリオでは、Query オブジェクトを使用します。 クエリを構築する保護されたメソッドを実装します。 また、結合されたデータ ソース、範囲、および選択フィールドを追加するために、複数の個別のメソッドを作成することもできます。 この方法を使用すると、クエリのさまざまな部分を個別に拡張できます。
    • SysQueryInsertRecordSet を使用して、insert_recordset をクエリに変換します。
    • select ステートメントではフィールド リストは避けてください。 この方法を使用すると、拡張する必要なく、エクステンダーが追加のフィールドを取得できるようになります。
    • クエリ範囲で in キーワードを使用し、拡張担当者が値をさらにクエリ範囲に追加できるようにします。 この方法は、列挙値を持つクエリ範囲に特に推奨されます。