从2个派生类中选择正确的枚举列表

Dav*_*man 5 c# enums modeling dictionary

我正在建立一个图书馆应用程序,我有一本抽象的书籍类,两种类型的衍生书籍和两本能够保存书籍类型的枚举.每本书都可以与一种或多种类型相关.

    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; }
    }
Run Code Online (Sandbox Code Playgroud)

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

    Dictionary<ReadingBooksGenre, int> _readingBooksDiscounts;
    Dictionary<TextBooksGenre, int> _textBooksDiscounts;
Run Code Online (Sandbox Code Playgroud)

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

    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;
    }
Run Code Online (Sandbox Code Playgroud)

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

我们很高兴听到任何改进建议.

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

谢谢.

Kir*_*ill 1

你的GetDiscount方法是违反开闭原则的典型例子。当您添加新的书籍类型时,您必须将新的if块添加到GetDiscount.

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

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

清单有 167 行长,所以这里是更舒适的Pastebin 复制

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
    }
}
Run Code Online (Sandbox Code Playgroud)