我一直在阅读有关OCP主要内容以及如何使用策略模式来实现这一目标.
我打算尝试向几个人解释这个,但我能想到的唯一例子是根据"订单"的状态使用不同的验证类.
我在线阅读了几篇文章,但这些文章通常没有描述使用该策略的真实原因,如生成报告/账单/验证等...
是否有任何现实世界的例子,您认为策略模式是常见的?
那这个呢:
您必须加密文件.
对于小文件,您可以使用"内存"策略,其中读取完整文件并保存在内存中(假设文件<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)
同样,一个旧的帖子但仍然出现在搜索上,所以我将再添加两个例子(代码在C#中).我非常喜欢策略模式,因为当项目经理说:"我们希望应用程序执行'X',但'X'尚不清楚,它可以在不久的将来改变时,它已经节省了很多次. " 该视频解释了战略模式,以星际争霸为例.
属于这一类的东西:
排序:我们想对这些数字进行排序,但我们不知道是否要使用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"等......当新的需求出现时,只需将它们切换出来.
我可以想到几个相当简单的例子:
排序列表.策略是比较用于决定列表中的两个项目中的哪一个是"第一"
您可能有一个应用程序,可以在运行时选择排序算法本身(QuickSort,HeapSort等)
Log4Net和Log4j中的 Appenders,Layouts和Filters
UI工具包中的布局管理器
数据压缩.您可能有一个ICompressor接口,其唯一方法如下所示:
byte [] compress(byte [] input);
您的具体压缩类可能是RunLengthCompression,DeflateCompression等.
主要说明:
策略是行为设计模式.它用于在算法族之间切换.
此模式包含一个抽象策略接口和该接口的许多具体策略实现(算法).
该应用程序仅使用策略界面.根据某些配置参数,具体策略将标记为接口.
来自维基百科的 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. MapstrategyContext = new HashMap (); 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的战略模式
源头制定的战略模式
策略模式的一个常见用法是定义自定义排序策略(在没有高阶函数的语言中),例如,在Java中按长度对字符串列表进行排序,传递匿名内部类(策略接口的实现):
Listnames = Arrays.asList("Anne", "Joe", "Harry"); Collections.sort(names, new Comparator () { public int compare(String o1, String o2) { return o1.length() - o2.length(); } }); Assert.assertEquals(Arrays.asList("Joe", "Anne", "Harry"), names);
以类似的方式,策略可用于具有对象数据库的本机查询,例如在db4o中:
Listset = db.query(new Predicate () { public boolean match(Document candidate) { return candidate.getSource().contains(source); } });
我有一个应用程序,它每天将它的用户群与我们的企业目录同步.根据用户在大学中的状态,用户符合资格或不符合资格.配置程序每天都会通过,并确保应用程序中配置了应该符合条件的人员,并且确保那些没有配置的人员(实际上根据优雅的降级算法,但这不是重点).星期六我做了一个更彻底的更新,同步每个用户的一些属性,并确保他们有适当的资格.在月底,我根据当月的使用情况做一些账单回溯处理.
我使用可组合策略模式来执行此同步.主程序基本上选择主策略,取决于星期几(仅同步变化/同步所有)和学期相对于学术日历的时间.如果结算周期结束,那么它还会使用结算策略进行组合.然后,它通过标准接口运行所选策略.
我不知道这有多常见,但我觉得它非常适合策略模式.
我知道这是一个老问题,但我想我最近实施了另一个有趣的例子.
这是在文档传递系统中使用的策略模式的非常实用的示例.
我有一个PDF传送系统,它收到一个包含大量文档和一些元数据的存档.根据元数据,它决定将文档放在哪里; 比如,根据数据,我可以在存储文档A
,B
或C
存储系统,或三者的组合.
不同的客户使用此系统,并且在出现错误时他们有不同的回滚/错误处理要求:一个人希望交付系统在第一个错误时停止,将所有文档保留在他们的存储中,但是停止该过程而不提供任何其他内容; 另一个人希望它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
失败则回滚东西).
根据我的理解,这是战略模式的一个例子.如果您(是的,您正在阅读)认为我错了,请在下面评论并告诉我.我很好奇什么会构成对策略模式的"纯粹"使用,以及我的实现的哪些方面违反了定义.我觉得它看起来有点滑稽,因为战略界面有点胖.到目前为止我看到的所有示例只使用了一种方法,但我仍然认为这封装了一种算法(如果一块业务逻辑可以被认为是一种算法,我认为它是这样做的).
由于策略也会在交付执行期间通知事件,因此它也可以被视为观察者,但这是另一个故事.
从进行一些研究看起来,这似乎是一个"复合模式"(如MVC,一种在特定方式下使用多种设计模式的模式),称为Advisor.它是关于交付是否应该继续的顾问,但它也是一个主动错误处理程序,因为它可以在被要求时回滚内容.
无论如何,这是一个非常复杂的例子,可能会让你觉得战略模式的用法过于简单/愚蠢.当与其他模式一起使用时,它可能非常复杂并且更加适用.
策略模式是最常用的模式,专门用于验证和排序算法.
让我用一个简单的实际例子来解释
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/
策略模式的一个很好的例子是在一个游戏中,我们可以拥有不同的角色,每个角色可以拥有多种武器来进攻,但一次只能使用一种武器。因此,我们以角色为背景,例如,国王,司令,骑士,士兵和武器为策略,其中Attack()可能是取决于所使用武器的方法/算法。因此,如果具体的武器类别是Sword,Axe,Crossbow,BowAndArrow等。它们都将实现Attack()方法。我确信不需要进一步的解释。