65.9K
CodeProject 正在变化。 阅读更多。
Home

重构 Switch 语句(第二版)

starIconstarIconstarIconemptyStarIconemptyStarIcon

3.00/5 (3投票s)

2009年6月24日

CPOL

2分钟阅读

viewsIcon

23061

如何重构 Switch 语句

距离我上一篇文章已经过去很长时间了。最近在 Sansir 发生了很多有趣的事情,占据了我所有的空闲时间,而且我是一个重视家庭的人,所以很难经常发表文章。我对此并不抱怨,一点也不,我感到“快乐”。

好了,停止抱怨吧。

在之前的文章中,我向你展示了如何重构一段代码,这段代码根据 switch 语句的评估结果来决定执行哪个方法;你可以在 这里 阅读关于它的内容。

作为最后的说明,我在文章中提到,所呈现的代码还可以进一步重构,因为如果你仔细观察,我们只是将维护噩梦转移到了程序中的另一个地方,但原始问题仍然存在。

那么该怎么办?我会这样做

就这样,有了我们新的方法,我们可以继续添加 NewsDisplayer 并将其用于我们的程序,而无需触碰程序的其他部分;通过向我们的程序集添加更多类,或者将程序集放入 bin 目录中来实现(这不就是插件模型吗?)。

  1. 为将要处理的每个运动创建特定的类;这些类必须实现一个简单的 interface,并且需要用一个属性进行装饰,该属性指定它将处理的目标运动。例如
    public interface INewsDisplayer
    {
        void Display();
    }
    
    public class NewsDisplayerDefinitionAttribute : Attribute 
    { 
        public NewsDisplayerDefinitionAttribute(Sports sport) 
        { 
            Sport = sport; 
        } 
        public Sports Sport { get; private set; } 
    }

    一个实现如下所示

    [NewsDisplayerDefinition(Sports.Soccer)]
    public class SoccerNewsDisplayer : INewsDisplayer 
    { 
    #region INewsDisplayer Members 
        public void Display() 
        { 
            Console.WriteLine("Displaying News for Soccer"); 
            // Real implementation below 
            // Do something 
        } 
    #endregion 
    }
  2. 使用 工厂模式,创建一个类,该类接受一个 Sport 参数并返回一个 IEnumerable<INewsDisplayer> 实例。与我们之前的尝试的一个区别在于,我们将能够为给定的 Sport 定义多个实现。这个工厂类将扫描当前 AppDomain 中的所有程序集,如果它找到一个遵循我之前描述的模式的类型,它将读取元数据并将该类型的实例附加到一个字典中,该字典将作为我们的查找表,以返回请求的 Sport 的正确实例。这个工厂类如下所示
    public static class NewsDisplayerFactory 
    { 
        private static readonly IDictionary<Sports, IEnumerable<INewsDisplayer>> 
          lookupTable = new Dictionary<Sports, IEnumerable<INewsDisplayer>>(); 
        static NewsDisplayerFactory() 
        { 
            BootStrap(); 
        }
    
        private static void BootStrap() 
        { 
            // Find all types in all the assemblies from the current appdomain 
            // that have a correct implementation of 
            // News Displayer (NewsDisplayerDefinitionAttribute + INewsDisplayer) 
            var interfaceFullName = typeof (INewsDisplayer).FullName; 
            var newsDisplayerAttributeType = typeof (NewsDisplayerDefinitionAttribute); 
            var result = from 
                assembly in AppDomain.CurrentDomain.GetAssemblies() 
                from 
                type in assembly.GetTypes() 
                let 
                definition = type 
                .GetCustomAttributes(newsDisplayerAttributeType, true) 
                .Cast<NewsDisplayerDefinitionAttribute>() 
                .FirstOrDefault() 
                where 
                type.GetInterface(interfaceFullName) != null && definition != null 
                group 
                (INewsDisplayer) Activator.CreateInstance(type) 
                by definition.Sport; 
    
            // Filling the dictionary 
            foreach (var item in result) 
            { 
                lookupTable.Add(item.Key, item.ToList()); 
            } 
        } 
    
        public static IEnumerable<INewsDisplayer> GetInstance(Sports sport) 
        { 
            IEnumerable<INewsDisplayer> newsDisplayer; 
            if (lookupTable.TryGetValue(sport, out newsDisplayer)) 
            { 
                return newsDisplayer; 
            } 
            else 
            { 
                throw new NotImplementedException(string.Format(
                  "The method for the sport {0} is not implemented", sport)); 
            } 
        } 
    }
  3. 使用 策略模式,我们将更改我们的 SportNews 类,通过传递要处理的 Sport 类型来更改其上下文。这将如下所示
    public class SportNews 
    { 
        private IEnumerable<INewsDisplayer> newsDisplayer; 
    
        public void SetContext(Sports sport) 
        { 
            newsDisplayer = NewsDisplayerFactory.GetInstance(sport); 
        } 
    
        public void DisplayNews() 
        { 
            foreach (var displayer in newsDisplayer) 
            { 
                displayer.Display(); 
            } 
        } 
    }

最后,我想展示我们的 Program 类,其中使用每个组件来执行该程序的原始想法

class Program 
{ 
    private static string options = null; 
    private static readonly Type sportsEnumType = typeof(Sports); 
    static void Main(string[] args) 
    { 
        var news = new SportNews(); 
        while (true) 
        { 
            Console.WriteLine(); 
            DisplayOptions(); 
            var key = Console.ReadKey().KeyChar.ToString(); 
            Console.WriteLine(); 
            try 
            { 
                var sport = (Sports)(Enum.Parse(sportsEnumType, key)); 
                news.SetContext(sport); 
                news.DisplayNews(); 
            } 
            catch (Exception ex) 
            { 
                Console.WriteLine(ex); 
                Console.ReadLine(); 
                return; 
            } 
        } 
    } 
    private static void DisplayOptions() 
    { 
        if (options == null) 
        { 
            StringBuilder optionsBuilder = new StringBuilder(); 
            FieldInfo[] enumFields = sportsEnumType.UnderlyingSystemType.GetFields(
                                     BindingFlags.Public | BindingFlags.Static); 
            foreach (FieldInfo enumField in enumFields) 
            { 
                object enumValue = enumField.GetRawConstantValue(); 
                Sports sport = (Sports)(enumValue); 
                optionsBuilder.AppendFormat("To display the news for {0} press {1}\n", 
                                            sport, enumValue); 
            } 
            options = optionsBuilder.ToString(); 
        } 
        Console.WriteLine(options); 
    } 
}

你可以从 这里 下载可用的示例。

祝你编码愉快,下次再见。

无耻的推广:你可以在我的博客 这里 查看这篇文章。

© . All rights reserved.