我尝试将策略设计模式应用于某些文本内容的解析,其中每个结果在不同的类中表示。
最小示例。
所以我的界面看起来像这样:
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);
带有黄色标记的推断类型:
简单的答案是您无法解决这个问题:
所以,我的问题是客户端必须知道这些类才能转换返回类型对象。
给出的任何答案,用户都需要知道将要返回的对象的类型,只有在知道运行时或上下文类的内部实现之后,上下文类的用户才能知道该类型。 Context
类。
但是,这种类型的代码不适合作为接口保证唯一的对象是返回对象。
[如果Context
的用户必须将选项传递给函数TypeToParse
,则上下文类的用户最好具有2个函数,它们返回正确的类型,例如
class Context
{
public ParseAData ParseAsA(string filePath)
{
...
}
public ParseBData ParseAsB(string filePath)
{
...
}
}
您正在以错误的方式实施策略模式。请重新阅读有关策略模式的内容,并尝试了解如何使用它。我将尝试在这里指出问题。想法是根据输入或状态(最重要的部分,输入或输出的类型不变)在代码库中注入逻辑。
问题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);
}
Info策略模式仅在使用策略的类没有对依赖项进行硬编码(即其全部思想)时有效。
您拥有的示例可能不是策略模式的好例子。我试图尽可能地修复它,以便您对实现中的错误有一个很好的了解。并非所有模式都支持所有方案。这是策略模式的示例实现,与您的https://refactoring.guru/design-patterns/strategy/csharp/example非常相似。
我希望这会有所帮助。
注我提供的代码不起作用。他们甚至可能不会编译,只是在那里表达策略模式背后的想法。对于每个类别ParseA
和ParseB
],正确的实现将具有不同的代码
更多
策略模式与IoC(控制反演)齐头并进。尝试学习IoC,您会发现更容易学习策略模式。 https://en.wikipedia.org/wiki/Inversion_of_control