战略模式还是没有战略模式?

时间:2020-01-15 07:16:15

标签: c# design-patterns strategy-pattern

不用输入学术定义,就可以说,当您具有执行操作的客户代码(上下文)时,可以使用“策略模式”,并且可以用不同的方式(算法)来实现此操作。例如:https://www.dofactory.com/net/strategy-design-pattern

根据某些输入条件的许多情况,将使用哪种策略(或算法)。这就是为什么有时将策略模式与工厂模式结合使用的原因。客户将输入条件传递给工厂。然后,工厂知道必须创建哪个策略。然后,客户端执行所创建策略的操作。

但是,我几次遇到一个问题,在我看来似乎是相反的。要执行的操作始终是相同的,但是只能根据一系列输入条件来执行。例如:

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,是因为我在某些场合似乎都这样)

如果此问题与策略模式所解决的问题不同,那么它是哪个模式?

1 个答案:

答案 0 :(得分:1)

这实际上不是一种策略模式,因为strategy pattern in Wikipedia中的定义是:

在计算机编程中,策略模式(也称为 策略模式)是一种行为软件设计模式 在运行时选择算法。而不是实施一个 直接使用算法,代码会收到有关 一族要使用的算法。[1]

您没有选择要在运行时执行的算法,只是检查条件以查看文件类型是否满足条件,然后执行该算法。

您希望这种情况会改变吗?您是否需要将其扩展,以便将来如果您需要根据文件类型执行不同的代码,可以轻松

如果对这些问题的回答是,那么您可以保留策略并进行少量更改。

首先定义基本策略类,该类定义要执行的代码

public abstract class StrategyBase
{
   public abstract bool CanProcess(string fileType);
   public virtual void Execute(File file)
   {
        _service.SaveInDatabase(file);
   }
}

您的策略会发生变化,以从基础中获取

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派生的新类,并将其添加到提供程序中的策略列表中,而无需进行其他更改。如果您需要为某种新文件类型执行不同的逻辑,则可以创建新策略和override Execute 方法。


如果这确实看起来像是一种矫kill过正,并且您不需要使用新的行为来扩展此解决方案,那么您唯一想要的就是能够添加新的条件,然后采用另一种方法。

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类中的好处,如果您需要新条件而不用触摸客户端代码。

相关问题