Delphi モード プログラミングのストラテジー モード (続き)
劉毅
1.3 ホテル管理システムにおける戦略モデルの適用
ホテル管理システムでは、通常、部屋の価格は固定されません。宿泊施設の閑散期と繁忙期、古くからの顧客と新規顧客、個人客と団体客など、それぞれに異なる販売戦略があるはずだ。明らかに、販売戦略がオファーを決定します。しかし、営業戦略に基づく見積システムは、特定の顧客に拘束することはできず、営業戦略に基づく見積システムを独立させることによってのみ、その再利用性や保守性が担保される。例えば、見積りシステムは、宿泊料金の優遇照会や宿泊決済など複数のクライアントのニーズに応える一方で、新たな販売戦略を常に調整するニーズにも応え、真の再利用性と保守性を実現します。 。上記の設計要件の場合、戦略モードを選択するのが最善です。 Strategy パターンを使用すると、それを使用するクライアントに関係なくアルゴリズムを変更できます。サンプルプログラムは、戦略モデルに基づく住宅価格優遇クエリモジュールであり、販売戦略に基づく見積システムと住宅価格優遇クエリインターフェースが含まれています。もちろん、優待価格照会インターフェースは、見積システムのクライアントの一つに過ぎず、見積システムは他のクライアントでも利用可能である。優待価格照会モジュールの設計を図 1-6 に示します。これには以下が含まれます。 · 販売戦略クラス TSaleStrategy。特定の販売戦略クラスの抽象基本クラスです。 · 3 つの具体的な販売戦略カテゴリ: TVipStrategy (VIP カード販売戦略)、TTeamStrategy (チーム販売戦略)、TSeasonStrategy (季節販売戦略)。 · 引用クラス TPRiceContext。この戦略パターンのコンテキストであり、TStrategy への参照を保持します。 · クライアント クラス TClient は、住宅価格照会用のインターフェイスであるフォーム クラスです。図 1-6 戦略パターンに基づく優待宿泊料金照会モジュールのサンプルプログラム 1-1 は、HotelSaleStrategy ユニットのソースコードであり、販売戦略に基づく見積システムのビジネス ロジックが含まれており、戦略パターン。 TSaleStrategy は、販売戦略の抽象基本クラスとして、共通のインターフェイスを提供することを目的としています。仮想抽象関数 SalePrice はそのようなインターフェイスです。 3 つの具体的な販売戦略はシーズン、VIP カード、チームの規模に基づいて策定されるため、基本クラス インターフェイス SalePrice のパラメータ設計は 3 つの派生クラスのさまざまなニーズを満たす必要があります。 TSaleStrategy の SalePrice 関数は次のように宣言されます: function SalePrice(price:Currency;value:integer):Currency; abstract; その最初のパラメータは受信される固定価格を表し、2 番目のパラメータは受信される優先条件を表します。さまざまな派生クラス用。季節販売戦略 TSeasonStrategy では、このパラメータはチェックイン月として表され、VIP カード販売戦略 TVIPStrategy では、このパラメータは VIP カードのタイプとして表されます。チームの人数。これらのパラメーターはすべて整数型を使用できることがわかりました。そのため、基本クラスでは値パラメーターを巧みに使用して、派生クラスのさまざまなパラメーター要件を解決しています。このように、TPriceContext はデータをパラメータに直接入力し、それをさまざまな販売戦略オペレーションに渡すことができ、パラメータの冗長性を回避します。 {TPriceContext }function TPriceContext.GetPrice(price:Currency;value:integer):Currency;begin result:=Strategy.SalePrice(price,value);end;TPriceContext は、この戦略モードでコンテキストの役割を果たし、売上高を参照する責任があります。 Strategy オブジェクトのさまざまなインスタンスは、SalePrice インターフェイスを呼び出して、特定の割引アルゴリズムを動的に構成し、実際の販売価格を返します。 TPriceContext が仲介するため、クライアントは特定の販売戦略がどのように実装されているかを知る必要がありません。同様に、販売戦略が更新および調整されても、クライアント プログラムには影響しません。サンプル プログラム 1-1 HotelSaleStrategy ユニットのソース コード HotelSaleStrategy;インターフェイスは SysUtils、Windows、Messages、Classes、Graphics、Controls、Forms、Dialogs を使用します;type TSaleStrategy = class (TObject) public function SalePrice(price:Currency;value:integer): Currency ; 仮想; TSeasonStrategy = クラス (TSaleStrategy) パブリック関数SalePrice(価格:通貨;値:整数):通貨オーバーライド; TVIPStrategy = クラス (TSaleStrategy) パブリック関数SalePrice(価格:通貨;値:整数):通貨オーバーライド = クラス; (TObject) プライベート FStrategy: TSaleStrategy; プロシージャ SetStrategy(Value: TSaleStrategy); public function GetPrice(price:Currency;value:integer):Currency; プロパティ Strategy: TSaleStrategy read FStrategy write SetStrategy; TSeasonStrategy.SalePrice(price:Currency;value:integer):Currency;begin //季節販売戦略 {2 月、3 月、11 月は 15% オフ、4 月と 6 月は 10% オフ。 8. 9月は9.5%割引。 2,3,11:result:=price*0.85; 4,6:result:=price*0.95; else result:=price*0.95; の場合TVIPStrategy }function TVIPStrategy.SalePrice(price:Currency;value:integer):Currency;begin //VIP カード販売戦略{ 0: VIP シルバー カードの場合は 10% オフ 1: VIP ゴールド カードの場合は 20% オフ 2: VIP ダイヤモンド カードの場合は 30% オフ} の場合の値 0:result:=price*0.9; 1:result:=price*0.8; :result: =price*0.7; 終了;終了;{TTeamStrategy }関数TTeamStrategy.SalePrice(price:Currency;value:integer):Currency;begin //チーム販売戦略 {3 ~ 5 人のチームの場合は 10% オフ、6 ~ 10 人のチームの場合は 30% オフ。 11 ~ 20 人のチーム、1 人以上のグループの場合は 40% オフ。結果:= 価格; (値 < 6) および (値 > 2) の場合、結果:= 価格 *0.9; (値 < 11) および (値 > 5) の場合、結果:= 価格 *0.8 21) and (値>10) then result:=price*0.7; if (value>20) then result:=price*0.6;end;{TPriceContext }関数TPriceContext.GetPrice(価格:通貨;値:整数):通貨;開始結果:=Strategy.SalePrice(価格,値);終了;手順 TPriceContext.SetStrategy(値: TSaleStrategy);開始 FStrategy:=値;終了;終了。優待宿泊料金照会モジュールのクライアントプログラムをサンプルプログラム1-2に示します。プログラムは、問い合わせ者が優先プランを選択できるように、ユーザー選択インターフェイスを提供します。優待条件と公開料金を選択したら、「優待料金を確認する」ボタンをクリックすると、割引料金が表示されます。実際の運用効果を図 1-7 に示します。サンプル プログラム 1-2 ClientForm ユニットのソース コード ユニット ClientForm; インターフェイスは Windows、Messages、SysUtils、Variant、Classes、Graphics、Controls、Forms、Dialogs、StdCtrls、ExtCtrls、HotelSaleStrategy、ComCtrls、DateUtils を使用します。 type TClient = class(TForm) RadioGroup1 : TRadioGroup; btnCheck: TButton: TButton; cmbVIP: TLabel; TComboBox: TLabel; ラベル 5: TBevel;プロシージャ FormCreate(送信者: TObject); プロシージャ FormDestroy(Sender: TObject); プロシージャ RadioGroup1Click(Sender: TObject); TPriceContext パブリック; { パブリック宣言 } end;var クライアント: TClient;implementation{$R *.dfm}procedure TClient.FormCreate(Sender: TObject);begin FSeasonStrategy:=TSeasonStrategy.Create; FTeamStrategy:=TTeamStrategy.Create; ; FPriceSys:=TPriceContext.Create;end;procedure TClient.btnCheckClick(Sender:TObject);var i:integer;price:Currency;begin case RadioGroup1.ItemIndex of 0:begin FPriceSys.Strategy:=FSeasonStrategy ; .DateTime); 1:開始; FPriceSys.Strategy:=FVIPStrategy; 2:begin FPriceSys.Strategy(edtCount.Text) 終了; 300; //クラスAの標準部屋は300元です 1:price:=500; //クラスBのスタンダードルームは500元 2:price:=800; //VIPルームは800元 3:price:=1000; //ビジネススイートは1,000元 4:price:=2000;元のエンド; edtPrice .Text:=CurrToStr(FPriceSys.GetPrice(price,i));end;プロシージャ TClient.FormDestroy(送信者: TObject); FPriceSys.Free; FVIPStrategy.Free; プロシージャ TClient.btnExitClick(Sender:=false); edtCount.Enabled:=false; cmbVIP.Enabled:=false; 0:dtpDate.Enabled:=true; の場合、edtCount.Enabled:=true;図 1-7 優待価格クエリモジュールの実際の実行インターフェイス
1.4 実践のまとめ
前の例のデモンストレーションと分析を通じて、次のように戦略パターンについてさらに説明します。 · 戦略パターンは、一連のアルゴリズムを管理する方法を提供します。戦略クラスの階層は、TContext の一連の再利用可能なアルゴリズムまたは動作を定義します。 TStrategy 基本クラスはこれらのアルゴリズムの共通機能を抽出し、派生クラスは継承を通じてアルゴリズムの違いと種類を強化し、コードの重複を回避します。 · アルゴリズムを、アルゴリズムが使用されるコンテキストから分離せず、アルゴリズムを含む TContext クラスの派生クラスを直接生成し、それに異なる動作を与える場合、動作が TContext にハードコーディングされます。アルゴリズムの実装を TContext から分離する 実装が混在しているため、TContext の理解、維持、拡張が困難になります。最終的には、関連するクラスが多数生成されます。それらの唯一の違いは、使用するアルゴリズムです。明らかに、クラスの継承関係は強い関連であり、継承関係はアルゴリズムを動的に変更できませんが、オブジェクトの構成関係は弱い関連です。ストラテジ クラス オブジェクトを組み合わせることで、アルゴリズムは環境 (TContext) に依存せずに進化できます。アルゴリズムが使用されています。 · ストラテジ パターンを使用して、多数の条件付き分岐ステートメントを使用するプログラム コードをリファクタリングします。クラス内にさまざまな動作が積み重ねられている場合、適切な動作を選択するために条件付きステートメントの使用を避けることは困難です。動作を別のポリシー クラスにカプセル化すると、これらの条件ステートメントが排除されます。 · アルゴリズムが多すぎると、ポリシー オブジェクトの数が大量になる可能性があります。システムのオーバーヘッドを軽減するために、通常、アルゴリズム環境に依存する状態をクライアントに保存でき、TStrategy はさまざまなクライアントで共有できるステートレス オブジェクトとして実装されます。外部状態はすべて TContext によって維持されます。 TContext は、リクエストごとにこの状態を TStrategy オブジェクトに渡します。たとえば、サンプル プログラムでは、TSeasonStrategy の外部ステータス チェックイン月、TVIPStrategy の外部ステータス VIP カード タイプ、TTeamStrategy の外部ステータス チーム サイズをクライアントに保存し、これらのステータスを TPriceContext を通じて販売戦略クラスに渡します。この利点は、販売戦略クラスがステートレスになり、客室決済モジュールなどの他のモジュールで共有できることです。 · それぞれの特定の戦略によって実装されるアルゴリズムが単純であるか複雑であるかに関係なく、それらはすべて TStrategy によって定義されたインターフェイスを共有します。したがって、一部の特定のポリシーでは、このインターフェイスを通じて渡されるすべての情報が使用されない可能性があります。サンプルプログラムでTSaleStrategyのインターフェースを次のように設計すると、
SalePrice(価格:通貨;月:整数;VIP:整数;
カウント:整数):通貨;
これらのパラメータの一部は、特定の販売戦略クラスでは決して使用されません。これは、TContext が決して使用されないパラメータを作成および初期化する場合があることを意味します。このような問題があり、サンプルプログラムの手法が使えない場合には、TStrategyとTContextの密結合方式を採用するしかありません。
その他の関連記事やサンプル プログラムのソース コードは、著者の Web サイト http://www.liu-yi.net からダウンロードできます。