簡體   English   中英

不同返回類型的 C# 策略設計模式

[英]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非常相似。

我希望這有幫助。

注意我提供的代碼不是工作版本。 他們甚至可能不會編譯,他們只是在那里表達策略模式背后的想法。 正確的實現將為ParseAParseB每個類提供不同的代碼

更多策略模式和 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM