日々の開発では、関数を実装するために多数のアルゴリズムや戦略が存在する状況に遭遇することがよくありますが、この関数はさまざまなアルゴリズムや戦略に基づいて実装できます。例: 物流計算方法を計算する場合、すべてが請求されます。速達ごとに、JD Express、Best Express、YTO Express などの請求方法も異なります。送料の計算方法が異なります。では、どうすればそれを達成できるのでしょうか?単純なものは、if...else...または switch...case... です。これら 2 つの実装はハードコーディングと呼ばれます。 Yunda Express のような新しい請求方法がある場合は、アルゴリズムのソース コードを変更する必要があります。これにより、コードが肥大化して保守が困難になります。
したがって、それぞれが独自のアルゴリズムを持つ方法を実装する必要があり、クライアントで呼び出すメソッドを選択するだけで済みます。
環境クラス (Context): ConcreteStrategy オブジェクトで構成されます。 Strategy オブジェクトへの参照を維持します。 Strategy がそのデータにアクセスできるようにするインターフェイスを定義できます。
抽象戦略クラス (戦略): サポートされているすべてのアルゴリズムのパブリック インターフェイスを定義します。 Context はこのインターフェイスを使用して、ConcreteStrategy によって定義されたアルゴリズムを呼び出します。
具体的な戦略クラス (ConcreteStrategy): Strategy インターフェイスを使用して特定のアルゴリズムを実装します。
さまざまな運送会社の運賃を例として取り上げます:
ステップ 1: 抽象戦略クラス (請求方法) を定義します
public interface CommandStrategy { /** * 计费方式 * @param message */ void calMoney(String message); }
ステップ 2: 特定の戦略クラスを定義します (さまざまなアルゴリズム クラスがこのインターフェイスを実装します)
public class BaiShiCommand implements CommandStrategy { /** * 百世快递计费方式 * @param message */ @Override public void calMoney(String message) { System.out.println("百世快递收费方式:"+"起步20,每公斤6元"); } }
public class JingDongCommand implements CommandStrategy { /** * 京东快递计费方式 * @param message */ @Override public void calMoney(String message) { System.out.println("京东快递收费方式:"+"起步30,每公斤5元"); } }
public class YuanTongCommand implements CommandStrategy { /** * 圆通快递计费方式 * @param message */ @Override public void calMoney(String message) { System.out.println("圆通快递收费方式:"+"起步10,每公斤8元"); } }
ステップ 3: 環境クラスを定義します
public class CommandContext { public CommandStrategy getInstance(String commandType) { CommandStrategy commandStrategy = null; Map<String, String> allClazz = CommandEnum.getAllClazz(); //拿到对应算法类对应的路径 String clazz = allClazz.get(commandType.trim().toLowerCase()); if (StringUtils.isNotEmpty(clazz)) { try { try { //创建一个对象实例 commandStrategy = (CommandStrategy) Class.forName(clazz).newInstance();//调用无参构造器创建实例 } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } } catch (ClassNotFoundException e) { e.printStackTrace(); } } System.out.println("commandStrategy:"+commandStrategy); return commandStrategy; } }
列挙クラスを定義します:
public enum CommandEnum { JingDong("京东", "com.liubujun.design.command.JingDongCommand"), BaiShi("百世", "com.liubujun.design.command.BaishiCommand"), YuanTong("圆通", "com.liubujun.design.command.YuanTongCommand"); private String name; private String clazz; public static Map<String, String> getAllClazz() { Map<String, String> map = new HashMap<>(8); System.out.println("==================="+Arrays.toString(CommandEnum.values())+"================"); for (CommandEnum commandEnum : CommandEnum.values()) { map.put(commandEnum.getCommand(), commandEnum.getClazz()); } return map; } public String getCommand() { return name; } CommandEnum(String command, String clazz) { this.name = command; this.clazz = clazz; } public void setCommand(String command) { this.name = command; } public String getClazz() { return clazz; } public void setClazz(String clazz) { this.clazz = clazz; } }
Client :
public class MainStart { public static void main(String[] args) { String message = "京东"; CommandContext commandContext = new CommandContext(); //拿到message对应算法的对象实例 CommandStrategy commandStrategy = commandContext.getInstance(message); commandStrategy.calMoney(message); } }
このようにして、クライアントはどのエクスプレス請求方法を直接呼び出すことができます
利点 :
1) 関連するアルゴリズム シリーズ Strategy クラス階層は、Context の一連の再利用可能なアルゴリズムまたは動作を定義します。継承は、これらのアルゴリズムの共通機能を抽出するのに役立ちます。
2) 継承関係を置き換える方法を提供します。継承は、複数のアルゴリズムまたは動作をサポートする別の方法を提供します。 Context クラスを直接サブクラス化して、異なる動作を与えることができます。しかし、これにより動作がコンテキストにハードコーディングされ、アルゴリズムの実装とコンテキストの実装が混在するため、コンテキストの理解、維持、拡張が難しくなり、アルゴリズムを動的に変更することができなくなります。最終的には多数の関連クラスが作成されますが、それらの唯一の違いは、使用するアルゴリズムまたは動作です。アルゴリズムを独立した Strategy クラスにカプセル化すると、コンテキストから独立してアルゴリズムを変更できるため、切り替え、理解、拡張が容易になります。
3) 一部の if else 条件ステートメントを削除する: ストラテジー モードは、条件ステートメントを使用して目的の動作を選択する代替手段を提供します。クラス内にさまざまな動作が積み重ねられている場合、適切な動作を選択するために条件付きステートメントの使用を避けることは困難です。動作を個別の Strategy クラスにカプセル化すると、これらの条件付きステートメントが排除されます。多くの条件ステートメントを含むコードは通常、ストラテジー モードを使用する必要があることを示します。
4) 実装の選択 ストラテジー モードでは、同じ動作のさまざまな実装を提供できます。お客様は、さまざまな時間/空間のトレードオフ要件に基づいて、さまざまな戦略から選択できます。
欠点:
1) クライアントはすべてのポリシー クラスを知っていて、どれを使用するかを決定する必要があります: このモデルには潜在的な欠点があり、クライアントは適切なポリシー クラスを選択する必要があります。これらの戦略の違いを理解する必要があります。この時点で、特定の実装の問題を顧客に明らかにする必要がある場合があります。したがって、戦略モードは、これらのさまざまな行動のバリアントが顧客の行動に関連する場合にのみ必要になります。
2) Strategy と Context の間の通信オーバーヘッド: 各 ConcreteStrategy によって実装されるアルゴリズムが単純であるか複雑であるかに関係なく、それらはすべて Strategy によって定義されたインターフェイスを共有します。したがって、一部の ConcreteStrategy は、このインターフェイスを通じて渡されたすべての情報を使用しない可能性があり、単純な ConcreteStrategy では、どの情報も使用しない可能性があります。これは、コンテキストが決して使用されないパラメータを作成および初期化する場合があることを意味します。このような問題が存在する場合は、戦略とコンテキストをより緊密に結合する必要があります。
3) 戦略パターンにより多くの戦略クラスが生成されます。フライウェイト パターンを使用すると、オブジェクトの数をある程度まで減らすことができます。オブジェクト数の増加戦略では、アプリケーション内のオブジェクトの数が増加します。さまざまなコンテキストで共有できるステートレス オブジェクトとしてストラテジーを実装することで、このオーバーヘッドを軽減できる場合があります。残りの状態はすべてコンテキストによって維持されます。コンテキストは、リクエストごとにこの状態を Strategy オブジェクトに渡します。共有ストラテジーでは、呼び出し間で状態を維持すべきではありません。
以上がJava デザイン パターンでのストラテジー パターンの実装の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。