C# 策略模式的真实世界示例
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/370258/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Real World Example of the Strategy Pattern
提问by
I've been reading about the OCP principaland how to use the strategy pattern to accomplish this.
我一直在阅读关于OCP 原则以及如何使用策略模式来实现这一点。
I was going to try and explain this to a couple of people, but the only example I can think of is using different validation classes based on what status an "order" is.
我打算尝试向几个人解释这一点,但我能想到的唯一示例是根据“订单”的状态使用不同的验证类。
I've read a couple of articles online, but these don't usually describe a real like reason to use the strategy, like generating reports/bills/validation etc...
我在网上阅读了几篇文章,但这些文章通常没有描述使用该策略的真正原因,例如生成报告/账单/验证等......
Are there any real world examples where you think a strategy pattern is common?
是否有任何现实世界的例子让您认为策略模式很常见?
回答by OscarRyz
What about this:
那这个呢:
You have to encrypt a file.
您必须加密文件。
For small files, you can use "in memory" strategy, where the complete file is read and kept in memory ( let's say for files < 1 gb )
对于小文件,您可以使用“内存中”策略,读取完整文件并将其保存在内存中(假设文件 < 1 gb )
For large files, you can use another strategy, where parts of the file are read in memory and partial encrypted results are stored in tmp files.
对于大文件,您可以使用另一种策略,其中文件的一部分在内存中读取,部分加密结果存储在 tmp 文件中。
These may be two different strategies for the same task.
对于同一任务,这可能是两种不同的策略。
The client code would look the same:
客户端代码看起来是一样的:
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.
}
}
The
这
Cipher c = CipherFactory.getCipher( file.size() );
Would return the correct strategy instance for the cipher.
将返回密码的正确策略实例。
I hope this helps.
我希望这有帮助。
( I don't even know if Cipher is the right word :P )
(我什至不知道 Cipher 是否正确:P)
回答by tvanfosson
I have an application that synchronizes it's user base each day against our enterprise directory. User's are eligible or not eligible based on their status in the University. Each day the provisioning program goes through and makes sure that those who are supposed to be eligible are provisioned in the application and those who are not are de-provisioned (actually according to a graceful degradation algorithm, but that's beside the point). On Saturday I do a more thorough update that synchronizes some properties of each user as well as making sure that they have the proper eligibility. At the end of the month I do some bill back processing based on usage for that month.
我有一个应用程序,每天将它的用户群与我们的企业目录同步。用户是否符合条件取决于他们在大学中的身份。供应程序每天都会执行并确保那些应该有资格的人在应用程序中供应,而那些没有被取消供应(实际上是根据优雅的降级算法,但这无关紧要)。在星期六,我进行了更彻底的更新,同步每个用户的一些属性,并确保他们具有适当的资格。在月底,我会根据当月的使用情况进行一些账单退款处理。
I use a composable strategy pattern to do this synchronization. The main program basically chooses a master strategy depending on the day of the week (sync changes only/sync all) and the time of semester relative to the academic calendar. If the billing cycle is ending, then it also composes it with a billing strategy. It then runs the chosen strategy via a standard interface.
我使用可组合的策略模式来执行此同步。主程序基本上根据星期几(仅同步更改/同步所有)和相对于学术日历的学期时间来选择主策略。如果计费周期即将结束,那么它还将与计费策略组合在一起。然后它通过标准接口运行所选策略。
I don't know how common this is, but I felt like it was a perfect fit for the strategy pattern.
我不知道这有多普遍,但我觉得它非常适合策略模式。
回答by JoshBerke
I used the strategy approach in a fairly complex engine in an application that is a good example. Essentially the engine's role was to go and first find a list of people who had a widget, it's second role was to figure out which were the 10 best people with a widget based on an unknown number of parameters (things like price distance previous business together, ammount on stock, shipping options etc etc etc...)
我在一个相当复杂的引擎的应用程序中使用了策略方法,这是一个很好的例子。本质上,引擎的作用是首先找到拥有小部件的人的列表,第二个作用是根据未知数量的参数(例如价格距离以前的业务在一起)找出拥有小部件的最佳 10 人,库存量,运输选项等等等......)
Essentially what we did was we broke the problem into two strategies the first being data retrieval, as we knew that we had multiple sources of our widgets and we needed to be able to get the data and transform it to a common structure.
基本上我们所做的是将问题分解为两种策略,第一种是数据检索,因为我们知道我们的小部件有多个来源,我们需要能够获取数据并将其转换为通用结构。
We then also realized that we had multiple algorithims some were based on weighting the parameters, others were very weird and propitery and I couldn't do them justice without pulling out visios and charts and well you get the picture, we had lots of algorithims for selecting the best people.
然后我们也意识到我们有多种算法,其中一些基于对参数进行加权,另一些非常奇怪和适当,如果不拉出 visios 和图表,我就无法公正地对待它们,你明白了,我们有很多算法用于选择最优秀的人。
Our service itself was very thing it essentially defined the inputs, outputs and did some normalization of the data, it also used a provider pattern to plug-in the application specific data providers and algorithim providers which used the strategy. This was a fairly effective system.
我们的服务本身非常重要,它本质上定义了输入、输出并对数据进行了一些规范化,它还使用提供者模式来插入使用该策略的特定于应用程序的数据提供者和算法提供者。这是一个相当有效的系统。
We had some debates if we were using a strategy or a template pattern which we never resolved.
如果我们使用的是我们从未解决过的策略或模板模式,我们就进行了一些辩论。
回答by Alan
A few weeks ago, I added a common Java interface which was implemented by one of our domain objects. This domain object was loaded from the database, and the database representation was a star schema with about 10+ branches. One of the consequences of having such a heavy weight domain object is that we've had to make other domain objects that represented the same schema, albeit less heavyweight. So I made the other lightweight objects implement the same interface. Put otherwise we had:
几周前,我添加了一个通用 Java 接口,该接口由我们的域对象之一实现。这个域对象是从数据库加载的,数据库表示是一个星型模式,大约有 10 多个分支。拥有如此重量级的域对象的后果之一是,我们不得不制作表示相同模式的其他域对象,尽管重量级较小。所以我让其他轻量级对象实现了相同的接口。否则我们有:
public interface CollectibleElephant {
long getId();
String getName();
long getTagId();
}
public class Elephant implements CollectibleElephant { ... }
public class BabyElephant implements CollectibleElephant { ... }
Originally, I wanted to use CollectibleElephant
to sort Elephant
s. Pretty quickly, my teammates glommed onto CollectibleElephant
to run security checks, filter them as they get sent to the GUI, etc.
本来,我想用CollectibleElephant
对Elephant
s进行排序。很快,我的队友就CollectibleElephant
开始运行安全检查,在它们被发送到 GUI 时对其进行过滤等。
回答by grootjans
Are you sure that the status of an "order" is not a State pattern? I have a hunch that an order will not be handled differently depending on its status.
您确定“订单”的状态不是状态模式吗?我有一种预感,不会根据订单的状态对其进行不同的处理。
Take for example the method Shipon the Order:
以订单上的发货方法为例:
order.Ship();
- If the shipping method varies in function of its status, then you've got a strategy pattern.
- If however the Ship()method succeeds only when the order has been paid, and the order has not been shipped yet, you've got a state pattern.
- 如果运输方式因其状态而异,那么您就有了一种策略模式。
- 但是,如果Ship()方法仅在订单已支付且订单尚未发货时成功,则您有一个状态模式。
The best example of the state pattern (and other patterns) I found was in the book "Head First Design Patterns", which is amazing. A close second will be David Cumps' blogging series of patterns.
我发现的状态模式(和其他模式)最好的例子是在“ Head First Design Patterns”一书中,这真是太棒了。紧随其后的是David Cumps 的博客系列模式。
回答by Coxy
We had to create a third-party provisioning interface for an enterprise platform with a very complicated database. The submission of data to be provisioned was as a list of our data types which were put into a priority queue in our application so they could be written to the database in the correct order due to dependencies.
我们必须为具有非常复杂数据库的企业平台创建第三方配置接口。要提供的数据的提交是我们的数据类型列表,这些数据类型被放入我们应用程序的优先队列中,因此它们可以由于依赖关系以正确的顺序写入数据库。
The process to write that data was then quite simple, keep popping off the top of the priority queue and then choose a strategy based on the type of the object that you extract.
写入数据的过程非常简单,不断从优先级队列的顶部弹出,然后根据您提取的对象类型选择策略。
回答by Eric Pohl
I can think of several fairly simple examples:
我能想到几个相当简单的例子:
- Sorting a list. The strategy is the comparison used to decide which of two items in the list is "First"
- You might have an application where the sorting algorithm itself (QuickSort, HeapSort, etc.) may be chosen at runtime
- Appenders, Layouts, and Filters in Log4Netand Log4j
- Layout Managersin UI toolkits
Data compression. You might have an ICompressor interface whose sole method looks something like this:
byte[] compress(byte[] input);
Your concrete compression classes might be things like RunLengthCompression, DeflateCompression, etc.
回答by Fabian Steeg
One common usage of the strategy pattern is to define custom sorting strategies (in languages without higher-order functions), e.g. to sort a list of strings by length in Java, passing an anonymous inner class (an implementation of the strategy interface):
策略模式的一个常见用法是定义自定义排序策略(在没有高阶函数的语言中),例如在 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);
In a similar manner, strategies can be used for native queries with object databases, e.g. in db4o:
以类似的方式,策略可用于对象数据库的本地查询,例如在 db4o 中:
List<Document> set = db.query(new Predicate<Document>() {
public boolean match(Document candidate) {
return candidate.getSource().contains(source);
}
});
回答by Gui Prá
I know this is an old question, but I think I have another interesting example that I implemented recently.
我知道这是一个老问题,但我想我最近实施了另一个有趣的例子。
This is a very practical example of the strategy pattern being used in a document delivery system.
这是在文档交付系统中使用的策略模式的一个非常实用的示例。
I had a PDF delivery system which received an archive containing lots of documents and some metadata. Based on the metadata, it decided where to put the document in; say, depending on the data, I could store the document in A
, B
, or C
storage systems, or a mix of the three.
我有一个 PDF 交付系统,它接收了一个包含大量文档和一些元数据的档案。根据元数据,它决定将文档放在哪里;比如说,根据数据,我可以将文档存储在A
、B
、 或C
存储系统中,或者三者的混合。
Different customers used this system, and they had different rollback / error handling requirements in case of errors: one wanted the delivery system to stop on the first error, leave all documents already delivered in their storages, but stop the process and not deliver anything else; another one wanted it to rollback from B
in case of errors when storing in C
, but leave whatever was already delivered to A
. It's easy to imagine that a third or fourth one will also have different needs.
不同的客户使用了这个系统,他们在出现错误时有不同的回滚/错误处理要求:一个人希望交付系统在第一个错误时停止,将已经交付的所有文档留在他们的存储中,但停止该过程并且不交付任何其他东西; 另一个人希望它B
在存储时发生错误时回滚C
,但保留已经交付给的任何内容A
。很容易想象第三个或第四个也有不同的需求。
To solve the problem, I have created a basic delivery class that contains the delivery logic, plus methods for rolling back stuff from all storages. Those methods are not actually called by the delivery system directly in case of errors. Instead, the class uses Dependency Injection to receive a "Rollback / Error Handling Strategy" class (based on the customer using the system), which is called in case of errors, which in turn calls the rollback methods if it's appropriate for that strategy.
为了解决这个问题,我创建了一个基本的交付类,其中包含交付逻辑,以及从所有存储回滚东西的方法。如果出现错误,这些方法实际上不会由交付系统直接调用。相反,该类使用依赖注入来接收“回滚/错误处理策略”类(基于使用系统的客户),在出现错误时调用该类,如果适合该策略,则会调用回滚方法。
The delivery class itself reports what's going on to the strategy class (what documents were delivered to what storages, and what failures happened), and whenever an error occurs, it asks the strategy whether to continue or not. If the strategy says "stop it", the class calls the strategy's "cleanUp" method, which uses the information previously reported to decide which rollback methods to call from the delivery class, or simply do nothing.
交付类本身向策略类报告正在发生的事情(哪些文档被交付到哪些存储,发生了什么故障),并且每当发生错误时,它会询问策略是否继续。如果策略说“停止它”,则该类调用该策略的“cleanUp”方法,该方法使用先前报告的信息来决定从交付类调用哪些回滚方法,或者干脆什么都不做。
rollbackStrategy.reportSuccessA(...);
rollbackStrategy.reportFailureB(...);
if (rollbackStrategy.mustAbort()) {
rollbackStrategy.rollback(); // rollback whatever is needed based on reports
return false;
}
So I now have two different strategies: one is the QuitterStrategy
(which quits on the first error and cleans up nothing) and the other one is the MaximizeDeliveryToAStrategy
(which tries as much as possible not to abort the process and never rollback stuff delivered to storage A
, but rollbacks stuff from B
if delivery to C
fails).
所以我现在有两种不同的策略:一种是QuitterStrategy
(在第一个错误时退出并且不清除任何东西),另一种是MaximizeDeliveryToAStrategy
(尽可能不中止进程并且从不回滚传送到存储的东西A
,但是从B
如果交付C
失败回滚东西)。
From my understanding, this is one example of the strategy pattern. If you (yes, you reading) think I'm wrong, please comment below and let me know. I'm curious as to what would constitute a "pure" use of the strategy pattern, and what aspects of my implementation violate the definition. I think it looks a bit funny because the strategy interface is a bit fat. All examples I've seen so far use only one method, but I still think this encapsulates an algorithm (if a piece of business logic can be considered an algorithm, which I think it does).
根据我的理解,这是策略模式的一个例子。如果您(是的,您正在阅读)认为我错了,请在下面发表评论并告诉我。我很好奇什么会构成策略模式的“纯”使用,以及我的实现的哪些方面违反了定义。我觉得它看起来有点好笑,因为策略界面有点胖。到目前为止,我看到的所有示例都只使用一种方法,但我仍然认为这封装了一个算法(如果可以将一段业务逻辑视为一种算法,我认为确实如此)。
Since the strategy is also notified about events during the delivery execution, it can also be considered an Observer, but that's another story.
由于该策略还会在交付执行期间收到有关事件的通知,因此也可以将其视为Observer,但那是另一回事了。
From doing a little research, it seems like this is a "composite pattern" (like MVC, a pattern that uses multiple design patterns underneath in a particular way) called the Advisor. It's an advisor on whether the delivery should continue or not, but it's also an active error handler since it can rollback stuff when asked to.
通过做一些研究,这似乎是一种称为Advisor的“复合模式”(如 MVC,一种以特定方式在底层使用多种设计模式的模式)。它是关于交付是否应该继续的顾问,但它也是一个活动的错误处理程序,因为它可以在被要求时回滚内容。
Anyways, this is a quite complex example that might make that feeling of yours that usages of the strategy pattern are all too simple / silly. It can be really complex and even more applicable when used together with other patterns.
无论如何,这是一个非常复杂的例子,它可能会让你觉得策略模式的用法都太简单/愚蠢了。当与其他模式一起使用时,它可能非常复杂,甚至更适用。
回答by Céryl Wiltink
Again, an old post but still turns up on searches so I'll add two more examples (Code is in C#). I absolutely love the Strategy pattern since it has saved my butt a lot of times when the project managers say: "We want the application to do 'X', but 'X' is not yet clear and it can change in the near future." This video explaining the strategy pattern, uses StarCraft as an example.
同样,一个旧帖子,但仍然出现在搜索中,所以我将添加另外两个示例(代码在 C# 中)。我非常喜欢 Strategy 模式,因为当项目经理说:“我们希望应用程序执行 'X',但 'X' 尚不明确,并且在不久的将来它可能会改变时,它已经让我省了很多时间。 ” 本视频讲解策略模式,以星际争霸为例。
Stuff that falls in this category:
属于这一类的东西:
Sorting: We want to sort these numbers, but we don't know if we are gonna use BrickSort, BubbleSort or some other sorting
Validation: We need to check items according to "Some rule", but it's not yet clear what that rule will be, and we may think of new ones.
Games: We want player to either walk or run when he moves, but maybe in the future, he should also be able to swim, fly, teleport, burrow underground, etc.
Storing information: We want the application to store information to the Database, but later it may need to be able to save a file, or make a webcall
Outputting: We need to output X as a plain string, but later may be a CSV, XML, JSON, etc.
排序:我们想对这些数字进行排序,但我们不知道是否要使用 BrickSort、BubbleSort 或其他一些排序
验证:我们需要根据“一些规则”检查项目,但目前尚不清楚该规则是什么,我们可能会想到新的规则。
游戏:我们希望玩家在移动时要么走路要么跑步,但也许在未来,他还应该能够游泳、飞行、传送、在地下挖洞等。
存储信息:我们希望应用程序将信息存储到数据库中,但稍后它可能需要能够保存文件,或进行网络调用
输出:我们需要将 X 输出为纯字符串,但稍后可能是 CSV、XML、JSON 等。
Examples
例子
I have a project where the users can assign products to people in a database. This assignment of a product to a person has a status which is either "Approved" or "Declined", which is dependent on some business rules. For example: if a user assigns a product to a person with a certain age, it's status should be declined; If the difference between two fields in the item is larger than 50, it's status is declined, etc.
我有一个项目,用户可以将产品分配给数据库中的人。将产品分配给某人的状态为“已批准”或“已拒绝”,这取决于某些业务规则。例如:如果用户将产品分配给某个年龄的人,则其状态应为拒绝;如果项目中两个字段之间的差异大于 50,则其状态为拒绝等。
Now, at the moment of development these business rules are not yet all completely clear, and new rules could come up at any time. The power of the stragety-pattern is that I made a RuleAgent, which is given a list of IRules.
现在,在发展的时候,这些业务规则还没有完全明确,新的规则随时可能出现。策略模式的强大之处在于我创建了一个 RuleAgent,它被赋予了一个 IRules 列表。
public interface IRule {
bool IsApproved(Assignment assignment);
}
At the moment of assigning a product to a person, I create a RuleAgent, give it a list of rules (which all implement IRule), and ask it to validate an assignment. It'll run through all it's rules. Which, because they all implement the same interface, all have the IsApproved
method and return false if any of them returns false.
在将产品分配给某人的那一刻,我创建了一个 RuleAgent,给它一个规则列表(都实现了 IRule),并要求它验证分配。它将贯穿所有规则。其中,因为它们都实现了相同的接口,所以都有IsApproved
方法并且如果它们中的任何一个返回 false,则返回 false。
Now when for instance the manager suddenly comes up and says, we also need to decline all assignments to interns, or all assignments to people working overtime... You make new classes like this:
现在,例如,当经理突然出现并说,我们还需要拒绝对实习生的所有分配,或对加班人员的所有分配……您可以像这样创建新课程:
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;
}
}
You see that you don't have to keep adding or removing if-statements or code, just make a new rule-class that implements the IRUle interface and switch those out when needed.
您会看到您不必不断添加或删除 if 语句或代码,只需创建一个新的规则类来实现 IRUle 接口并在需要时将它们切换出来。
Another great example: Scott Allen's video series at http://www.asp.net/mvc/pluralsightwhere he uses the strategy pattern in the Unit-test part of the application
另一个很好的例子:Scott Allen 在http://www.asp.net/mvc/pluralsight的视频系列,他在应用程序的单元测试部分使用了策略模式
He builds a website which has a page that displays items based on popularity. However "Popular" can be many things (most views, most subscribers, creation date, most activity, least amount of comments, etc), and in case management doesn't yet know exactly how to order, and may want to experiment with different orderings at a later date. You make an interface (IOrderAlgorithm or something) with an order method, and let an Orderer-object delegate the ordering to a concrete implementation of the IOrderAlgorithm interface. You can make a "CommentOrderer", "ActivityOrderer", etc... And just switch these out when new requirements come up.
他建立了一个网站,该网站有一个根据受欢迎程度显示项目的页面。然而,“流行”可以是很多东西(最多的观看次数、最多的订阅者、创建日期、最多的活动、最少的评论等),以防管理人员还不确切知道如何订购,并且可能想要尝试不同的稍后订购。您使用 order 方法创建一个接口(IOrderAlgorithm 或其他东西),并让 Orderer 对象将排序委托给 IOrderAlgorithm 接口的具体实现。您可以创建“CommentOrderer”、“ActivityOrderer”等……当出现新需求时,只需将它们切换掉即可。