拡張可能なメソッドの書き込み
メソッドを拡張する前に、メソッドの公開されている機能と、メソッドが使用されるシナリオに拡張が与える影響を評価する必要があります。 たとえば、ビジネス シナリオによっては、テーブル レコードを初期化する拡張機能を有効にした場合に低リスクになりますが、固有の検証をスキップする拡張機能を有効にした場合にリスクが高くなります。 メソッドが他の機能拡張と並行して拡張される場合の影響を検討することもできます。
メソッドを拡張可能にすると、メソッドのシグネチャまたはロジックが変更された場合のユーザーへの潜在的な影響のため、メソッドのさらなる変更が制限されます。
拡張可能コードを作成するときに準拠するべきいくつかのガイドラインを次に示します。
短くて簡潔なメソッドを記述: メソッドの責任は 1 つだけにしてください。 これによりでは、メソッドの拡張が簡単になり、拡張がメソッドの特定の責任でのみ機能します。 単純な例として、クラス オブジェクトの構築および初期化を 2 つの異なるメソッドに保持します。
必要なものだけを公開: 追加される新しいクラス メンバーまたはメソッドの場合、追加する新しいクラス メンバーまたはメソッドを非公開のままにし、最小限のアクセスを許可します。
プライベート、保護、パブリック、最終を明示的に使用する: メソッドおよびクラス フィールドでは、この方法により、コードの拡張担当者を拡張ポイントに導きますが、拡張担当者が注意しなかったり依存しなかった部分を完全に管理し続けることができます。
メソッド parameters
- このメソッドは、多くの場合長くなるため、リファクタリングする必要があります。 メソッド全体をクラスにリファクタリングするか、必要なパラメーターが少ないより小さいメソッドにメソッドを分割するかを検討してください。
- それ以外の場合、いくつかのパラメーターが必要なときは、パラメーターには多くの場合がクラスによって表すことができる一貫性があります。 クラスでこれらのパラメーターをカプセル化することにより、アプリケーション プログラミング インターフェイス (API) を後で中断せずに、拡張担当者が基準メソッドにパラメーターを追加しやすくなります。
ブロックの切り替え
メソッドの途中でブロックを切り替えないでください。 切り替えブロックは、拡張可能になるにはそれ自身のメソッドになければなりません。
長いケース ブロックは、各ケース ブロックのサブクラスを持つクラス/クラス階層にリファクタリングされるのに適した候補です。 例については、SalesLineCopyFromSource クラス階層を参照してください。
切り替えステートメントでは既定のブロックを回避してください。切り替えブロックを拡張不能にするメソッドを作成するためです。
切り替えステートメントの既定のブロックでスロー ステートメントを回避してください。切り替えステートメントを拡張不能にするスイッチを作成するためです。 既定のケースでのスローを処理する 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 ループ内でのリファクタリング ロジック
リファクタリング後の拡張可能メソッド
If..else ステートメント
- if ステートメントで条件を拡張できるようにするには、if 条件のロジックを別のメソッドに抽出します。
- 入れ子になった if..else ブロックは回避してください。いずれかのロジックでの変更がむずかしくなるためです。 この問題を解決するための 1 つの方法は、各条件および各ブロックのロジックを個別のメソッドにリファクタリングすることです。 この方法では、条件または各ブロックのロジックを拡張することができます。
- if..else ブロックが特殊化を処理する場合、ロジックをクラス階層に移動することを検討してください。 例については、SalesLineCopyFromSource を参照してください。
- 一部のシナリオでは、メソッドの "else" ブロックでのスロー (メソッドに if.. else のみある場合) によってメソッドが拡張不能になります。 else でのスローを処理する 1 つの方法は、スローの条件を個別のメソッドにリファクタリングすることです。
PrmIsDefault の使用を避ける – メソッドがオーバーライドされるか折り返し可能な場合、super() または next() の呼び出し元がすべてのパラメーターを提供します。 したがって、prmIsDefault() は常に false を返します。
enumCnt の使用を避ける – コンパイル時、このメソッドは列挙が持つ値の数の数値リテラルを使用します。 列挙が拡張されるか、後で拡張可能になった場合、コードを再コンパイルする必要があります。 代わりに DictEnum.values() を使用します。
Construct メソッド
- 拡張を簡単にするには、SysExtension フレームワークを使用します。
- ファクトリ メソッドでのスローを回避します。 この問題を解決するための 1 つの方法は、拡張できる別のメソッドにスローの条件を抽出することです。 詳細については、このリストの後方にあるスロー ステートメントのガイドラインを参照してください。
静的メソッド: 余分な状態によって静的メソッド拡張することはできません。 たとえば、メソッド拡張担当者は、パラメーター メソッドを使用して設定できるプロパティを導入できます。 この方法が可能な場合は必ず、代わりにインスタンス メソッドを使用します。
長いメソッドでロジックの一部を拡張する機能: メソッド全体をリファクタリングすることができないが、目標がメソッドの一部を拡張可能にすることである場合、抽出メソッド リファクタリングを適用します。 新しい保護されたメソッドには単一の責任が必要であり、その責任を概念的かつ正確に説明する名前も必要です。 これにより、オーナーおよびすべての拡張担当者は互いに中断することがなくメソッドを使用できます。 たとえば、初期化、挿入、テーブル レコードの更新、またはクラスのインスタンス化および初期化は、小さいメソッドに抽出でき、それらの小さい各メソッドで拡張を有効にできます。 その後、元のメソッドがこれらの個々のメソッドを呼び出します。 したがって、このメソッドの呼び出し元は中断されません。
スロー ステートメント : 拡張可能な既存のメソッドに追加されるスローは、拡張担当者を中断させる可能性があります。 拡張可能メソッドでスローの条件を追加することを検討してください。 これにより、拡張担当者はメソッドを利用でき、スローを取り除くことができます。
条件が保護されているメソッドにリファクタリングされる場合
リファクタリング後の拡張可能メソッド
作成、読み取り、更新、および削除 (CRUD) ステートメント
- クエリを拡張可能にする必要のある状況でクエリ オブジェクトを使用します。 クエリを構築する保護されたメソッドを実装します。 さらに、結合されたデータ ソース、範囲、および選択フィールドを追加するために、複数の個別のメソッドを構築できます。 この方法では、クエリのさまざまな部分を個別に拡張できます。
- SysQueryInsertRecordSet を使用して、insert_recordset をクエリに変換します。
- select ステートメントではフィールド リストは避けてください。 この方法では、拡張することがなく、拡張担当者が追加のフィールドを取得できるようにすることができます。
- クエリ範囲で in キーワードを使用し、拡張担当者が値をさらにクエリ範囲に追加できるようにします。 この方法は特に、列挙値を持つクエリ範囲にお勧めします。