105

我一直在阅读有关OCP 原则以及如何使用策略模式来实现这一点的内容。

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

我在网上阅读了几篇文章,但这些文章通常不会描述使用该策略的真正相似原因,例如生成报告/账单/验证等...

有没有你认为策略模式很常见的真实例子?

4

19 回答 19

110

那这个呢:

您必须加密文件。

对于小文件,您可以使用“内存中”策略,将完整文件读取并保存在内存中(假设文件 < 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)

于 2008-12-16T01:40:38.113 回答
73

同样,一个旧帖子,但仍然出现在搜索中,所以我将添加另外两个示例(代码在 C# 中)。我非常喜欢策略模式,因为当项目经理说:“我们希望应用程序做‘X’,但‘X’还不清楚,它可能在不久的将来发生变化时,它救了我很多次。 " 该视频解释了策略模式,以星际争霸为例。

属于这一类的东西:

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

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

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

  • 存储信息:我们希望应用程序将信息存储到数据库中,但稍后它可能需要能够保存文件或进行网络通话

  • 输出:我们需要将 X 输出为纯字符串,但稍后可能是 CSV、XML、JSON 等。


例子

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

现在,在开发的那一刻,这些业务规则还没有完全清楚,新的规则随时可能出现。策略模式的强大之处在于我制作了一个 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 对象将排序委托给 IOrderAlgorithm 接口的具体实现。您可以创建一个“CommentOrderer”、“ActivityOrderer”等......当新的需求出现时,只需将它们切换出来。

于 2014-01-16T14:57:09.307 回答
23

关键说明:

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

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

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

来自维基百科的 UML 图

在此处输入图像描述

一个真实的例子:航空公司在某些月份(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的策略模式

采购策略模式

于 2016-02-03T14:52:30.843 回答
12

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

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

    字节[] 压缩(字节[] 输入);

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

于 2010-01-08T21:07:26.467 回答
11

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

策略模式的一个很好的例子是在一个游戏中,我们可以有不同的角色,每个角色可以有多种武器进行攻击,但一次只能使用一种武器。所以我们将角色作为上下文,例如国王、指挥官、骑士、士兵和武器作为策略,其中攻击()可以是取决于所使用武器的方法/算法。因此,如果具体的武器类是 Sword、Axe、Crossbow、BowAndArrow 等......它们都将实现 attack() 方法。我确信不需要进一步的解释。

于 2015-01-15T16:08:20.790 回答
8

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

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

我有一个 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,但这是另一回事。

通过做一些研究,这似乎是一种称为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

您确定“订单”的状态不是状态模式吗?我有一种预感,订单的处理方式不会根据其状态而有所不同。

以按订单发货的方法为例:

order.Ship();
  • 如果运输方式的功能因状态而异,那么您就有了策略模式。
  • 但是,如果Ship()方法仅在订单已付款且订单尚未发货时才成功,那么您就有了一个状态模式。

我发现的状态模式(和其他模式)的最佳示例是在“ Head First Design Patterns ”一书中,这太棒了。紧随其后的是David Cumps 的博客系列模式

于 2009-01-22T23:07:35.900 回答
3
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);
    }
}
于 2017-05-23T04:38:06.273 回答
2

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

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

然后我们还意识到我们有多种算法,有些是基于参数加权的,有些是非常奇怪和固有的,如果不拉出 visios 和图表,我就无法做到公正,你明白了,我们有很多算法选择最优秀的人。

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

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

于 2008-12-16T03:57:39.380 回答
2

假设您想编写一个算法来计算给定月份和年份的第 n 个 Xday,例如 2014 年 10 月的第二个星期一。您想使用 Android 的 Time 类android.text.format.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,我只需编写一个类 CalendarMath 实现 DatetimeMath 中的三个方法,然后使用

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

几周前,我添加了一个由我们的一个域对象实现的通用 Java 接口。这个域对象是从数据库中加载的,数据库表示是一个带有大约 10 多个分支的星型模式。拥有如此重量级的域对象的后果之一是,我们不得不制作代表相同模式的其他域对象,尽管重量级较轻。所以我让其他轻量级对象实现了相同的接口。否则我们有:

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

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

最初,我想使用CollectibleElephantto sort Elephants。很快,我的队友就CollectibleElephant开始运行安全检查,在它们被发送到 GUI 时对其进行过滤,等等。

于 2008-12-16T05:06:26.607 回答
1

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

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

于 2009-01-29T02:06:50.783 回答
1

计算项目 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());
        });
    }
}
于 2021-06-14T10:28:21.190 回答
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;
}
于 2021-06-16T07:11:22.820 回答
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());
}
于 2020-07-31T02:37:34.257 回答
0

来自维基百科

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

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

如果您想用红色绘制一个圆圈,而不是提供“RedCircle”选项,他们让您选择圆圈和您选择的颜色。

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

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

于 2018-06-08T05:30:36.727 回答