2012-12-01 13 views
5

Sto costruendo un'app librerie di libri, ho un libro di classe astratto, due tipi di libri derivati ​​e due enum che salveranno il genere del libro. Ogni libro può essere correlato a un genere o più.selezionando l'elenco corretto di Enum da 2 classi derivate

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

Ora voglio salvare gli sconti in base ai generi Book (senza doppi sconti, solo il più alto sconto è calcolato), così sto pensando di fare due dizionari che salveranno tutti gli sconti per ogni genere , in questo modo:

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

Così ora ho bisogno di controllare il genere di ogni libro, al fine di trovare il più alto sconto, c'è un modo migliore per farlo che:

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

è esimo C'è un modo per selezionare il dizionario corretto senza verificare il tipo? o forse anche un modo per farlo senza i dizionari, o usando uno? forse in qualche modo collega il tipo di libro all'Enum?

sarà lieto di ricevere suggerimenti di miglioramento.

(ci sono un sacco di più sconti in base al nome libri, la data e l'autore. Anche alcuni tipi di libri più è per questo che in questo modo non mi sembra giusto per me)

Grazie.

+1

Sono un po 'sospettoso di un modello in cui i libri di testo non sono un sottoinsieme di non-fiction. Anche se suppongo che si debbano prendere indennità per _Quidditch Through The Ages di J. K. Rowling _... –

risposta

1

Il metodo GetDiscount è un classico esempio di violazione di Open/Closed principle. Quando aggiungi un nuovo tipo di libro devi aggiungere il nuovo blocco if a GetDiscount.

Il modo migliore è utilizzare alcuni tecnici esistenti che consentono di aggiungere nuove funzionalità senza necessità di modificare il codice esistente. Ad esempio, Composite pattern. Scriverò alcuni progetti di implementazione del valutatore di sconto composito. È possibile aggiungere facilmente nuovi valutatori di sconto in base alle eventuali valutazioni di libri (data, prezzo, ecc.).

Inoltre, userò le interfacce invece dell'ereditarietà. L'ereditarietà è un legame molto forte tra due entità e in questo caso è eccessivo.

elenco è lungo 167 linee, ecco un maggiore comfort 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

Vorrei creare un metodo generico che accetta i dizionari e il libro di tipo corrispondente. In questo modo puoi ottenere l'algoritmo abbastanza generico e il tuo codice abbastanza pulito. Naturalmente in questo modo il GetDiscount sarebbe anche generico, ma non è possibile mescolarli in modo errato. (Oh, sì, Book sarebbe anche generico con i Genres che restituiscono il tipo corretto.) Penso che questo codice possa essere implementato con un po 'di LINQ, ma forse non ne vale la pena.

0

Mi sembra che il concetto di "genere" nel vostro sistema è troppo complicato per un semplice enum. Promuovere il concetto alla sua gerarchia di classi:

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> { } 
Problemi correlati