[英]C# strategy design pattern for different return types
我嘗試應用策略設計模式來解析一些文本內容,其中每個結果在不同的類中表示。
最小的例子。
所以我的界面是這樣的:
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);
推斷出黃色標記的類型:
您正在以錯誤的方式實施策略模式。 請再次閱讀有關策略模式的內容,並嘗試了解如何使用它。 我將嘗試在這里指出問題。 這個想法是根據輸入或狀態在代碼庫中注入邏輯,最重要的部分是輸入或輸出的類型不會改變。
問題 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;
}
}
....
}
是違反了這一點。 它有一個知道類型的 switch-case,這是不可能的。 一個正確的實現應該是這樣的 -
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
是 ac# 特定的好東西,您可以使用它來對任何對象進行類型轉換。 但這並不意味着使用對象可以解決所有問題。 即使返回類型相同( 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非常相似。
我希望這有幫助。
注意我提供的代碼不是工作版本。 他們甚至可能不會編譯,他們只是在那里表達策略模式背后的想法。 正確的實現將為ParseA
和ParseB
每個類提供不同的代碼
更多策略模式和 IoC,(控制反轉)齊頭並進。 嘗試學習 IoC,你會發現策略模式更容易學習。 https://en.wikipedia.org/wiki/Inversion_of_control
簡單的答案是你不能,解決這個問題:
所以,我的問題是客戶端必須知道類才能轉換返回類型對象。
給用戶的任何答案都需要知道將要返回的對象的類型,只有在了解Context
類的內部實現的情況下, Context
類的用戶才會知道這要到運行時或可能之前.
然而,這種類型的代碼並不適合,因為您的接口唯一保證的是返回一個對象。
如果Context
用戶必須將選項傳遞給函數TypeToParse
上下文類的用戶最好有 2 個返回正確類型的函數,例如
class Context
{
public ParseAData ParseAsA(string filePath)
{
...
}
public ParseBData ParseAsB(string filePath)
{
...
}
}
您好,我認為您誤解了策略模式。 在此線程中閱讀有關如何解決您的問題的更多信息: https : //social.msdn.microsoft.com/Forums/en-US/2bbef57c-4172-48a1-b683-faf779d6a415/strategy-pattern-with-specific-return-類型?論壇=architecturegeneral
根據這里的答案,我認為我設法解決了這個問題。 所以,這是我正在尋找的實現:
固定上下文類
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");
現在編譯器可以推斷類型:
除此之外,我認為我必須對TryParse方法進行一些限制......
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.