105

OCP の原則と、戦略パターンを使用してこれを達成する方法について読んでいます。

これを数人に説明しようとしましたが、私が思いつく唯一の例は、「注文」のステータスに基づいてさまざまな検証クラスを使用することです。

私はいくつかの記事をオンラインで読みましたが、これらは通常、レポート/請求書/検証の生成など、戦略を使用する本当の理由を説明していません...

戦略パターンが一般的だと思われる実例はありますか?

4

19 に答える 19

73

繰り返しますが、古い投稿ですが、検索でまだ出てくるので、さらに 2 つの例を追加します (コードは C# です)。プロジェクトマネージャーが「アプリケーションに「X」を実行させたいが、「X」はまだ明確ではなく、近い将来変更される可能性があります。 " 戦略パターンを説明するこのビデオでは、例として StarCraft を使用しています。

このカテゴリに該当するもの:

  • 並べ替え: これらの数値を並べ替えたいのですが、BrickSort、BubbleSort、またはその他の並べ替えを使用するかどうかはわかりません

  • 検証:「何らかのルール」に従ってアイテムをチェックする必要がありますが、そのルールがどのようなものになるかはまだ明確ではなく、新しいルールを考える可能性があります。

  • ゲーム: プレイヤーが移動するときに歩くか走るかのどちらかを求めていますが、将来的には、泳いだり、飛んだり、テレポートしたり、地下に穴を掘ったりすることもできるようにする必要があります。

  • 情報の保存: アプリケーションでデータベースに情報を保存する必要がありますが、後でファイルを保存したり、Web 呼び出しを行う必要がある場合があります。

  • 出力: X をプレーンな文字列として出力する必要がありますが、後で CSV、XML、JSON などになる可能性があります。


ユーザーがデータベース内の人に製品を割り当てることができるプロジェクトがあります。個人への製品のこの割り当てには、いくつかのビジネス ルールに依存する「承認済み」または「拒否済み」のいずれかのステータスがあります。たとえば、ユーザーが特定の年齢の人に製品を割り当てた場合、そのステータスは拒否する必要があります。アイテムの 2 つのフィールドの差が 50 より大きい場合、そのステータスは拒否されます。

現在、開発の時点では、これらのビジネス ルールはまだ完全に明確ではなく、新しいルールがいつでも登場する可能性があります。戦略パターンの威力は、RuleAgent を作成したことです。RuleAgent には、IRule のリストが与えられます。

public interface IRule {
    bool IsApproved(Assignment assignment); 
 }

製品を人に割り当てる瞬間に、RuleAgent を作成し、それにルールのリスト (すべて IRule を実装する) を渡し、割り当てを検証するように依頼します。それはすべてのルールを実行します。それらはすべて同じインターフェースを実装しているため、すべてIsApprovedメソッドを持ち、いずれかが false を返す場合は false を返します。

たとえば、マネージャーが突然現れて、インターンへのすべての割り当て、または時間外労働者へのすべての割り当ても辞退する必要があると言った場合...次のような新しいクラスを作成します。

public OvertimeRule : IRule
{
    public bool IsApproved(Assignment assignment) //Interface method
    {
        if (assignment.Person.Timesheet >= 40)
        {
            return false;
        }
        return true;
    }
}

public InternRule : IRule
{
    public bool IsApproved(Assignment assignment) //Interface method
    {
        if (assignment.Person.Title == "Intern")
        {
            return false;
        }
        return true;
    }
}

if ステートメントやコードを追加または削除し続ける必要がないことがわかります。IRUle インターフェースを実装する新しい規則クラスを作成し、必要に応じてそれらを切り替えるだけです。


もう 1 つの優れた例: Scott Allen のビデオ シリーズ ( http://www.asp.net/mvc/pluralsight ) では、アプリケーションの単体テスト部分で戦略パターンを使用しています。

彼は、人気に基づいてアイテムを表示するページを持つ Web サイトを構築します。ただし、「人気」には多くの要素が含まれる可能性があります (ほとんどのビュー、ほとんどの購読者、作成日、最も多くのアクティビティ、最も少ないコメント数など)。注文は後日。order メソッドを使用してインターフェイス (IOrderAlgorithm など) を作成し、Orderer オブジェクトに順序付けを IOrderAlgorithm インターフェイスの具体的な実装に委譲させます。「CommentOrderer」、「ActivityOrderer」などを作成できます...そして、新しい要件が発生したときにこれらを切り替えるだけです。

于 2014-01-16T14:57:09.307 に答える
23

重要な注意事項:

  1. 戦略は行動設計パターンです。アルゴリズムのファミリを切り替えるために使用されます。

  2. このパターンには、1 つの抽象的な戦略インターフェースと、そのインターフェースの多くの具体的な戦略実装 (アルゴリズム) が含まれています。

  3. アプリケーションは戦略インターフェースのみを使用します。設定パラメータによっては、具体的な戦略interfaceにタグ付けされます。

ウィキペディアのUMLダイアグラム

ここに画像の説明を入力

実例の 1 つ:ある月 (7 月から 12 月) に割引を提供する航空会社。月数に応じて価格設定オプションを決定する運賃モジュールを 1 つ持つことができます。

簡単な例を見てみましょう。この例は、特別な日やハッピーアワーにショッピング カートの商品を簡単に割引するオンライン小売アプリケーションに拡張できます。

import java.util.*;

/* Interface for Strategy */
interface OfferStrategy {
    public String getName();
    public double getDiscountPercentage();
}
/* Concrete implementation of base Strategy */
class NoDiscountStrategy implements OfferStrategy{
    public String getName(){
        return this.getClass().getName();
    }
    public double getDiscountPercentage(){
        return 0;
    }
}
/* Concrete implementation of base Strategy */
class QuarterDiscountStrategy implements OfferStrategy{
    public String getName(){
        return this.getClass().getName();
    }
    public double getDiscountPercentage(){
        return 0.25;
    }
}
/* Context is optional. But if it is present, it acts as single point of contact
   for client. 

   Multiple uses of Context
   1. It can populate data to execute an operation of strategy
   2. It can take independent decision on Strategy creation. 
   3. In absence of Context, client should be aware of concrete strategies. Context acts a wrapper and hides internals
   4. Code re-factoring will become easy
*/
class StrategyContext {
    double price; // price for some item or air ticket etc.
    Map<String,OfferStrategy> strategyContext = new HashMap<String,OfferStrategy>();
    StrategyContext(double price){
        this.price= price;
        strategyContext.put(NoDiscountStrategy.class.getName(),new NoDiscountStrategy());
        strategyContext.put(QuarterDiscountStrategy.class.getName(),new QuarterDiscountStrategy());        
    }
    public void applyStrategy(OfferStrategy strategy){
        /* 
        Currently applyStrategy has simple implementation. You can use Context for populating some more information,
        which is required to call a particular operation            
        */
        System.out.println("Price before offer :"+price);
        double finalPrice = price - (price*strategy.getDiscountPercentage());
        System.out.println("Price after offer:"+finalPrice);
    }
    public OfferStrategy getStrategy(int monthNo){
        /*
            In absence of this Context method, client has to import relevant concrete Strategies everywhere.
            Context acts as single point of contact for the Client to get relevant Strategy
        */
        if ( monthNo < 6 )  {
            return strategyContext.get(NoDiscountStrategy.class.getName());
        }else{
            return strategyContext.get(QuarterDiscountStrategy.class.getName());
        }

    }
}
public class StrategyDemo{    
    public static void main(String args[]){
        StrategyContext context = new StrategyContext(100);
        System.out.println("Enter month number between 1 and 12");
        int month = Integer.parseInt(args[0]);
        System.out.println("Month ="+month);
        OfferStrategy strategy = context.getStrategy(month);
        context.applyStrategy(strategy);
    }

}

出力:

Enter month number between 1 and 12
Month =1
Price before offer :100.0
Price after offer:100.0

Enter month number between 1 and 12
Month =7
Price before offer :100.0
Price after offer:75.0

役立つ記事:

dzoneによる攻略パターン

ソースメイキングによる戦略パターン

于 2016-02-03T14:52:30.843 に答える
12

いくつかのかなり単純な例を考えることができます:

  • リストの並べ替え。戦略は、リスト内の 2 つの項目のどちらが「最初」であるかを決定するために使用される比較です。
  • 実行時にソート アルゴリズム自体 (QuickSort、HeapSort など) を選択できるアプリケーションがある場合があります。
  • Log4NetおよびLog4jのアペンダ、レイアウト、およびフィルタ
  • UI ツールキットのレイアウト マネージャー
  • データ圧縮。唯一のメソッドが次のような ICompressor インターフェイスを持つ場合があります。

    バイト[]圧縮(バイト[]入力);

    具体的な圧縮クラスは、RunLengthCompression、DeflateCompression などです。

于 2010-01-08T21:07:26.467 に答える
11

ストラテジー パターンの一般的な使用方法の 1 つは、カスタムの並べ替えストラテジーを定義することです (高階関数を持たない言語で)。たとえば、Java で文字列のリストを長さで並べ替え、匿名の内部クラス (ストラテジー インターフェイスの実装) を渡します。

List<String> names = Arrays.asList("Anne", "Joe", "Harry");
Collections.sort(names, new Comparator<String>() {
  public int compare(String o1, String o2) {
    return o1.length() - o2.length();
  }
});
Assert.assertEquals(Arrays.asList("Joe", "Anne", "Harry"), names);

同様の方法で、db4o などのオブジェクト データベースを使用したネイティブ クエリに戦略を使用できます。

List<Document> set = db.query(new Predicate<Document>() {
  public boolean match(Document candidate) {
    return candidate.getSource().contains(source);
  }
});
于 2010-01-11T16:12:32.850 に答える
10

エンタープライズ ディレクトリに対して毎日ユーザー ベースを同期するアプリケーションがあります。ユーザーは、大学でのステータスに基づいて、資格があるか資格がないかを判断します。プロビジョニング プログラムは毎日実行され、適格であると想定されているユーザーがアプリケーションでプロビジョニングされ、そうでないユーザーがプロビジョニング解除されることを確認します (実際には、グレースフル デグラデーション アルゴリズムに従っていますが、それは重要ではありません)。土曜日には、各ユーザーのいくつかのプロパティを同期し、ユーザーが適切な資格を持っていることを確認する、より徹底的な更新を行います。月末に、その月の使用量に基づいて請求処理を行います。

この同期を行うために、構成可能な戦略パターンを使用します。メイン プログラムは基本的に、曜日 (変更のみを同期/すべてを同期) とアカデミック カレンダーに対する学期の時間に応じてマスター戦略を選択します。請求サイクルが終了している場合は、請求戦略でそれを構成します。次に、標準インターフェースを介して選択された戦略を実行します。

これがどれほど一般的かはわかりませんが、戦略パターンにぴったりだと感じました。

于 2008-12-16T02:36:25.630 に答える
8

これは古い質問であることは承知していますが、最近実装した別の興味深い例があると思います。

これは、ドキュメント配信システムで使用されている戦略パターンの非常に実用的な例です。

多くのドキュメントといくつかのメタデータを含むアーカイブを受け取る PDF 配信システムがありました。メタデータに基づいて、ドキュメントを配置する場所を決定しました。たとえば、データに応じて、ドキュメントをAB、またはCストレージ システムに保存したり、3 つを組み合わせて保存したりできます。

さまざまな顧客がこのシステムを使用しており、エラーが発生した場合のさまざまなロールバック/エラー処理要件がありました。最初のエラーで配信システムを停止し、すべてのドキュメントが既にストレージに配信されたままにし、プロセスを停止し、それ以外は何も配信しないようにしたいと考えていました。 ; 別の人はB、 に保存するときにエラーが発生した場合にロールバックすることを望んでいましCたが、すでに に配信されたものはそのままにしておきAます。3 番目または 4 番目のものも異なるニーズを持つことは容易に想像できます。

この問題を解決するために、配信ロジックと、すべてのストレージからデータをロールバックするためのメソッドを含む基本的な配信クラスを作成しました。これらのメソッドは、エラーが発生した場合に配信システムによって実際に直接呼び出されることはありません。代わりに、クラスは依存性注入を使用して、(システムを使用している顧客に基づいて) 「ロールバック / エラー処理戦略」クラスを受け取ります。このクラスは、エラーの場合に呼び出され、その戦略に適切な場合はロールバック メソッドを呼び出します。

配信クラス自体は、何が起こっているか (どのドキュメントがどのストレージに配信され、どのような障害が発生したか) を戦略クラスに報告し、エラーが発生するたびに、続行するかどうかを戦略に問い合わせます。ストラテジーが「停止する」と言う場合、クラスはストラテジーの「cleanUp」メソッドを呼び出します。このメソッドは、以前に報告された情報を使用して、配信クラスから呼び出すロールバック メソッドを決定するか、単に何もしません。

rollbackStrategy.reportSuccessA(...);
rollbackStrategy.reportFailureB(...);

if (rollbackStrategy.mustAbort()) {
    rollbackStrategy.rollback(); // rollback whatever is needed based on reports
    return false;
}

したがって、現在、2 つの異なる戦略があります。1 つはQuitterStrategy(最初のエラーで終了し、何もクリーンアップしない) で、もう 1 つはMaximizeDeliveryToAStrategy(可能な限りプロセスを中止せず、ストレージAに配信されたものを決してロールバックしないようにします) ですが、へのB配信が失敗した場合からのものをロールバックしCます)。

私の理解では、これは戦略パターンの一例です。あなた (はい、読んでいます) が私が間違っていると思われる場合は、以下にコメントしてお知らせください。戦略パターンの「純粋な」使用を構成するものと、実装のどの側面が定義に違反しているかについて興味があります。戦略インターフェースが少し太っているので、少しおかしく見えると思います。これまでに見たすべての例では 1 つのメソッドしか使用していませんが、これはアルゴリズムをカプセル化していると思います (ビジネス ロジックの一部をアルゴリズムと見なすことができる場合は、アルゴリズムと見なすことができると思います)。

戦略は配信実行中のイベントについても通知されるため、 Observerと見なすこともできますが、それは別の話です。

少し調べてみると、これはAdvisorと呼ばれる「複合パターン」(MVC のような、特定の方法で複数の設計パターンを使用するパターン) のようです。これは、配信を続行するかどうかのアドバイザーですが、要求されたときにロールバックできるため、アクティブなエラー ハンドラーでもあります。

とにかく、これは非常に複雑な例であり、戦略パターンの使用法が単純すぎる/ばかげていると感じるかもしれません。他のパターンと一緒に使用すると、非常に複雑になり、さらに適用しやすくなります。

于 2012-10-05T05:57:20.640 に答える
6

戦略パターンは、特に検証とソート アルゴリズムに最もよく使用されるパターンです。

簡単な実際の例で説明しましょう

enum Speed {
  SLOW, MEDIUM, FAST;
}

class Sorter {
 public void sort(int[] input, Speed speed) {
    SortStrategy strategy = null;
    switch (speed) {
    case SLOW:
        strategy = new SlowBubbleSortStrategy();
        break;
    case MEDIUM:
        strategy = new MediumInsertationSortStrategy();
        break;

    case FAST:
        strategy = new FastQuickSortStrategy();
        break;
    default:
        strategy = new MediumInsertationSortStrategy();
    }
    strategy.sort(input);
 }

}

interface SortStrategy {

    public void sort(int[] input);
}

class SlowBubbleSortStrategy implements SortStrategy {

   public void sort(int[] input) {
    for (int i = 0; i < input.length; i++) {
        for (int j = i + 1; j < input.length; j++) {
            if (input[i] > input[j]) {
                int tmp = input[i];
                input[i] = input[j];
                input[j] = tmp;
            }
        }
    }
    System.out.println("Slow sorting is done and the result is :");
    for (int i : input) {
        System.out.print(i + ",");
    }
  }

 }

class MediumInsertationSortStrategy implements SortStrategy {

public void sort(int[] input) {
    for (int i = 0; i < input.length - 1; i++) {
        int k = i + 1;
        int nxtVal = input[k];
        while (input[k - 1] > nxtVal) {
            input[k] = input[k - 1];
            k--;
            if (k == 0)
                break;
        }
        input[k] = nxtVal;
    }
    System.out.println("Medium sorting is done and the result is :");
    for (int i : input) {
        System.out.print(i + ",");
    }

 }

}

class FastQuickSortStrategy implements SortStrategy {

public void sort(int[] input) {
    sort(input, 0, input.length-1);
    System.out.println("Fast sorting is done and the result is :");
    for (int i : input) {
        System.out.print(i + ",");
    }
}

private void sort(int[] input, int startIndx, int endIndx) {
    int endIndexOrig = endIndx;
    int startIndexOrig = startIndx;
    if( startIndx >= endIndx)
        return;
    int pavitVal = input[endIndx];
    while (startIndx <= endIndx) {
        while (input[startIndx] < pavitVal)
            startIndx++;
        while (input[endIndx] > pavitVal)
            endIndx--;
        if( startIndx <= endIndx){
            int tmp = input[startIndx];
            input[startIndx] = input[endIndx];
            input[endIndx] = tmp;
            startIndx++;
            endIndx--;
        }
    }
    sort(input, startIndexOrig, endIndx);
    sort(input, startIndx, endIndexOrig);
 }

}  

これのテストコードは

public class StrategyPattern {
  public static void main(String[] args) {
    Sorter sorter = new Sorter();
    int[] input = new int[] {7,1,23,22,22,11,0,21,1,2,334,45,6,11,2};
    System.out.print("Input is : ");
    for (int i : input) {
        System.out.print(i + ",");
    }
    System.out.println();
    sorter.sort(input, Speed.SLOW);
 }

}

同じ例がhttp://coder2design.com/strategy-pattern/から取られています

于 2015-07-08T11:38:23.497 に答える
3

「注文」のステータスが状態パターンではないことは確かですか? ステータスによって注文の扱いが変わることはないと思います。

たとえば、Ship on the Orderという方法を考えてみましょう。

order.Ship();
  • ステータスによって配送方法が異なる場合は、戦略パターンがあります。
  • ただし、Ship()メソッドが成功するのは、注文が支払われ、注文がまだ発送されていない場合のみです。これは状態パターンです。

私が見つけた状態パターン (およびその他のパタ​​ーン) の最も良い例は、驚くべき本「 Head First Design Patterns 」にありました。2 番目に近いのは、David Cumps のブログ シリーズのパターンです。

于 2009-01-22T23:07:35.900 に答える
2

良い例であるアプリケーションのかなり複雑なエンジンで戦略アプローチを使用しました。基本的に、エンジンの役割は、最初にウィジェットを持っている人のリストを見つけることでした。2 番目の役割は、不明な数のパラメーターに基づいて、ウィジェットを持っている最高の 10 人を特定することでした (以前の取引の価格距離など)。 、在庫量、配送オプションなどなど...)

基本的に私たちが行ったことは、問題を 2 つの戦略に分割することでした。1 つ目はデータの取得です。これは、ウィジェットのソースが複数あり、データを取得して共通の構造に変換できるようにする必要があることを知っていたからです。

その後、複数のアルゴリズムがあり、一部はパラメータの重み付けに基づいていて、他は非常に奇妙で適切なものであり、visios とチャートを引き出すことなくそれらを正当化することはできませんでした。最高の人を選ぶ。

私たちのサービス自体は、本質的に入力、出力を定義し、データの正規化を行うものでした。また、プロバイダーパターンを使用して、戦略を使用するアプリケーション固有のデータプロバイダーとアルゴリズムプロバイダーをプラグインしました。これはかなり効果的なシステムでした。

解決しなかった戦略またはテンプレート パターンを使用しているかどうかについて、いくつかの議論がありました。

于 2008-12-16T03:57:39.380 に答える
2

たとえば、2014 年 10 月の第 2 月曜日など、特定の月と年のn 番目の Xdayを計算するアルゴリズムを書きたいとします。android.text.format.Time日付を表すために Android の Time クラスを使用したいのですが、一般的なアルゴリズムも書きたいとします。にも当てはまりますjava.util.Calendar

これが私がしたことです。

DatetimeMath.java で:

public interface DatetimeMath { 
    public Object createDatetime(int year, int month, int day);

    public int getDayOfWeek(Object datetime);

    public void increment(Object datetime);
}

TimeMath.java では:

public class TimeMath implements DatetimeMath {
    @Override
    public Object createDatetime(int year, int month, int day) {
        Time t = new Time();
        t.set(day, month, year);
        t.normalize(false);
        return t;
    }

    @Override
    public int getDayOfWeek(Object o) {
        Time t = (Time)o;
        return t.weekDay;
    }   

    @Override
    public void increment(Object o) {
        Time t = (Time)o;
        t.set(t.monthDay + 1, t.month, t.year);
        t.normalize(false);
    }
}

OrdinalDayOfWeekCalculator.java で、汎用アルゴリズムを持つクラス:

public class OrdinalDayOfWeekCalculator {   
    private DatetimeMath datetimeMath;

    public OrdinalDayOfWeekCalculator(DatetimeMath m) {
        datetimeMath = m;
    }

    public Object getDate(int year, int month, int dayOfWeek, int ordinal) {
        Object datetime = datetimeMath.createDatetime(year, month, 1);
        if (datetimeMath.getDayOfWeek(datetime) == dayOfWeek) {
            return datetime;
        } 
        int xDayCount = 0;
        while (xDayCount != ordinal) {
            datetimeMath.increment(datetime);
            if (datetimeMath.getDayOfWeek(datetime) == dayOfWeek) {
                xDayCount++;
            }
        }
        return datetime;
    }
}

私のAndroidアプリでは、次のように呼び出します

OrdinalDayOfWeekCalculator odowc = 
        new OrdinalDayOfWeekCalculator(new TimeMath());
Time canadianThanksgiving = (Time)odowc.getDate(
        year, Calendar.OCTOBER, Time.MONDAY, 2);

で同じアルゴリズムを再利用したい場合はjava.util.Calendar、DatetimeMath の 3 つのメソッドを実装するクラス CalendarMath を作成し、次を使用します。

OrdinalDayOfWeekCalculator odowc2 = 
        new OrdinalDayOfWeekCalculator(new CalendarMath());
Calendar canadianThanksgivingCal = (Calendar)odowc2.getDate(
        year, Calendar.OCTOBER, Calendar.MONDAY, 2);
于 2014-01-18T19:27:08.403 に答える
1

非常に複雑なデータベースを使用するエンタープライズプラットフォーム用に、サードパーティのプロビジョニングインターフェイスを作成する必要がありました。プロビジョニングされるデータの送信は、依存関係のために正しい順序でデータベースに書き込むことができるように、アプリケーションの優先キューに入れられたデータ型のリストとして行われました。

そのデータを書き込むプロセスは非常に単純で、優先キューの先頭から飛び出し続け、抽出するオブジェクトのタイプに基づいて戦略を選択します。

于 2009-01-29T02:06:50.783 に答える
0

ウィキペディアより

コンピューター プログラミングでは、戦略パターン (ポリシー パターンとも呼ばれます) は、実行時にアルゴリズムを選択できる動作ソフトウェア設計パターンです。単一のアルゴリズムを直接実装する代わりに、コードは、アルゴリズムのファミリのどれを使用するかについて実行時の指示を受け取ります。

Windows ペイント アプリケーションでは、さまざまなセクションで形状と色を個別に選択できる戦略パターンを確認できます。ここで、形状と色は、実行時に変更できるアルゴリズムです。

「RedCircle」のオプションを提供するのではなく、赤い色で円を描きたい場合は、円と選択した色を選択できます。

Shape redCircle = new RedCircle(); // Without stretegy Pattern
Shaped redCircle = new Shape("red","circle"); // With Strategy pattern

戦略パターンがなければ、形と色のデカルト積でクラスの数が増えます。また、実装ごとにインターフェイスが変わります。

于 2018-06-08T05:30:36.727 に答える