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联系起来?
我们很高兴听到任何改进建议.
(根据书籍名称,日期和作者有很多折扣.甚至更多的书籍类型,这就是为什么这种方式对我来说似乎不对)
谢谢.
你的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)