【问题标题】:selecting correct list of Enums from 2 derived classes从 2 个派生类中选择正确的枚举列表
【发布时间】:2012-11-19 15:31:54
【问题描述】:

我正在构建一个图书库应用程序,我有一个抽象图书类、两种类型的派生图书和两个将保存图书类型的枚举。 每本书都可以与一种或多种类型相关。

    abstract public class Book
    {   
       public int Price { get; set; } 
       ...
    }

    public enum ReadingBooksGenre
    {
       Fiction,
       NonFiction
    }

    public enum TextBooksGenre
    {
        Math,
        Science
    } 

    abstract public class ReadingBook : Book
    { 
        public List<ReadingBooksGenre> Genres { get; set; }
    }

    abstract public class TextBook : Book
    {   
        public List<TextBooksGenre> Genres { get; set; }
    }

现在我想根据书籍类型保存折扣(没有双重折扣,只计算最高折扣),所以我正在考虑制作两个字典来保存每种类型的所有折扣,如​​下所示:

    Dictionary<ReadingBooksGenre, int> _readingBooksDiscounts;
    Dictionary<TextBooksGenre, int> _textBooksDiscounts;

所以现在我需要检查每本书的类型才能找到最高的折扣,有没有比这更好的方法:

    private int GetDiscount(Book b)
    {
        int maxDiscount = 0;
        if (b is ReadingBook)
        {
            foreach (var genre in (b as ReadingBook).Genres)
            {
                // checking if the genre is in discount, and if its bigger than other discounts.
                if (_readingBooksDiscounts.ContainsKey(genre) && _readingBooksDiscounts[genere]>maxDiscount)
                {
                    maxDiscount = _readingBooksDiscounts[genere];
                }
            }
        }
        else if (b is TextBook)
        {
            foreach (var genre in (b as TextBook).Genres)
            {
                if (_textBooksDiscounts.ContainsKey(genre) && _textBooksDiscounts[genere]>maxDiscount)
                {
                    maxDiscount = _textBooksDiscounts[genere];
                }
            }
        }
        return maxDiscount;
    }

有没有办法在不检查类型的情况下选择正确的字典? 或者甚至是一种不用字典或使用字典的方法? 也许以某种方式将书籍类型与枚举联系起来?

很高兴听到任何改进建议。

(根据书籍名称、日期和作者,还有很多折扣。甚至更多的书籍类型,这就是为什么这种方式对我来说似乎不合适的原因)

谢谢。

【问题讨论】:

  • 我有点怀疑教科书不是非小说的子集的模型。虽然我认为必须考虑到 J.K.罗琳的 Quidditch Through The Ages...

标签: c# dictionary enums modeling


【解决方案1】:

我会创建一个通用方法来接受相应类型的字典和书籍。这样,您可以获得足够通用的算法并且您的代码非常干净。自然地,这样 GetDiscount 也是通用的,但您不能以错误的方式混合它们。 (哦,是的,Book 也是通用的,类型返回正确的类型。) 我认为这段代码可以用一点 LINQ 来实现,但可能不值得付出额外的努力。

【讨论】:

    【解决方案2】:

    您的GetDiscount 方法是Open/Closed principle 违规的经典示例。添加新书类型时,您必须将新的if 块添加到GetDiscount

    更好的方法是使用一些现有的技术,这些技术允许您添加新功能而无需修改现有代码。例如,Composite pattern。我将编写一些复合折扣评估器的实现草案。您可以根据任何图书属性(日期、价格等)轻松添加新的折扣评估器。

    另外,我将使用接口而不是继承。继承是两个实体之间非常紧密的联系,在这种情况下它是过度的。

    列表有 167 行,所以这里更舒适pastebin copy

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main()
            {
                var compositeDiscountEvaluator = ConfigureEvaluator();
                var scienceBook = new TextBook
                                   {
                                       Date = DateTime.Now,
                                       Price = 100,
                                       Genres = new[] {TextBooksGenre.Math}
                                   };
                var textBook = new TextBook
                                   {
                                       Date = DateTime.Now,
                                       Price = 100,
                                       Genres = new[] {TextBooksGenre.Math, TextBooksGenre.Science}
                                   };
                var fictionBook = new ReadingBook
                            {
                                Date = DateTime.Now,
                                Price = 200,
                                Genres = new[] {ReadingBooksGenre.Fiction}
                            };
                var readingBook = new ReadingBook
                                      {
                                          Date = DateTime.Now,
                                          Price = 300,
                                          Genres = new[] {ReadingBooksGenre.Fiction, ReadingBooksGenre.NonFiction}
                                      };
                Console.WriteLine(compositeDiscountEvaluator.GetDiscount(scienceBook));
                Console.WriteLine(compositeDiscountEvaluator.GetDiscount(textBook));
                Console.WriteLine(compositeDiscountEvaluator.GetDiscount(fictionBook));
                Console.WriteLine(compositeDiscountEvaluator.GetDiscount(readingBook));
            }
    
            private static IDiscountEvaluator ConfigureEvaluator()
            {
                var evaluator = new CompositeDiscountEvaluator();
                evaluator.AddEvaluator(new ReadingBookDiscountEvaluator());
                evaluator.AddEvaluator(new TextBookDiscountEvaluator());
                return evaluator;
            }
        }
    
        class CompositeDiscountEvaluator : IDiscountEvaluator
        {
            private readonly ICollection<IDiscountEvaluator> evaluators;
    
            public CompositeDiscountEvaluator()
            {
                evaluators = new List<IDiscountEvaluator>();
            }
    
            public void AddEvaluator(IDiscountEvaluator evaluator)
            {
                evaluators.Add(evaluator);
            }
    
            public bool CanEvaluate<TGenre>(IBook<TGenre> book)
            {
                return evaluators.Any(e => e.CanEvaluate(book));
            }
    
            public int GetDiscount<TGenre>(IBook<TGenre> book)
            {
                if (!CanEvaluate(book))
                    throw new ArgumentException("No suitable evaluator");
                return evaluators.Where(e => e.CanEvaluate(book)).Select(e => e.GetDiscount(book)).Max();
            }
        }
    
        interface IDiscountEvaluator
        {
            bool CanEvaluate<TGenre>(IBook<TGenre> book);
            int GetDiscount<TGenre>(IBook<TGenre> book);
        }
    
        class ReadingBookDiscountEvaluator : IDiscountEvaluator
        {
            private readonly IDictionary<ReadingBooksGenre, int> discounts;
    
            public ReadingBookDiscountEvaluator()
            {
                discounts = new Dictionary<ReadingBooksGenre, int>
                                {
                                    {ReadingBooksGenre.Fiction, 3},
                                    {ReadingBooksGenre.NonFiction, 4}
                                };
            }
    
            public bool CanEvaluate<TGenre>(IBook<TGenre> book)
            {
                return book is ReadingBook;
            }
    
            public int GetDiscount<TGenre>(IBook<TGenre> book)
            {
                var readingBook = (ReadingBook) book;
                return readingBook.Genres.Select(g => discounts[g]).Max();
            }
        }
    
        class TextBookDiscountEvaluator : IDiscountEvaluator
        {
            private readonly IDictionary<TextBooksGenre, int> discounts;
    
            public TextBookDiscountEvaluator()
            {
                discounts = new Dictionary<TextBooksGenre, int>
                                {
                                    {TextBooksGenre.Math, 1},
                                    {TextBooksGenre.Science, 2}
                                };
            }
    
            public bool CanEvaluate<TGenre>(IBook<TGenre> book)
            {
                return book is TextBook;
            }
    
            public int GetDiscount<TGenre>(IBook<TGenre> book)
            {
                var textBook = (TextBook) book;
                return textBook.Genres.Select(g => discounts[g]).Max();
            }
        }
    
        interface IBook<TGenre>
        {
            int Price { get; set; }
            DateTime Date { get; set; }
            TGenre[] Genres { get; set; }
        }
    
        class ReadingBook : IBook<ReadingBooksGenre>
        {
            public int Price { get; set; }
            public DateTime Date { get; set; }
            public ReadingBooksGenre[] Genres { get; set; }
        }
    
        class TextBook : IBook<TextBooksGenre>
        {
            public int Price { get; set; }
            public DateTime Date { get; set; }
            public TextBooksGenre[] Genres { get; set; }
        }
    
        enum TextBooksGenre
        {
            Math,
            Science
        }
    
        public enum ReadingBooksGenre
        {
            Fiction,
            NonFiction
        }
    }
    

    【讨论】:

      【解决方案3】:

      在我看来,您系统中的“流派”概念对于简单的枚举来说太复杂了。我会将这个概念推广到它自己的类层次结构中:

      public class Genre
      {
          public int Discount { get; set; }
      }
      public class ReadingBooksGenre : Genre { }
      public class TextBooksGenre : Genre { }
      
      abstract public class Book<T> where T : Genre
      {
          public List<T> Genres { get; set; }
          public int Discount
          {
              get
              {
                  return (Genres.Count == 0) ? 0 : Genres.Max(g => g.Discount);
              }
          }
      }
      abstract public class ReadingBook : Book<ReadingBooksGenre> { }
      abstract public class TextBook : Book<TextBooksGenre> { }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-01-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多