2012-12-01 15 views
5

Ich baue eine Buchbibliothek App, ich habe ein abstraktes Buch Klasse, zwei Arten von abgeleiteten Bücher und zwei Enums, die das Genre des Buches speichern wird. Jedes Buch kann zu einem Genre oder mehr gehören.die richtige Liste von Enums aus 2 abgeleiteten Klassen auswählen

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

Jetzt möchte ich die Rabatte speichern basiert auf dem Buch Genres (ohne Doppel Rabatte, nur der höchste Rabatt berechnet wird), so dass ich denke über zwei Wörterbücher zu machen, die alle Rabatte für jedes Genre sparen , wie folgt aus:

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

So, jetzt muss ich das Genre von jedem Buch, um den Check den höchsten Rabatt zu finden, gibt es einen besseren Weg, es zu tun, als:

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

th Gibt es eine Möglichkeit, das richtige Wörterbuch auszuwählen, ohne nach dem Typ zu suchen? oder vielleicht sogar eine Möglichkeit, es ohne die Wörterbücher zu tun, oder mit einem? vielleicht irgendwie Buchtyp mit der Enum verbinden?

wird sich freuen, irgendwelche Verbesserungsvorschläge zu hören.

(es gibt eine Menge von mehr Rabatte basierend auf Bücher Name, Datum und Autor. Auch einige weitere Buchtypen, deshalb ist diese Art und Weise mir scheint nicht richtig)

Danke.

+1

Ich bin ein wenig misstrauisch von einem Modell, in dem Lehrbücher _arenn' eine Untermenge der Sachliteratur nicht sind. Obwohl ich denke, man muss J. K. Rowlings "Quidditch Through The Ages" berücksichtigen ... –

Antwort

1

Ihre GetDiscount Methode ist klassisches Beispiel für Open/Closed principle Verletzung. Wenn Sie einen neuen Buchtyp hinzufügen, müssen Sie den neuen if Block zu GetDiscount hinzufügen.

Bessere Möglichkeit ist es, einige vorhandene Techinques zu verwenden, die es Ihnen ermöglichen, neue Funktionen hinzuzufügen, ohne den vorhandenen Code ändern zu müssen. Zum Beispiel Composite pattern. Ich werde eine Entwurfsimplementierung des Composite-Rabatt-Evaluators schreiben. Sie können auf einfache Weise neue Rabattbewerter basierend auf beliebigen Bucheigenschaften (Datum, Preis usw.) hinzufügen.

Auch ich werde Schnittstellen anstelle der Vererbung verwenden. Vererbung ist eine sehr starke Verbindung zwischen zwei Entitäten und in diesem Fall ist es übermäßig.

Auflistung ist 167 Zeilen lang, so ist hier mehr 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

Ich würde eine generische Methode erstellen, die die Wörterbücher und das Buch des entsprechenden Typs akzeptiert. Auf diese Weise können Sie den Algorithmus generisch genug und Ihren Code ziemlich sauber machen. Natürlich wäre der GetDiscount auch generisch, aber man könnte sie nicht falsch mischen. (Oh, ja, Buch wäre auch generisch mit den Genres, die den richtigen Typ zurückgeben.) Ich denke, dass dieser Code mit ein bisschen LINQ implementiert werden kann, aber vielleicht ist es den zusätzlichen Aufwand nicht wert.

0

Es scheint mir, dass der Begriff „Genre“ in Ihrem System für eine einfache Enumeration zu kompliziert ist. Ich würde das Konzept zu seiner eigenen Klassenhierarchie fördern:

Verwandte Themen