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

    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;
    }

有没有办法在不检查类型的情况下选择正确的词典?
或者甚至可以不用字典,或者用字典?
也许可以用某种方式将book type与enum联系起来?
很高兴听到任何改进的建议。
(根据书名、日期和作者,有很多折扣。甚至更多的书籍类型这就是为什么我觉得这样做不对的原因)
谢谢您。

最佳答案

你的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
    }
}

07-26 02:43