2012-12-01 9 views
5

Buduję aplikację biblioteki książek, mam książkę abstrakcyjną Klasa, dwa typy książek pochodnych i dwie wyliczenia, które zachowają gatunek książki. Każda książka może być powiązana z jednym gatunkiem lub więcej.wybieranie prawidłowej listy wyliczeń z 2 klas pochodnych

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

Teraz chcę zapisać zniżki na podstawie gatunków Book (bez podwójnych rabaty, najwyższa zniżka jest obliczana tylko), więc myślę o zrobieniu dwóch słowników, które będą zapisywać wszystkie rabaty dla każdego gatunku coś takiego:

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

Więc teraz muszę sprawdzić gatunek każdej książki, aby wybrać najwyższą zniżkę, czy istnieje lepszy sposób to zrobić niż:

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

jest th jesteś sposobem, aby wybrać odpowiedni słownik bez sprawdzania typu? , a może nawet sposób, aby to zrobić bez słowników lub używając jednego? może jakoś połączyć typ książki z Enum?

z przyjemnością usłyszy wszelkie sugestie dotyczące ulepszeń.

(istnieje wiele więcej zniżek w oparciu o nazwę, datę i książek autora. Nawet kilka rodzajów książek, dlatego ten sposób nie wydaje się słuszne, aby mnie)

Dziękuję.

+1

Jestem trochę nieufny wobec modelu, w którym podręczniki nie stanowią podgrupy literatury faktu. Chociaż przypuszczam, że trzeba się zastanowić nad "IQuidditch Through The Ages" J. K. Rowlinga ... –

Odpowiedz

1

Twoja metoda GetDiscount jest klasycznym przykładem Open/Closed principle naruszenia. Dodając nowy typ książki, musisz dodać nowy blok if do GetDiscount.

Lepszym sposobem jest użycie niektórych istniejących techinques które pozwalają na dodawanie nowych funkcji bez konieczności modyfikowania istniejącego kodu. Na przykład Composite pattern. Napiszę projekt implementacji kalkulatora rabatowego. Możesz łatwo dodawać nowe oceny ze zniżek na podstawie dowolnych właściwości księgi (daty, ceny itp.).

Używam także interfejsów zamiast dziedziczenia. Dziedziczenie jest bardzo silnym połączeniem między dwoma podmiotami, w tym przypadku jest nadmierne.

aukcji jest długa 167 linii, więc tutaj jest większy komfort 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 
    } 
} 
0

Utworzę ogólną metodę akceptującą słowniki i książkę odpowiedniego typu. W ten sposób można uzyskać wystarczający algorytm, a kod całkiem czysty. Naturalnie w ten sposób GetDiscount będzie również generyczny, ale nie można ich zmiksować w niewłaściwy sposób. (Och, tak, Book będzie również generyczny z gatunkami zwracającymi odpowiedni typ.) Myślę, że ten kod można zaimplementować z odrobiną LINQ, ale może nie jest to warte dodatkowego wysiłku.

0

Wydaje mi się, że pojęcie „gatunek” w systemie jest zbyt skomplikowane dla prostego wyliczenia. Propagowałbym koncepcję do własnej hierarchii klas:

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> { } 
Powiązane problemy