サーチ…


隠れ戦略実装の詳細

オブジェクト指向設計における非常に一般的なガイドラインは、「可能な限り少ないが必要なだけ」である。これは戦略パターンにも当てはまります。実際に戦略を実装するクラスなど、実装の詳細を非表示にすることをお勧めします。

外部パラメータに依存しない単純な戦略の場合、最も一般的なアプローチは、実装するクラスそのものをプライベート(ネストされたクラス)またはパッケージプライベートにし、パブリッククラスの静的フィールドを通してインスタンスを公開することです。

public class Animal {

  private static class AgeComparator implements Comparator<Animal> {
    public int compare(Animal a, Animal b) {
      return a.age() - b.age();
    }
  }

  // Note that this field has the generic type Comparator<Animal>, *not*
  // Animal.AgeComparator!
  public static final Comparator<Animal> AGE_COMPARATOR = new AgeComparator();

  private final int age;

  Animal(int age) {
    this.age = age;
  }

  public int age() {
    return age;
  }

}

List<Animal> myList = new LinkedList<>();
myList.add(new Animal(10));
myList.add(new Animal(5));
myList.add(new Animal(7));
myList.add(new Animal(9));

Collections.sort(
  myList,
  Animal.AGE_COMPARATOR
);

パブリックフィールドAnimal.AGE_COMPARATORは、 Collections.sortなどのメソッドで使用できるストラテジを定義しますが、実装クラスについても何も知る必要はありません。

ご希望の場合は、匿名クラスを使用することができます:

public class Animal {

  public static final Comparator<Animal> AGE_COMPARATOR = new Comparator<Animal> {
    public int compare(Animal a, Animal b) {
      return a.age() - b.age();
    }
  };

  // other members...
}

戦略がもう少し複雑でパラメータが必要な場合は、 Collections.reverseOrder(Comparator<T>)などの静的ファクトリメソッドを使用するのが一般的です。メソッドの戻り値の型は実装の詳細を公開すべきではありません。たとえば、 reverseOrder()は次のように実装されます。

public static <T> Comparator<T> reverseOrder(Comparator<T> cmp) {
  // (Irrelevant lines left out.)
  return new ReverseComparator2<>(cmp);
}

Contextクラスを使用したjavaの戦略パターン例

戦略:

Strategyは行動パターンであり、関連するアルゴリズムのファミリーからアルゴリズムを動的に変更することができます。

ウィキペディアの戦略パターンのUML

ここに画像の説明を入力

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 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

問題の声明:7月から12月の月間に商品の価格を25%割引で提供します。 1月〜6月の月間は割引を提供しないでください。

上の例は、 Context使ったStrategyパターンの使い方を示しています。 Contextは、 Client単一接点として使用できます。

2つの戦略 - NoOfferStrategyQuarterDiscountStrategyが問題文ごとに宣言されています。

出力欄に示すように、入力した月に応じて割引が適用されます

戦略パターンのユースケース

  1. 交換可能なアルゴリズムファミリを持ち、実行時にアルゴリズムを変更する必要がある場合は、このパターンを使用します。

  2. 条件文を削除してコードをきれいにする

コンテキストクラスなしの戦略パターン/ Java

以下は、コンテキストクラスなしで戦略パターンを使用する簡単な例です。インターフェイスを実装し、同じ問題をさまざまな方法で解決する2つの実装方法があります。 EnglishTranslationクラスのユーザーは、目的の戦略を指定することによって、translateメソッドを呼び出して、翻訳に使用する戦略を選択することができます。

// The strategy interface
public interface TranslationStrategy {
    String translate(String phrase);
}

// American strategy implementation 
public class AmericanTranslationStrategy implements TranslationStrategy {

    @Override
    public String translate(String phrase) {
        return phrase + ", bro";
    }
}

// Australian strategy implementation     
public class AustralianTranslationStrategy implements TranslationStrategy {

    @Override
    public String translate(String phrase) {
        return phrase + ", mate";
    }
}

// The main class which exposes a translate method
public class EnglishTranslation {

    //  translate a phrase using a given strategy
    public static String translate(String phrase, TranslationStrategy strategy) {
        return strategy.translate(phrase);
    }

    // example usage
    public static void main(String[] args) {

        // translate a phrase using the AustralianTranslationStrategy class
        String aussieHello = translate("Hello", new AustralianTranslationStrategy());
        // Hello, mate

        // translate a phrase using the AmericanTranslationStrategy class    
        String usaHello = translate("Hello", new AmericanTranslationStrategy());
        // Hello, bro
    }
}

Strategyパターンを実装するためのJava 8機能インタフェースの使用

この例の目的は、Java 8機能インタフェースを使用してStrategyパターンを実現する方法を示すことです。従来のJavaのシンプルなユースケースコードから始めて、Java 8の方法でコードを再コーディングします。

私たちが使用している問題の例は、距離を超えて通信するさまざまな方法を記述するアルゴリズム(戦略)のファミリーです。

クラシックJavaバージョン

私たちのアルゴリズムファミリーの契約は、次のインターフェイスで定義されています。

public interface CommunicateInterface {
    public String communicate(String destination);
}

次に、以下のようにいくつかのアルゴリズムを実装できます。

public class CommunicateViaPhone implements CommunicateInterface {
    @Override
    public String communicate(String destination) {
        return "communicating " + destination +" via Phone..";
    }
}

public class CommunicateViaEmail implements CommunicateInterface {
    @Override
    public String communicate(String destination) {
        return "communicating " + destination + " via Email..";
    }
}

public class CommunicateViaVideo implements CommunicateInterface {
    @Override
    public String communicate(String destination) {
        return "communicating " + destination + " via Video..";
    }
}

これらは次のようにインスタンス化できます。

CommunicateViaPhone communicateViaPhone = new CommunicateViaPhone();
CommunicateViaEmail communicateViaEmail = new CommunicateViaEmail();
CommunicateViaVideo communicateViaVideo = new CommunicateViaVideo();

次に、戦略を使用するサービスを実装します。

public class CommunicationService {
    private CommunicateInterface communcationMeans;

    public void setCommuncationMeans(CommunicateInterface communcationMeans) {
        this.communcationMeans = communcationMeans;
    }

    public void communicate(String destination) {
        this.communcationMeans.communicate(destination);
    }
}

最後に、次のようなさまざまな戦略を使用できます。

CommunicationService communicationService = new CommunicationService();

// via phone
communicationService.setCommuncationMeans(communicateViaPhone);
communicationService.communicate("1234567");

// via email
communicationService.setCommuncationMeans(communicateViaEmail);
communicationService.communicate("[email protected]");

Java 8機能インタフェースの使用

異なるアルゴリズム実装の契約は、専用のインタフェースを必要としません。代わりに、既存のjava.util.function.Function<T, R>インタフェースを使用して記述することができます。

アルゴリズムthe family of algorithms構成the family of algorithms異なるアルゴリズムは、ラムダ式として表すことができる。これは、戦略クラスそのインスタンス化を置き換えます。

Function<String, String> communicateViaEmail = 
        destination -> "communicating " + destination + " via Email..";
Function<String, String> communicateViaPhone = 
        destination -> "communicating " + destination + " via Phone..";
Function<String, String> communicateViaVideo = 
        destination -> "communicating " + destination + " via Video..";

次に、「サービス」を次のようにコーディングすることができます。

public class CommunicationService {
    private Function<String, String> communcationMeans;

    public void setCommuncationMeans(Function<String, String> communcationMeans) {
        this.communcationMeans = communcationMeans;
    }

    public void communicate(String destination) {
        this.communcationMeans.communicate(destination);
    }
}

最後に、以下のような戦略を使用します

CommunicationService communicationService = new CommunicationService();

// via phone
communicationService.setCommuncationMeans(communicateViaPhone);
communicationService.communicate("1234567");

// via email
communicationService.setCommuncationMeans(communicateViaEmail);
communicationService.communicate("[email protected]");

あるいは:

communicationService.setCommuncationMeans(
    destination -> "communicating " + destination + " via Smoke signals.." );
CommunicationService.communicate("anyone");

戦略(PHP)

www.phptherightway.comの

<?php

interface OutputInterface
{
    public function load();
}

class SerializedArrayOutput implements OutputInterface
{
    public function load()
    {
        return serialize($arrayOfData);
    }
}

class JsonStringOutput implements OutputInterface
{
    public function load()
    {
        return json_encode($arrayOfData);
    }
}

class ArrayOutput implements OutputInterface
{
    public function load()
    {
        return $arrayOfData;
    }
}


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow