如何设计包含大量IF或Switch语句的方法

时间:2010-12-14 15:46:14

标签: c#

如何让这个类的设计更具动态性,以便我可以根据需要添加新的扩展和类型。

public class Processor
{
    public Processor(string fileName)
    {
        string extension = Path.GetExtension(fileName);
        if(extension == "jpg" || extension == "gif" || extension == "png")
        {
            //Process Image file type
        } 
        else if(extension == "xls" || extension == "xlsx")
        {
            //Process spreadsheet type
        }
        else if(extension == "doc" || extension == "docx")
        {
            //Process document file type
        }       
        //and so forth ...
    }   
}

我们将来可能需要处理.tiff文件,或者我们可能需要处理视频文件,这意味着新的if分支

else if(extension == "avi" || extension == "mp4")
{
    //Process video file type
}

如你所见,这可能会很长。

有效的文件类型和组存储在DB ...

任何人都可以推荐任何模式或聪明的想法来解决这个问题吗? 干杯

6 个答案:

答案 0 :(得分:4)

使用字典。

Dictionary<string, IFileHandler> fileHandlers = new Dictionary<string, IFileHandler>
{
    { "jpg", imageHander },
    { "gif", imageHander },
    { "xls", spreadsheetHander },
    // ...
};

然后按如下方式使用:

public void Process(string fileName)
{
    string extension = Path.GetExtension(fileName);

    // TODO: What should happen if the filetype is unknown?
    fileHandlers[extension].Process(fileName); 
}

  

有效的文件类型和组存储在DB ...

然后,您可能希望查询数据库以从扩展中获取正确的组,并将该组用作字典键而不是扩展名。

答案 1 :(得分:3)

如果由于您声明的原因(主要是必须返回并添加代码分支)而预期会扩展此结构通常是一件坏事。

我将它实现为一个Dictionary,键入文件扩展名字符串,并包含方法委托作为值。如果设计得当,那将允许您添加新的扩展而不会影响旧代码,因为新代码将采用完全不同的方法,甚至可以存在于具有此逻辑的类之外。然后,只需查找正确的委托即可通过文件扩展名调用。

基本示例:

//Put this in your class somewhere
public readonly Dictionary<string, Action<FileInfo>> fileHandlers = new Dictionary<string, Action<FileInfo>>();

...

//depending on the dictionary's visibility, you can add these from pretty much anywhere
fileHandlers.Add("xls", ProcessExcelFile);
fileHandlers.Add("xlsx", ProcessExcelFile);
fileHandlers.Add("jpg", ProcessImageFile);

...

//Then all you have to do to invoke the logic is...
fileHandlers[extension](fileInfo);

答案 2 :(得分:3)

我建议为各种文件处理器提供一个接口来实现:

public interface IFileProcessor
{
    void Process(string fileName);
    IEnumerable<string> FileExtensions {get;}
}

然后使用工厂为特定文件名获取适当的处理器。和其他的回答者一样,我建议你使用Dictionary来保存处理器及其文件名的映射。此示例使用反射来注册它在加载的程序集中找到的每个文件处理器。这很符合您的可扩展性要求,因为您需要的只是一些已加载的程序集具有实现此接口的类,并且它将自动注册到工厂。但是,您可以提供所需的任何系统来注册这些类型:

public class ProcessorFactory
{
    static IDictionary<string, IFileProcessor> ProcessorsByExtension = 
        new Dictionary<string, IFileProcessor>();

    static ProcessorFactory() 
    {
        var processorTypes = 
            from a in AppDomain.CurrentDomain.GetAssemblies()
            from t in a.GetTypes()
            where typeof(IFileProcessor).IsAssignableFrom(t)
            select t;
        foreach(var t in processorTypes)
        {
            // Preferably use your DI framework to generate this.
            var processor = (IFileProcessor)Activator.CreateInstance(t);
            foreach(var ext in processor.FileExtensions)
            {
                if(ProcessorsByExtension.ContainsKey(ext))
                {
                    throw new InvalidOperationException(
                        "Multiple processors are registered to extension " + ext);
                }
                ProcessorsByExtension[ext] = processor;
            }
        }
    }

    public IFileProcessor GetProcessorForFile(string fileName)
    {
        string extension = Path.GetExtension(fileName);
        return ProcessorsByExtension[extension];
    }   
}

文件处理器接口的典型实现可能如下所示:

public class ImageFileProcessor : IFileProcessor
{
    public IEnumerable<string> FileExtensions 
    {
        get {return new[]{"jpg", "gif", "png"};}
    }
    public void Process(string fileName) 
    {
        // Process Image file type
    }
}

这种方法可以使您的代码保持模块化和可扩展性,同时即使您拥有庞大的文件处理器列表也能提供出色的性能。

答案 3 :(得分:1)

一种方法可能是构建一个表(列表),其中每个项目都包含扩展名以及有关如何处理具有该扩展名的名称的信息。

然后,您可以编写一个简单的循环,与表中的每个扩展进行比较。

如果您对每个扩展程序执行的操作很复杂且无法在简单表中定义,则可以包含委托。当循环找到匹配的扩展名时,它可以调用相应的委托来处理名称。

答案 4 :(得分:1)

这种重构有一个规则 -

  • 用多态性/策略替换条件。 if / switch的明显问题是很可能在这样的代码中出错,并且很难维护或增强。

考虑相同和“开放封闭的校长”( - 课程应该开放以进行扩展,但是因修改而关闭。)

我建议使用以下代码。

public class Processor
    {
        private Dictionary<string,FileParserBase> _fileExtension2FileParser;

        public Processor() {
            _fileExtension2FileParser = new Dictionary<string, FileParserBase>();

            AddParser(new DocExtensionWordParser());
            AddParser(new DocXExtensionWordParser());
            //..more,more
        }

        private void AddParser(FileParserBase fileParserBase) {

            _fileExtension2FileParser.Add(fileParserBase.Extension, fileParserBase);
        }


    public void Process(string fileName)
    {
        string extension = Path.GetExtension(fileName);
        FileParserBase fileParser;
        if (_fileExtension2FileParser.TryGetValue(extension, out fileParser)) {
            fileParser.Process(fileName);
        }

    }
    }

    public interface FileParserBase
    {
        string Extension { get; }
        void Process(string filePath);
    }

    public abstract class WordParserBase : FileParserBase
    {
        private string _extension;

        public WordParserBase(string extension)
        {
            _extension = extension;
        }

        public override void Process(string filePath)
        {
            //Do the processing for WORD Document
        }

        public override string Extension
        {
            get { return _extension; }
        }
    }

    public class DocExtensionWordParser : WordParserBase
    {

        public DocExtensionWordParser():base("doc"){}
    }

    public class DocXExtensionWordParser : WordParserBase
    {

        public DocXExtensionWordParser() : base("docx") { }
    }

答案 5 :(得分:0)

对于这个特殊情况,我会构建像

这样的东西
List<string> excelExtensions = new List<string>(){ "xls", "xlsx" };
List<string> wordExtensions = new List<string>(){ "doc", "docx" };

if(excelExtensions.Contains(extension))
{
}

if(wordExtensions.Contains(extension))
{
}

等?