策略模式还是无策略模式?
Strategy pattern or no strategy pattern?
在不输入学术定义的情况下,假设当您有将执行操作的客户端代码(上下文)时使用策略模式,并且可以以不同的方式(算法)实现此操作。例如:https://www.dofactory.com/net/strategy-design-pattern
使用何种策略(或算法)取决于一些输入条件的很多场合。这就是为什么有时将策略模式与工厂模式结合使用的原因。 Client 将输入条件传递给 Factory。然后工厂知道必须创建哪个策略。然后Client执行创建的Strategy的操作。
然而,我曾多次遇到在我看来恰恰相反的问题。要执行的操作总是相同的,但它只会根据一系列输入条件来执行。例如:
public interface IStrategy
{
string FileType { get; }
bool CanProcess(string text);
}
public class HomeStrategy : IStrategy
{
public string FileType => ".txt";
public bool CanProcess(string text)
{
return text.Contains("house") || text.Contains("flat");
}
}
public class OfficeStrategy : IStrategy
{
public string FileType => ".doc";
public bool CanProcess(string text)
{
return text.Contains("office") || text.Contains("work") || text.Contains("metting");
}
}
public class StragetyFactory
{
private List<IStrategy> _strategies = new List<IStrategy>{ new HomeStrategy(), new OfficeStrategy() };
public IStrategy CreateStrategy(string fileType)
{
return _strategies.Single(s => s.FileType == fileType);
}
}
现在客户端代码将从某个存储库获取文件并将文件保存在数据库中。就是这个操作,把文件存入数据库,只是看文件的类型和每个文件的具体情况。
public class Client
{
public void Execute()
{
var files = repository.GetFilesFromDisk();
var factory = new StragetyFactory();
foreach (var file in files)
{
var strategy = factory.CreateStrategy(file.Type);
if (strategy.CanProcess(file.ContentText))
{
service.SaveInDatabase(file);
}
}
}
}
我认为这是与策略模式不同的模式,我错了吗? (尽管我已经在上面的代码中调用了 Strategy,因为我在很多场合下都是这样)
如果这个问题与策略模式解决的问题不同,那么它是哪种模式?
不是真正的策略模式,因为 strategy pattern in Wikipedia 中的定义说:
In computer programming, the strategy pattern (also known as the
policy pattern) is a behavioral software design pattern that enables
selecting an algorithm at runtime. Instead of implementing a single
algorithm directly, code receives run-time instructions as to which in
a family of algorithms to use.[1]
您不是在选择要在运行时执行的算法,您只是检查条件以查看文件类型是否满足条件,然后执行算法。
你希望这会改变吗?您是否需要它是可扩展的,以便将来如果您需要根据文件类型执行不同的代码,您可以做到 easily.
如果这些问题的答案是是,那么您可以保留策略并进行少量更改。
首先定义定义要执行的代码的基本策略class
public abstract class StrategyBase
{
public abstract bool CanProcess(string fileType);
public virtual void Execute(File file)
{
_service.SaveInDatabase(file);
}
}
您的策略更改为派生自 base
public class HomeStrategy : StrategyBase
{
public string FileType => ".txt";
public override bool CanProcess(string text)
{
return text.Contains("house") || text.Contains("flat");
}
}
// 对其余策略执行相同的操作...
如评论中所述,它并不是真正的工厂,因为它不会在每次调用时都创建新策略。它更像是一个提供程序,提供基于文件类型执行的策略。
public class StragetyProvider
{
private List<StrategyBase> _strategies = new List<StrategyBase>{ new HomeStrategy(), new OfficeStrategy() };
public StrategyBase GetStrategy(string fileType)
{
return _strategies.FirstOrDefault(s => s.CanProcess(fileType));
}
}
结果客户端代码变得简单多了:
public class Client
{
public void Execute()
{
var files = repository.GetFilesFromDisk();
var provider = new StragetyProvider();
foreach (var file in files)
{
var strategy = provider.GetStrategy(file.Type);
strategy?.Execute(file);
}
}
}
注意,当您需要添加新条件时,您只需实现一个派生自 StrategyBase
的新 class 并将其添加到提供程序中的策略列表中,无需进行其他更改。如果您需要为某些新文件类型执行不同的逻辑,您将创建新策略和 override
Execute 方法,仅此而已。
如果这确实看起来有点矫枉过正,并且您不需要使用新行为来扩展此解决方案,并且您唯一想要的就是能够添加新条件,那么请采用另一种方法。
public interface ISatisfyFileType
{
bool Satisfies(string fileType);
}
public class HomeCondition : ISatisfyFileType
{
public string FileType => ".txt";
public bool Satisfies(string text)
{
return text.Contains("house") || text.Contains("flat");
}
}
// the rest of conditions
综合所有条件
public class FileConditions
{
private List<ISatisfyFileType> _conditions = new List<ISatisfyFileType>{ new HomeStrategy(), new OfficeStrategy() };
public bool Satisfies(string fileType) =>
_conditions.Any(condition => condition.Satisfies(fileType));
}
而客户:
public class Client
{
public void Execute()
{
var files = repository.GetFilesFromDisk();
var fileTypeConditions = new FileConditions();
foreach (var file in files)
{
if (fileTypeConditions.Satisfies(file.ContentText))
{
service.SaveInDatabase(file);
}
}
}
}
如果您需要新条件而不触及客户端代码,这也有利于实施新条件并将其添加到 FileConditions
class。
在不输入学术定义的情况下,假设当您有将执行操作的客户端代码(上下文)时使用策略模式,并且可以以不同的方式(算法)实现此操作。例如:https://www.dofactory.com/net/strategy-design-pattern
使用何种策略(或算法)取决于一些输入条件的很多场合。这就是为什么有时将策略模式与工厂模式结合使用的原因。 Client 将输入条件传递给 Factory。然后工厂知道必须创建哪个策略。然后Client执行创建的Strategy的操作。
然而,我曾多次遇到在我看来恰恰相反的问题。要执行的操作总是相同的,但它只会根据一系列输入条件来执行。例如:
public interface IStrategy
{
string FileType { get; }
bool CanProcess(string text);
}
public class HomeStrategy : IStrategy
{
public string FileType => ".txt";
public bool CanProcess(string text)
{
return text.Contains("house") || text.Contains("flat");
}
}
public class OfficeStrategy : IStrategy
{
public string FileType => ".doc";
public bool CanProcess(string text)
{
return text.Contains("office") || text.Contains("work") || text.Contains("metting");
}
}
public class StragetyFactory
{
private List<IStrategy> _strategies = new List<IStrategy>{ new HomeStrategy(), new OfficeStrategy() };
public IStrategy CreateStrategy(string fileType)
{
return _strategies.Single(s => s.FileType == fileType);
}
}
现在客户端代码将从某个存储库获取文件并将文件保存在数据库中。就是这个操作,把文件存入数据库,只是看文件的类型和每个文件的具体情况。
public class Client
{
public void Execute()
{
var files = repository.GetFilesFromDisk();
var factory = new StragetyFactory();
foreach (var file in files)
{
var strategy = factory.CreateStrategy(file.Type);
if (strategy.CanProcess(file.ContentText))
{
service.SaveInDatabase(file);
}
}
}
}
我认为这是与策略模式不同的模式,我错了吗? (尽管我已经在上面的代码中调用了 Strategy,因为我在很多场合下都是这样)
如果这个问题与策略模式解决的问题不同,那么它是哪种模式?
不是真正的策略模式,因为 strategy pattern in Wikipedia 中的定义说:
In computer programming, the strategy pattern (also known as the policy pattern) is a behavioral software design pattern that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use.[1]
您不是在选择要在运行时执行的算法,您只是检查条件以查看文件类型是否满足条件,然后执行算法。
你希望这会改变吗?您是否需要它是可扩展的,以便将来如果您需要根据文件类型执行不同的代码,您可以做到 easily.
如果这些问题的答案是是,那么您可以保留策略并进行少量更改。
首先定义定义要执行的代码的基本策略class
public abstract class StrategyBase
{
public abstract bool CanProcess(string fileType);
public virtual void Execute(File file)
{
_service.SaveInDatabase(file);
}
}
您的策略更改为派生自 base
public class HomeStrategy : StrategyBase
{
public string FileType => ".txt";
public override bool CanProcess(string text)
{
return text.Contains("house") || text.Contains("flat");
}
}
// 对其余策略执行相同的操作...
如评论中所述,它并不是真正的工厂,因为它不会在每次调用时都创建新策略。它更像是一个提供程序,提供基于文件类型执行的策略。
public class StragetyProvider
{
private List<StrategyBase> _strategies = new List<StrategyBase>{ new HomeStrategy(), new OfficeStrategy() };
public StrategyBase GetStrategy(string fileType)
{
return _strategies.FirstOrDefault(s => s.CanProcess(fileType));
}
}
结果客户端代码变得简单多了:
public class Client
{
public void Execute()
{
var files = repository.GetFilesFromDisk();
var provider = new StragetyProvider();
foreach (var file in files)
{
var strategy = provider.GetStrategy(file.Type);
strategy?.Execute(file);
}
}
}
注意,当您需要添加新条件时,您只需实现一个派生自 StrategyBase
的新 class 并将其添加到提供程序中的策略列表中,无需进行其他更改。如果您需要为某些新文件类型执行不同的逻辑,您将创建新策略和 override
Execute 方法,仅此而已。
如果这确实看起来有点矫枉过正,并且您不需要使用新行为来扩展此解决方案,并且您唯一想要的就是能够添加新条件,那么请采用另一种方法。
public interface ISatisfyFileType
{
bool Satisfies(string fileType);
}
public class HomeCondition : ISatisfyFileType
{
public string FileType => ".txt";
public bool Satisfies(string text)
{
return text.Contains("house") || text.Contains("flat");
}
}
// the rest of conditions
综合所有条件
public class FileConditions
{
private List<ISatisfyFileType> _conditions = new List<ISatisfyFileType>{ new HomeStrategy(), new OfficeStrategy() };
public bool Satisfies(string fileType) =>
_conditions.Any(condition => condition.Satisfies(fileType));
}
而客户:
public class Client
{
public void Execute()
{
var files = repository.GetFilesFromDisk();
var fileTypeConditions = new FileConditions();
foreach (var file in files)
{
if (fileTypeConditions.Satisfies(file.ContentText))
{
service.SaveInDatabase(file);
}
}
}
}
如果您需要新条件而不触及客户端代码,这也有利于实施新条件并将其添加到 FileConditions
class。