战略格局的现实世界范例

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

我将试着向几个人解释这个问题,但我能想到的唯一例子是根据“订单”的状态使用不同的validation类。

我已经在线阅读了几篇文章,但是这些通常不会描述使用策略的真实原因,比如生成报告/账单/validation等。

有没有现实世界的例子,你认为战略模式是常见的?

那这个呢:

你必须encryption一个文件。

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

对于大文件,可以使用另一种策略,其中部分文件在内存中读取,部分encryption结果存储在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() ); 

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

我希望这有帮助。

(我甚至不知道密码是否是正确的字:P)

再一次,一个旧的post,但仍然打开search,所以我会再添加两个例子(代码是在C#中)。 我绝对喜欢这个策略模式,因为当项目经理说:“我们希望应用程序可以做,但还不清楚,在不久的将来可能会改变的时候,我已经节省了很多次了”。 我很确定这个video解释了所有关于战略模式的一切,以星际争霸为例: http : //www.youtube.com/watch?v=MOEsKHqLiBM

属于这一类的东西:

  • sorting(我们要sorting这些数字,但是我们不知道是否要使用BrickSort,BubbleSort或其他sorting)

  • validation(我们需要根据“某些规则”来检查项目,但是目前还不清楚这个规则是什么,我们可能会想到新的规则。)

  • 游戏(我们希望玩家在移动的时候走路或跑步,但也许将来他也应该可以游泳,飞行,传送,在地下挖洞等)

  • 存储信息(我们希望应用程序将信息存储到数据库中,但稍后它可能需要能够保存文件或进行networking调用)

  • 输出(我们需要输出X为一个纯string,但后来可能是一个CSV,XML,JSON等)


例子

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

现在,在发展的时刻,这些企业还没有完全清晰,新的规则可能随时出现。 stragety模式的力量是我做了一个RuleAgent,给出了一个IRule的列表。

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

在将产品分配给某个人的时候,我创build了一个RuleAgent,给它一个规则列表(这些规则都实现了IRule),并要求它validation一个分配。 它将运行所有的规则(因为它们都实现了相同的接口,都有IsApproved方法),如果它们中的任何一个返回false,则返回false。

现在当pipe理者突然出现并说,我们也需要拒绝所有的女性任务,或者所有的姓名为“Johnsson”或者其他任务的任务…你做这样的新class级:

 public LastNameRule : IRule { public bool IsApproved(Assignment assignment) //Interface method { if (assignment.Person.Lastname == "Johnsson") { return false; } return true; } } public GenderRule : IRule { public bool IsApproved(Assignment assignment) //Interface method { if (assignment.Person.Gender== Gender.Female) { return false; } return true; } } 

您将看到,您不必一直添加或删除if语句或代码,只需创build一个实现IRUle接口的新规则类,并在需要时将其切换出去。


另一个很好的例子(Scott Allen的video系列http://www.asp.net/mvc/pluralsight )在应用程序的UNIT-test部分中使用策略模式的部分)

他build立一个网站,其中有一个网页,显示基于stream行的项目。 然而,“stream行”可以是许多事情(大多数观点,大多数订阅者,创builddate,大多数活动,最less量的评论,无论如何),并且如果pipe理层还不知道如何订购,并且可能想要尝试不同的在晚些时候订购。 你可以用order方法创build一个接口(IOrderAlgorithm或somethng),然后让一个Orderer对象将这个顺序委托给IOrderAlgorithm接口的具体实现。 你可以创build一个“CommentOrderer”,“ActivityOrderer”等等,当新的需求出现时,把它们切换出来。

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

  • sorting列表。 该策略是用于确定列表中的两项中的哪一项是“第一”
  • 你可能有一个应用程序,在运行时可以selectsortingalgorithm本身(QuickSort,HeapSort等)
  • Appenders,Layouts和Filters in Log4Net和Log4j
  • UI工具包中的布局pipe理器
  • 数据压缩。 你可能有一个ICompressor接口,其唯一的方法如下所示:

    byte [] compress(byte [] input);

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

我有一个应用程序每天同步它的用户基础与我们的企业目录。 用户符合或不符合大学的地位。 每天configuration程序都要经过并确保那些应该符合条件的人在应用程序中被调配,而那些没有被调配的人(实际上是根据优雅的降级algorithm,但是这不重要)。 星期六我做了一个更彻底的更新,同步每个用户的一些属性,以及确保他们有适当的资格。 在本月底,我会根据当月的使用情况做一些退款处理。

我使用可组合的策略模式来执行此同步。 主程序主要根据星期几(仅同步更改/全部同步)和学期相对于学术日历的时间来select主策略。 如果结算周期正在结束,那么它也使用结算策略进行组合。 然后通过标准接口运行所select的策略。

我不知道这有多普遍,但我觉得这是一个完美的战略模式。

策略模式的一个常见用法是定义自定义的sorting策略(在没有高阶函数的语言中),例如按照Java的长度对string列表进行sorting,传递一个匿名内部类(策略接口的实现):

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

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

这是在文件传递系统中使用的战略模式的一个非常实际的例子。

我有一个PDF传送系统,收到一个包含大量文件和一些元数据的档案。 根据元数据,决定将文档放在哪里; 例如,根据数据,我可以将文档存储在ABC存储系统中,或三者的组合。

不同的客户使用这个系统,并且在出现错误时有不同的回滚/error handling要求:一个想让交付系统在第一个错误时停止,将所有已经交付的文档保留在其存储器中,但是停止处理并且不交付其他任何东西; 另一个人希望它在B存储时以错误的方式回滚,但是保留已经交付给A任何东西。 很容易想象,第三或第四个也会有不同的需求。

为了解决这个问题,我创build了一个包含交付逻辑的基本交付类,以及从所有存储器回滚内容的方法。 这些方法实际上并不直接由交付系统在出现错误的情况下调用。 相反,类使用dependency injection来接收一个“回滚/error handling策略”类(基于客户使用系统),如果有错误,则会调用该类。如果适用于该策略,则会调用回滚方法。

交货类本身报告了什么发生在战略类(什么文件被传送到什么存储,什么故障发生),并且每当发生错误,它就问策略是否继续。 如果策略显示为“停止”,则类将调用策略的“清除”方法,该方法使用先前报告的信息来决定从传递类中调用哪个回滚方法,或者什么都不做。

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

所以我现在有两个不同的策略:一个是QuitterStrategy (退出第一个错误,不清除任何东西),另一个是MaximizeDeliveryToAStrategy (尽可能尝试不中止过程,并且不回退传递到存储的东西A ,但是如果传递给C则从B回滚失败)。

根据我的理解,这是战略模式的一个例子。 如果你(是的,你阅读)认为我错了,请评论下面,让我知道。 我对什么构成战略模式的“纯粹”使用感到好奇,我的执行的哪些方面违反了定义。 我觉得这看起来有点有趣,因为战略界面有点胖。 到目前为止我看到的所有例子都只使用一种方法,但我仍然认为这封装了一个algorithm(如果一块业务逻辑可以被认为是algorithm,我认为它)。

由于该策略在交付执行期间也被通知事件,所以也可以被视为观察者 ,但这是另一个故事。

从一个小小的研究,似乎这是一个“合成模式”(像MVC,一种模式,在一个特定的方式下使用多个devise模式)称为顾问 。 这是一个关于交付是否应该继续的顾问,但它也是一个活动的error handling程序,因为它可以在需要时回滚。

无论如何,这是一个相当复杂的例子,可能会让你感觉到战略模式的使用太简单/愚蠢。 与其他模式一起使用时,它可能非常复杂,更加适用。

重要提示:

  1. 策略是行为devise​​模式。 它用于在algorithm族之间切换。

  2. 这个模式包含一个抽象的策略接口以及该接口的许多具体的策略实现( algorithm )。

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

来自维基百科的 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

战略模式由来源

战略模式的一个很好的例子是在一个游戏中,我们可以有不同的angular色,每个angular色可以有多个武器来攻击,但一次只能使用一个武器。 因此,我们以angular色为背景,例如国王,指挥官,骑士,士兵和武器作为一种策略,其中攻击()可以是取决于使用的武器的方法/algorithm。 所以如果具体武器类是剑,斧,弩,弓和箭等,他们都会实施攻击()方法。 我相信进一步的解释是不需要的。

策略模式是专门用于validation和sortingalgorithm的最常用的模式。

让我用一个简单的实例来解释

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

testing代码是这样的

 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/

你确定一个“订单”的状态不是一个国家模式吗? 我有一个预感,一个订单将不会根据其状态处理不同。

例如,订单上的方法:

 order.Ship(); 
  • 如果运输方式因地位而异,那么你就有了一个战略模式。
  • 但是,如果Ship()方法只在订单已经付款时成功,而且订单还没有发货,那么您已经有了一个状态模式。

我发现的国家模式(和其他模式)的最好的例子是在“ 头第一devise模式 ”,这是惊人的。 紧随其后的将是David Cumps的博客系列模式 。

假设你想写一个algorithm来计算给定月份和年份的第n个Xday ,例如2014年10月的第二个星期一。你想用Android的Time类android.text.format.Time来表示date,但是你也想写一个通用的algorithm,也可以适用于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中,使用genericsalgorithm的类:

 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重复使用相同的algorithm,那么我只需要编写一个类CalendarMath来实现DatetimeMath中的三个方法,然后使用

 OrdinalDayOfWeekCalculator odowc2 = new OrdinalDayOfWeekCalculator(new CalendarMath()); Calendar canadianThanksgivingCal = (Calendar)odowc2.getDate( year, Calendar.OCTOBER, Calendar.MONDAY, 2); 

我在一个相当复杂的引擎中使用了策略方法,这是一个很好的例子。 从本质上来说,引擎的作用是首先find一个有一个小部件的人的列表,第二个angular色是根据未知数量的参数(例如价格距离以前的业务,库存,运输选项等等等等)

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

然后,我们也意识到,我们有多个algorithm,其中一些是基于加权参数,另外一些是非常奇怪的,而且我不能在没有提取visios和图表的情况下做到这一点,因为我们有很多algorithmselect最好的人。

我们的服务本身是非常重要的,它基本上定义了input,输出并对数据进行了一些规范化处理,它还使用提供者模式来插入使用策略的应用程序特定数据提供者和algorithm提供者。 这是一个相当有效的系统。

如果我们正在使用我们从未解决过的策略或模板模式,我们有一些争论。

几个星期前,我添加了一个通用的Java接口,由我们的一个域对象实现。 该域对象是从数据库加载的,数据库表示是具有大约10+个分支的星型模式。 有这么重的域对象的后果之一是,我们不得不使其他域对象代表相同的模式,尽pipe重量较轻。 所以我让其他轻量级对象实现相同的接口。 否则,我们有:

 public interface CollectibleElephant { long getId(); String getName(); long getTagId(); } public class Elephant implements CollectibleElephant { ... } public class BabyElephant implements CollectibleElephant { ... } 

本来我想用CollectibleElephant来分类Elephant 。 很快,我的队友就跑到了CollectibleElephant上进行安全检查,在发送到GUI时对其进行过滤。

我们必须为具有非常复杂的数据库的企业平台创build第三方configuration界面。 要提供的数据的提交是作为我们的数据types的列表,这些数据types被放入我们的应用程序的优先级队列中,以便由于依赖性而以正确的顺序将它们写入到数据库中。

编写这些数据的过程非常简单,不断popup优先级队列的顶部,然后根据您提取的对象的typesselect策略。

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