策略模式还是无策略模式?

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。