战略模式的现实世界范例

时间:2008-12-16 01:29:30

标签: java c# design-patterns strategy-pattern

我一直在阅读OCP principal以及如何使用策略模式来实现这一目标。

我打算尝试向几个人解释这一点,但我能想到的唯一例子是根据“订单”的状态使用不同的验证类。

我在线阅读了几篇文章,但这些文章通常没有描述使用该策略的真实原因,例如生成报告/账单/验证等......

您认为策略模式很常见吗?

19 个答案:

答案 0 :(得分:90)

这个怎么样:

您必须加密文件。

对于小文件,您可以使用“内存”策略,其中读取完整文件并保存在内存中(假设文件大小为<1 gb)

对于大型文件,您可以使用其他策略,其中部分文件在内存中读取,部分加密结果存储在tmp文件中。

对于同一任务,这可能是两种不同的策略。

客户端代码看起来一样:

 File file = getFile();
 Cipher c = CipherFactory.getCipher( file.size() );
 c.performAction();



// implementations:
interface  Cipher  {
     public void performAction();
}

class InMemoryCipherStrategy implements Cipher { 
         public void performAction() {
             // load in byte[] ....
         }
}

class SwaptToDiskCipher implements Cipher { 
         public void performAction() {
             // swapt partial results to file.
         }

}

     Cipher c = CipherFactory.getCipher( file.size() );

将为密码返回正确的策略实例。

我希望这会有所帮助。

(我甚至不知道Cipher是否是正确的词:P)

答案 1 :(得分:49)

同样,一个旧帖子但仍然出现在搜索中,所以我将再添加两个例子(代码在C#中)。我非常喜欢策略模式,因为当项目经理说:“我们希望应用程序执行'X',但'X'尚不清楚,它可以在不久的将来改变时,它已经节省了很多次。 “ 这个video explaining the strategy pattern以星际争霸为例。

属于此类别的东西:

  • 排序:我们想对这些数字进行排序,但我们不知道是否要使用BrickSort,BubbleSort或其他一些排序

  • 验证:我们需要根据“一些规则”检查项目,但目前尚不清楚该规则是什么,我们可能会想到新规则。

  • 游戏:我们希望玩家在移动时走路或跑步,但也许在未来,他还应该能够游泳,飞行,传送,挖洞等等。

  • 存储信息:我们希望应用程序将信息存储到数据库,但稍后可能需要能够保存文件或制作网络连接

  • 输出:我们需要输出X作为普通字符串,但后来可能是CSV,XML,JSON等。


<强>实施例

我有一个项目,用户可以将项目分配给数据库中的人员。将产品分配给某人的状态为“已批准”或“已拒绝”,这取决于某些业务规则。例如:如果用户将产品分配给具有特定年龄的人,则应拒绝其状态;如果项目中两个字段之间的差异大于50,则状态会被拒绝等等。

现在,在开发的那一刻,这些业务规则尚未完全清楚,新规则可能随时出现。 stragety-pattern的强大之处在于我制作了一个RuleAgent,它给出了一个IRules列表。

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接口的新规则类,并在需要时将其切换出来。


另一个很好的例子:Scott Allen在http://www.asp.net/mvc/pluralsight的视频系列,他在应用程序的单元测试部分使用策略模式

他建立了一个网站,其中有一个页面根据受欢迎程度显示项目。然而,“热门”可以是很多东西(大多数观点,大多数订阅者,创建日期,大多数活动,最少量的评论等),并且如果管理层还不知道如何订购,并且可能想要尝试不同的以后的订单。使用order方法创建接口(IOrderAlgorithm或其他),并让Orderer-object将排序委托给IOrderAlgorithm接口的具体实现。你可以创建一个“CommentOrderer”,“ActivityOrderer”等......当新的需求出现时,只需将它们切换出来。

答案 2 :(得分:11)

我可以想到几个相当简单的例子:

  • 排序列表。该策略是用于确定列表中两个项目中的哪一个是“第一”的比较
  • 您可能有一个应用程序可以在运行时选择排序算法本身(QuickSort,HeapSort等)
  • Log4NetLog4j
  • 中的附加工具,布局和过滤器 UI工具包中的
  • Layout Managers
  • 数据压缩。您可能有一个ICompressor接口,其唯一方法如下所示:

    byte [] compress(byte [] input);

    您的具体压缩类可能是RunLengthCompression,DeflateCompression等。

答案 3 :(得分:10)

主要提示:

  1. 策略 是行为设计​​模式。它用于在算法族之间切换。

  2. 此模式包含一个抽象策略 interface 和该接口的许多具体策略实现(算法)。

  3. 该应用程序仅使用策略 interface 。根据某些配置参数,具体策略将标记为 interface

  4. 来自wikipedia

    的UML图表

    enter image description here

    一个真实的例子:航空公司在几个月(7月至12月)提供折扣。您可以拥有一个票价模块,该模块根据月份数量决定定价选项。

    看一个简单的例子。此示例可以扩展到在线零售应用程序,它可以在特殊日子/欢乐时光轻松地为购物车项目提供折扣。

    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

    strategy模式

    源代码制作

    strategy模式

答案 4 :(得分:9)

策略模式的一个常见用法是定义自定义排序策略(在没有高阶函数的语言中),例如,在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);
  }
});

答案 5 :(得分:8)

我有一个应用程序,它每天将它的用户群与我们的企业目录同步。根据用户在大学中的状态,用户符合资格或不符合资格。配置程序每天都会通过并确保应用程序中的那些应用程序被配置,而未被配置的那些(实际上根据优雅的降级算法,但这不是重点)。星期六我做了一个更彻底的更新,同步每个用户的一些属性,并确保他们有适当的资格。在月底,我会根据当月的使用情况进行一些退款处理。

我使用可组合策略模式来执行此同步。主程序基本上选择主策略,取决于星期几(仅同步变化/同步所有)和学期相对于学术日历的时间。如果结算周期结束,那么它还会使用结算策略进行组合。然后,它通过标准接口运行所选策略。

我不知道这有多常见,但我觉得它非常适合策略模式。

答案 6 :(得分:7)

我知道这是一个老问题,但我想我最近实施了另一个有趣的例子。

这是文档传递系统中使用的策略模式的一个非常实用的示例。

我有一个PDF传送系统,它收到一个包含大量文档和一些元数据的存档。根据元数据,它决定将文档放在哪里;比方说,根据数据的不同,我可以将文档存储在ABC存储系统中,或者混合使用三种文件。

不同的客户使用此系统,并且在出现错误时他们有不同的回滚/错误处理要求:一个人希望交付系统在第一个错误时停止,将所有文档保留在他们的存储中,但是停止进程而不是提供别的东西;另一个人希望它在B中存储时出现错误时从C回滚,但是将已经发送的内容留给A。很容易想象第三或第四个也会有不同的需求。

为了解决这个问题,我创建了一个包含交付逻辑的基本交付类,以及从所有存储中回滚内容的方法。在出现错误的情况下,交付系统实际上不会调用这些方法。相反,该类使用依赖注入来接收“回滚/错误处理策略”类(基于使用系统的客户),在发生错误时调用该类,如果适合该策略,则会调用回滚方法。

交付类本身会报告战略类的内容(向哪些存储区提供了哪些文档,以及发生了哪些故障),并且每当发生错误时,它都会询问策略是否继续。如果策略说“停止它”,则该类调用策略的“cleanUp”方法,该方法使用先前报告的信息来决定从交付类调用哪些回滚方法,或者什么都不做。

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

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

所以我现在有两个不同的策略:一个是QuitterStrategy(退出第一个错误并且没有清空),另一个是MaximizeDeliveryToAStrategy(尽可能不尝试中止流程并永远不会回滚传递到存储的内容A,但如果传递到B失败,则从C回滚内容。

根据我的理解,这是战略模式的一个例子。如果您(是的,您正在阅读)认为我错了,请在下面评论并告诉我。我很好奇什么会构成策略模式的“纯粹”使用,以及我的实现的哪些方面违反了定义。我觉得它看起来有点滑稽,因为战略界面有点胖。到目前为止我看到的所有示例只使用了一种方法,但我仍然认为这封装了一种算法(如果一块业务逻辑可以被认为是一种算法,我认为它就是这样)。

由于策略在交付执行期间也会收到有关事件的通知,因此也可以将其视为Observer,但这是另一个故事。

通过做一些研究,似乎这是一个“复合模式”(如MVC,一种在特定方式下使用多种设计模式的模式),称为Advisor。它是关于交付是否应该继续的顾问,但它也是一个主动错误处理程序,因为它可以在被要求时回滚内容。

无论如何,这是一个非常复杂的例子,可能会让你觉得战略模式的用法过于简单/愚蠢。当与其他模式一起使用时,它可能非常复杂,甚至更适用。

答案 7 :(得分: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/

答案 8 :(得分:4)

战略模式的一个很好的例子就是在一个我们可以拥有不同角色的游戏中,每个角色可以拥有多种武器进行攻击,但一次只能使用一种武器。因此我们将角色作为背景,例如King,Commander,Knight,Soldier  和武器作为一种策略,其中attack()可能是取决于所使用武器的方法/算法。因此,如果具体武器类是Sword,Axe,Crossbow,BowAndArrow等,他们都将实施attack()方法。我相信不需要进一步的解释。

答案 9 :(得分:2)

您确定“订单”的状态不是州模式吗?我预感到订单的处理状态不会有所不同。

以订单

上的方法 Ship 为例
order.Ship();
  • 如果运送方式不同 它的地位的功能,然后你 得到了战略模式。
  • 但是 Ship()方法仅成功 订单付款时,和 订单尚未发货, 你有一个州模式。

我发现的状态模式(和其他模式)的最好例子是“Head First Design Patterns”一书,这很神奇。 紧随其后的是David Cumps' blogging series of patterns

答案 10 :(得分:2)

假设您要编写一个算法来计算给定月份和年份的 nth Xday ,例如2014年10月的第二个星期一。您想要使用Android的时间等级{{1} }表示日期,但您也想编写一个也适用于android.text.format.Time的通用算法。

这就是我所做的。

在DatetimeMath.java中:

java.util.Calendar

在TimeMath.java中:

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

    public int getDayOfWeek(Object datetime);

    public void increment(Object datetime);
}

在OrdinalDayOfWeekCalculator.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);
    }
}

在我的Android应用中,我会打电话给

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

如果我想为OrdinalDayOfWeekCalculator odowc = new OrdinalDayOfWeekCalculator(new TimeMath()); Time canadianThanksgiving = (Time)odowc.getDate( year, Calendar.OCTOBER, Time.MONDAY, 2); 重用相同的算法,我只想编写一个CalendarMath类来实现DatetimeMath中的三个方法,然后使用

java.util.Calendar

答案 11 :(得分:1)

我们必须为具有非常复杂的数据库的企业平台创建第三方配置界面。要提供的数据提交是我们的数据类型列表,这些数据类型被放入我们的应用程序的优先级队列中,因此可以根据依赖性以正确的顺序将它们写入数据库。

编写该数据的过程非常简单,不断弹出优先级队列的顶部,然后根据您提取的对象类型选择策略。

答案 12 :(得分:1)

几周前,我添加了一个由我们的域对象实现的通用Java接口。此域对象是从数据库加载的,数据库表示是一个包含大约10个以上分支的星型模式。拥有如此重量级域名对象的后果之一是我们必须制作代表相同架构的其他域对象,尽管重量较轻。所以我让其他轻量级对象实现了相同的接口。换句话说,我们有:

public interface CollectibleElephant { 
    long getId();
    String getName();
    long getTagId();
}

public class Elephant implements CollectibleElephant { ... }
public class BabyElephant implements CollectibleElephant { ... }

最初,我想使用CollectibleElephantElephant进行排序。很快,我的队友们匆匆走上CollectibleElephant进行安全检查,在发送到GUI时对其进行过滤等等。

答案 13 :(得分:1)

我在一个应用程序中相当复杂的引擎中使用了策略方法,这是一个很好的例子。本质上,引擎的作用是首先找到一个拥有小部件的人的列表,它的第二个角色是根据未知数量的参数(比如价格距离以前的业务一起)找出10个最好的人。 ,安装库存,运输选项等等...)

基本上我们所做的是将问题分解为两个策略,第一个是数据检索,因为我们知道我们有多个小部件源,我们需要能够获取数据并将其转换为通用结构。

然后我们也意识到我们有多个算法,其中一些基于加权参数,其他算法非常奇怪和有利于我,如果不拉出粘性物品和图表我就无法做到正义,而且你得到了图片,我们有很多用于选择最佳人选的算法。

我们的服务本身就是它本质上定义了输入,输出并对数据进行了一些规范化,它还使用提供者模式来插入使用该策略的特定于应用程序的数据提供者和算法提供者。这是一个相当有效的系统。

如果我们使用的策略或模板模式我们从未解决过,我们会进行一些辩论。

答案 14 :(得分:1)

public class StrategyDemo {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();

        Item item1 = new Item("1234", 10);
        Item item2 = new Item("5678", 40);

        cart.addItem(item1);
        cart.addItem(item2);

        // pay by paypal
        cart.pay(new PaypalStrategy("myemail@example.com", "mypwd"));

        // pay by credit card
        cart.pay(new CreditCardStrategy("Pankaj Kumar", "1234567890123456", "786", "12/15"));
    }
}

interface PaymentStrategy {
    public void pay(int amount);
}

class CreditCardStrategy implements PaymentStrategy {

    private String name;
    private String cardNumber;
    private String cvv;
    private String dateOfExpiry;

    public CreditCardStrategy(String nm, String ccNum, String cvv, String expiryDate) {
        this.name = nm;
        this.cardNumber = ccNum;
        this.cvv = cvv;
        this.dateOfExpiry = expiryDate;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid with credit/debit card");
    }

}

class PaypalStrategy implements PaymentStrategy {

    private String emailId;
    private String password;

    public PaypalStrategy(String email, String pwd) {
        this.emailId = email;
        this.password = pwd;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid using Paypal.");
    }

}

class Item {

    private String upcCode;
    private int price;

    public Item(String upc, int cost) {
        this.upcCode = upc;
        this.price = cost;
    }

    public String getUpcCode() {
        return upcCode;
    }

    public int getPrice() {
        return price;
    }

}

class ShoppingCart {

    // List of items
    List<Item> items;

    public ShoppingCart() {
        this.items = new ArrayList<Item>();
    }

    public void addItem(Item item) {
        this.items.add(item);
    }

    public void removeItem(Item item) {
        this.items.remove(item);
    }

    public int calculateTotal() {
        int sum = 0;
        for (Item item : items) {
            sum += item.getPrice();
        }
        return sum;
    }

    public void pay(PaymentStrategy paymentMethod) {
        int amount = calculateTotal();
        paymentMethod.pay(amount);
    }
}

答案 15 :(得分:0)

来自维基百科

  

在计算机编程中,策略模式(也称为策略模式)是一种行为软件设计模式,可以在运行时选择算法。代码不是直接实现单个算法,而是接收关于在一系列算法中使用哪个算法的运行时指令

在Windows Paint应用程序中,您可以看到一种策略模式,您可以在其中在不同的部分中单独选择形状和颜色。这里的形状和颜色是可以在运行时更改的算法。

如果你想画一个红色的圆圈,而不是提供一个RedCircle&#39;他们让你选择圆圈和你选择的颜色。

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

没有策略模式会增加具有形状和颜色的笛卡尔积的类的数量。此外,每个实现的界面也会发生变化。

答案 16 :(得分:0)

想象一下一个与AI敌人进行的射击游戏。 您希望他们根据发生的事情以不同的方式持续战斗。 使用策略模式,您可以不断循环并动态更改执行特定操作或操作的方式。

interface FightingStategy{
    public void fight();
}
public Defense implements FightingStrategy{
    public void figth(){
        ... hide behind wall to shoot
    }
}
public Berserker implements FightingStrategy{
    public void fight(){
        ... run towards you, headrolls and shoots
    }
}
public Dead implements FightingStrategy{
    public void fight(){
        ... is dead, doesn't move
    }
}

public AiShooter{

    FightingStrategy fightingStrategy;

    public AiShooter(){
        fightStrategy = new Berserker();
    }

    public void fight(){
        this.fightingStrategy.fight();
    }

    public void changeStrategy(FightingStrategy f){
        this.fightingStrategy = f;
    }
}

public static void main(){

    ... create list of AiShooters...
    while (condition){
        list.forEach(shooter -> shooter.fight());
    }
    ... you shoot back
    list.ForEach(shooter -> shooter.changeStrategy(new 
Defense()));

    ... you kill one
    list.get(n).changeStrategy(new Dead());
}

答案 17 :(得分:0)

计算商品 GST 税总额的示例

public interface TaxCalculation {

    public Double calculateTax(Double price);  
}
public class FivePercentage implements TaxCalculation {

    @Override
    public Double calculateTax(Double price) {
        
        Double dbl = (price*5)/100;
        return dbl;
    }

}
public class EighteenPercentage implements TaxCalculation {

    @Override
    public Double calculateTax(Double price) {
        Double dbl = (price*18)/100;
        return dbl;
    }

}
public class Item {

    public String name;
    public Double price;
    public int taxRate;
    public Double totalTax;
    
    public Item(String name, Double price, int taxRate) {
        super();
        this.name = name;
        this.price = price;
        this.taxRate = taxRate;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Double getPrice() {
        return price;
    }
    public void setPrice(Double price) {
        this.price = price;
    }
    public int getTaxRate() {
        return taxRate;
    }

    public void setTaxRate(int taxRate) {
        this.taxRate = taxRate;
    }

    public Double getTotalTax() {
        return totalTax;
    }

    public void setTotalTax(Double totalTax) {
        this.totalTax = totalTax;
    }

    public void calculateTax(TaxCalculation taxcalulation, Double price) {
        this.totalTax = taxcalulation.calculateTax(price);
    }
    
    @Override
    public String toString() {
        return "Items [name=" + name + ", price=" + price + ", taxRate=" + taxRate + ", totalTax=" + totalTax + "]";
    }
    
}
public class CalculateTax {

    public static void main(String[] args) {
        
        List<Item> itemList = new ArrayList<>();
        
        Item item1 = new Item("Engine Oil", 320.0, 5);
        Item item2 = new Item("Painting", 3500.00, 18);
        
        itemList.add(item1);
        itemList.add(item2);
        
        itemList.stream().forEach(x-> {
            if(x.getTaxRate() == 5) {
                x.calculateTax(new FivePercentage(), x.getPrice());
            } else if(x.getTaxRate() == 18) {
                x.calculateTax(new EighteenPercentage(), x.getPrice());
            }
        });
        
        itemList.stream().forEach(x-> {
            System.out.println(x.toString());
        });
    }
}

答案 18 :(得分:0)

此答案适用于初学者并希望通过最简单的示例来理解这一点的人 - (参考:Headfirst Design Patterns)

#include<iostream>
using namespace std;

/*
    Where it is applicable?
    The selection of an algorithm is required from a family of algorithms.
    To avoid multiple conditional statements for selection of algorithms
    and to hide its algorithm data structures and complexity from client.
*/

class Fly {
public:
    virtual void fly() = 0;
};

//concrete Fly : rocketFly
class rocketFly : public Fly {
public:
    void fly() {
        cout <<"rocketFly::fly()" << endl;
    }
};

//concrete Fly : normalFly
class normalFly : public Fly {
public:
    void fly() {
        cout <<"normalFly::fly()" << endl;
    }
};

//Duck "HAS A" relationship with Fly
class Duck {
private:
    //Duck has a Fly behavour
    Fly* flyObj;
public:
    Duck(Fly* obj) : flyObj(obj) {
        
    }

    void DuckFly() {
        flyObj->fly();
    }
};

int main() {
    rocketFly* rObj = new rocketFly;
    Duck wildDuck(rObj);
    wildDuck.DuckFly();

    normalFly* nObj = new normalFly;
    Duck cityDuck(nObj);
    cityDuck.DuckFly();

    /*
        I didn't have to create classes like wildDuck which inherits from Duck and they implement their own
        fly, quack etc behaviour. There will be code duplication.
        So, instead of that, create an interface of fly, make concrete fly classes with different
        fly behaviour. Use objects to any of these concrete classes to inject to one generic duck
        class which will automatically call correct fly behaviour.
    */

return 0;
}