tags:

views:

313

answers:

9

hello

I have a UI-dialog something like this: You must choose a book from a list. Optionally, you can either choose a publisher (another class) from a list or enter the publisher-name as as a string.

I think this gives me 3 types as the output from the dialog.

  1. book
  2. book with publisher-class
  3. book with publisher-string

How would you model this in objects? It seems to me that the having a book base-class, and then two subclasses for publisher and publisher name is the correct choice. Are there any alternatives, perhaps favoring composition that would give a better model?

+5  A: 

Number two would be my approach.

I would have a class for Publisher with a property called Name, along with any other properties needed to describe a publisher.

Then I would have a class for book with properties to describe it, along with a property of type Publisher.

If the user enters a new publisher as a string, create a new Publisher object.

If the user does not enter a publisher, leave the property null. That will satisfy the condition that the book has no publishers. Alternatively, you could have publisher with the name "No publisher" but I think that is going too far out of your way to avoid nulls.

Bob
Forgot to tell that the publisher class is quite complicated and have several other properties than just the name.
Karsten
Agreeg stick to a class. You can always make it abstract later if you need refined publisher treatment.
Frank Krueger
+1  A: 

I wouldn't create a Publisher class to inherit Book, since Publisher isn't a book, it is metadata information about a book. The Bible though, would inherit Book.

My advice would be to create a Publisher property on the Book. This Publisher could be of type IPublishInformation, and the string publisher could use a NamedPublisher{string Name} class, implementing IPublishInformation.

That's my thoughts anyway!

baretta
A: 

I think it's difficult to make a design decision based just on this dialog. For instance, are you picking from a set of existing books? In that case, what if the user enters a publisher who doesn't exist? In this case, you may not want to return an instance of any Book class at all, but raise some kind of exception.

Do all books in your case have publishers? If so, then I agree with @Bob that you could make a Publisher class, and have a Book class contain an instance of a Publisher object. If only some books have Publishers, then you could go with the inheritance model you describe, but I would collapse options 2 and 3 into a single choice (again by using a Publisher object) because otherwise you could end up with two instances of the same book that are objects of different types (one where the user entered the publisher as a string, and once by picking from the list).

--Phil

serenader
+2  A: 

From an OO perspective, HAS-A relationships solve this problem better than IS-A relationships in this case. A book HAS-A publisher (1:1) and a publisher HAS-A list of books that it publishes (1:many). Create a Book class that contains a reference to a Publisher and a Publisher class that has a list of references to Books. Further, the Publisher HAS-A string which you can use to locate a specific publisher

mxg
+2  A: 

I must disagree with this statement in the last paragraph:

It seems to me that the having a book base-class, and then two subclasses for publisher and publisher name is the correct choice.

Subclasses are used to represent "is-a-kind-of" relationship. (The old tired stereotype is a Fruit class, with Apple and Orange as subclasses.) A more realistic example would be a payroll system with an Employee class, specialized by HourlyEmployee and SalariedEmployee classes. In each case, the subclass represents a specific category within the superclass.

In contrast, a publisher is not a kind of book. A better model would be to have a Book class and a Publisher class, with a many-to-one relationship between them (a book has a single publisher, but a publisher may produce multiple books).

A book has many potential attributes, such as title, ISBN, publisher, and author; a publisher's potential attributes include business name and address (possibly multiple addresses) and a list of books published.

Depending on what you are trying to model, you may also need an Author class, but that's outside the scope of your original question.

joel.neely
A: 

I'll try to explain a bit more. A book doesn't need to have a publisher. The publisher object is not the same as a publisher-name entered as a string.

You must
-choose a book from an existing list

You can one of the following
-choose a publisher from an existing list or
-you can enter a publisher name or
-you can fill nothing about the publisher

Karsten
please edit your answer (its wiki editable) to clarify your question. This makes answers more clear when other people come across your question later.
Doug T.
A: 

If you are assuming that Books can have multiple publishers. Then I will would return a BookSelectonResult with two proprieties. One set to a Book and another set to the selected Publisher. This answer also assume that you have a book class, a publisher class, and a collection class for each.

The point of the Dialog is to return what the user selected. That is a unique "thing" so deserves it's own class. If all you were doing is return a single book or a single publisher then using the original class directly would be ok. But here you have multiple possibilities in the results so that calls for a unique result class.

The other consideration is how trivial is this dialog? For example picking a file name to open a book. The file name existence is fleeting what you want is the book to be stored in your system. The same for the result of selecting a book and a publisher. If you had an another object to hold the result already (like a check out list) then I suggest don't bother with any special methods or classes. Bundle the dialog into a Command object and just return the result in the members of the dialog class. Then process the result to where they are finally stored.

In my CAM software I do this often with the dialog that manipulate configuration options instead of using my elaborate model-view-presenter architecture. The reason I can get away with this is because when a user click on an item or perform an action, the UI controller executes a Command object which then manipulates the model. Everything goes through Command Object in my setup. So for trivial dialogs I just bundle them up with Command Objects that uses the dialog rather than create the whole MVP layer.

Ultimately it is a judgment call.

RS Conley
+1  A: 

I'll try to explain a bit more. A book doesn't need to have a publisher. The publisher object is not the same as a publisher-name entered as a string.

You must -choose a book from an existing list

You can one of the following -choose a publisher from an existing list or -you can enter a publisher name or -you can fill nothing about the publisher

Still calls for a custom result object. Now you have three fields a Book Object, a Publisher Object, and a Publisher String. You then pass it to the code that can deal with it intelligently. You could add methods to address custom processing needs. But in the end it is a specialized result of THAT dialog and should not be some subfield of book or publisher or any other object.

If the book is nothing you know you got an error because you need a book. You have four combinations of Publisher Object and Publisher_String to deal with as well. Of this indicates to me that you need a class to deal specifically with the result.

RS Conley
A: 

Try this (C#):

Class Book
{
   public string Name;
   public List<Publisher> publishers = new List<Publishers>;

   Book()
   {
      // Load publishers array with relevant instances of Publisher class...
   }
}

Class Books
{
   private List<Book> books = new List<Book>;
   public Books()
   {
      // Load books array with all books...
   }

   public List<Book> GetBook (string publisher)
   {
      List<Book> retVal = new List<Book>;
      foreach (Book item in books)
      {
         if (item.Publisher.Name == publisher)
         {
            retVal.Add(item);
         }
      }
   }

   public List<Book> GetBook (Publisher publisher)
   {
      List<Book> retVal = new List<Book>;
      foreach (Book item in books)
      {
         if (item.Publisher.Name == publisher.Name)
         {
            retVal.Add(item);
         }
      }
   }
}

Class Publisher
{
   public string Name;
}
Eric