不同收益类型的C#策略设计模式

时间:2020-03-08 12:43:34

标签: c# strategy-pattern

我尝试将策略设计模式应用于某些文本内容的解析,其中每个结果在不同的类中表示。

最小的例子。

所以我的界面如下:

public interface IParseStrategy
{
    object Parse(string filePath);
}

实现算法的类:

class ParseA : IParseStrategy
{
    public object Parse(string filePath) => new ConcurrentQueue<ParseAData>();
}

class ParseB : IParseStrategy
{
    public object Parse(string filePath) => new Dictionary<string, ParseBData>();
}

特定的“数据”类:

class ParseAData
{
    public int Id { get; set; }
    public string Name { get; set; }

}

class ParseBData
{
    private byte[] data;

    public byte[] GetData()
    {
        return data;
    }

    public void SetData(byte[] value)
    {
        data = value;
    }
}

为客户定义兴趣界面的上下文类:

class Context
{
    private IParseStrategy _strategy;

    private void SetParsingStrategy(IParseStrategy parseStrategy)
    {
        _strategy = parseStrategy;
    }

    public object TryParse(string filePath, TypeToParse typeToParse)
    {
        object parsedContent = new object();

        try
        {
            switch (typeToParse)
            {
                case TypeToParse.A:
                    SetParsingStrategy(new ParseA());
                    parsedContent = _strategy.Parse(filePath);
                    break;
                case TypeToParse.B:
                    SetParsingStrategy(new ParseB());
                    parsedContent = _strategy.Parse(filePath);
                    break;
                    throw new ArgumentOutOfRangeException(nameof(typeToParse), "Uknown type to parse has been provided!");
            }
        }
        catch (Exception)
        {

            throw;
        }

        return parsedContent;
    }

}

客户端可以在其中选择正确算法的枚举

public enum TypeToParse { A, B }

最后是主要方法:

static void Main(string[] args)
{
    var context = new Context();
    ConcurrentQueue<ParseAData> contentOfA = (ConcurrentQueue<ParseAData>)context.TryParse("file1.whatever", TypeToParse.A);
    Dictionary<string, ParseBData>contentOfB = (Dictionary<string, ParseBData>)context.TryParse("file2.whatever", TypeToParse.B);
}

所以,我的问题是客户端必须知道这些类才能转换返回类型object

如何将其重写为更通用的方式,以便编译器将通过使用var关键字自动推断类型,因此调用将类似于:

        var contentOfA = context.TryParse("file1.whatever", TypeToParse.A);
        var contentOfB = context.TryParse("file2.whatever", TypeToParse.B);

带有黄色标记的推断类型:

enter image description here

4 个答案:

答案 0 :(得分:2)

您正在以错误的方式实施策略模式。请重新阅读有关策略模式的内容,并尝试了解如何使用它。我将尝试在这里指出问题。想法是根据输入或状态(最重要的部分,输入或输出的类型不变)在代码库中注入逻辑。

问题1 ,上下文永远不应该知道有多种可用策略。仅知道它具有策略的实现,并且必须使用该策略来执行某些操作并返回结果。

因此,上下文类

public object TryParse(string filePath, TypeToParse typeToParse)
{
    object parsedContent = new object();

    try
    {
        switch (typeToParse)
        {
            case TypeToParse.A:
                SetParsingStrategy(new ParseA());...
                break;
            case TypeToParse.B:
                SetParsingStrategy(new ParseB());...
                break;
        }
    }
    ....
}

违反了此规定。它有一个开关柜,它知道类型,这是不可能的。正确的实现应类似于-

public object TryParse(string filePath, TypeToParse typeToParse)
{
    object parsedContent = _this._stategy.Parse(filePath); //it should never know which implementation is supplied, in other words wich strategy is applied. Only at runtime t will be decided.
}

问题2 ,该策略的两类实现具有这样的实现-

class ParseA : IParseStrategy
{
    public object Parse(string filePath) => new ConcurrentQueue<ParseAData>();
}

class ParseB : IParseStrategy
{
    public object Parse(string filePath) => new Dictionary<string, ParseBData>();
}

这也是违规行为。你为什么问?因为调用各个类的代码必须知道它们返回的内容。策略模式是一种模式,它并不关心c#是否支持它。 object是特定于C#的好东西,可用于类型转换任何对象。但这并不意味着使用对象可以解决所有问题。即使返回类型相同(object),实际的基础对象也不相同,因此这不能成为策略模式的实现。这些策略是动态注入的,因此没有人应该对此有硬编码的依赖性。一种可能的实现方式可能是-

interface IData
{
}

class ParseAData : IData
{
    public int Id { get; set; }
    public string Name { get; set; }

}

class ParseBData : IData
{
    private byte[] data;

    public byte[] GetData()
    {
        return data;
    }

    public void SetData(byte[] value)
    {
        data = value;
    }
}

public interface IParsedObject 
{
    void process(<Some other dependencies>);
}

public class ConcurrentQueue<T> : IParsedObject where T: ParseAData
{

}

public class ParsedDictionary<T> : IParsedObject where T: ParseBData
{

}


public interface IParseStrategy
{
    IParsedObject Parse(string filePath);
}

//the method will be somesiliar to this 
public IParsedObject TryParse(string filePath, TypeToParse typeToParse)
{
    IParsedObject parsedContent = _this._stategy.Parse(filePath); //it should never know which implementation is supplied, in other words wich strategy is applied. Only at runtime t will be decided.
}

class ParseA : IParseStrategy
{
    public IParsedObject Parse(string filePath) => new ConcurrentQueue<ParseAData>();
}

class ParseB : IParseStrategy
{
    public IParsedObject Parse(string filePath) => new Dictionary<string, ParseBData>();
}

通过这些修改,您现在可以编写-

static void Main(string[] args)
{
    var contextA = new Context();
    contentA.SetParsingStrategy(new ParseA());


    var contextB = new Context();
    contextB.SetParsingStrategy(new ParseB());

    var contentOfA = contextA.TryParse("file1.whatever", TypeToParse.A);
    var contentOfB = contextB.TryParse("file2.whatever", TypeToParse.B);
}

static void Main(string[] args)
{
    var context = new Context();
    contentA.SetParsingStrategy(new ParseA());
    var contentOfA = context.TryParse("file1.whatever", TypeToParse.A);

    context.SetParsingStrategy(new ParseB());
    var contentOfB = context.TryParse("file2.whatever", TypeToParse.B);
}

信息策略模式仅在使用策略的类没有对依赖项进行硬编码(即整个构想)时有效。

您所举的例子可能不是战略模式的好例子。我试图尽可能地修复它,以使您对实现中的错误有一个很好的了解。并非所有模式都支持所有方案。这是策略模式的示例实现,与您的https://refactoring.guru/design-patterns/strategy/csharp/example非常相似。

我希望这会有所帮助。

注意我提供的代码不起作用。他们可能甚至不会编译,只是在那里表达策略模式背后的想法。对于每个ParseAParseB

类,正确的实现将具有不同的代码。

更多策略模式与IoC(Cntrol的倒置)并驾齐驱。尝试学习IoC,您会发现策略模式更容易学习。 https://en.wikipedia.org/wiki/Inversion_of_control

答案 1 :(得分:1)

简单的答案是您无法解决这个问题:

所以,我的问题是客户端必须知道这些类才能转换返回类型的对象。

给出的任何答案都需要用户知道将要返回的对象的类型,只有在知道了内部实现的情况下,运行时或上下文类的用户才可能知道该类型。 Context类。

但是,这种类型的代码不适合作为您界面保证的唯一对象是返回对象。

如果Context的用户必须将选项传递给函数TypeToParse,则上下文类的用户最好具有2个函数,它们返回正确的类型,例如

class Context
{
    public ParseAData ParseAsA(string filePath)
    {
        ...
    }

    public ParseBData ParseAsB(string filePath)
    {
        ...
    }
}

答案 2 :(得分:0)

您好,我认为您误解了策略模式。在此线程中了解有关如何解决问题的更多信息:https://social.msdn.microsoft.com/Forums/en-US/2bbef57c-4172-48a1-b683-faf779d6a415/strategy-pattern-with-specific-return-types?forum=architecturegeneral

答案 3 :(得分:0)

我认为,根据这里的答案,我设法解决了这个问题。因此,这就是我要搜索的实现:

固定的上下文类

class Context
    {
        private IParseStrategy _strategy;

        public void SetParsingStrategy(IParseStrategy parseStrategy)
        {
            _strategy = parseStrategy;
        }

        public T TryParse<T>(string filePath)
        {
            T parsedContent = (T)_strategy.Parse(filePath);

            return parsedContent;
        }

主要

        var context = new Context();

        // parse A first
        context.SetParsingStrategy(new ParseA());
        var contentOfA = context.TryParse<ConcurrentQueue<ParseAData>>("file1.whatever");

        // parse B second
        context.SetParsingStrategy(new ParseB());
        var contentOfB = context.TryParse<Dictionary<string, ParseBData>>("file2.whatever");

现在,编译器可以推断出类型:

enter image description here

我认为,除此之外,我还必须在 TryParse 方法中添加一些禁忌素...