views:

195

answers:

5

I often have the same trouble when I have to design my class for a web application. The requirements are : - maintainable (no copy-paste for instance) - layers fully separated (the business layer doesn't have to know which method of the data layer is used) - high performance : don't load useless data.

First I have a table with all my customers and their addresses : Code :

Customer
--Id
--Name
--Address
----City
----ZC
----Street

Now I want a table (in another page) with all my customers and the books that they bought, I have a few possibilities :

1/ I create a new class :

Code :

CustomerWithBooks
--Id 
--Name
--Books[]
----ID
----name

PRO : I load only the useful data CONS : I build my class after my UI , and there is copy-paste.

2/ I add Books[] to the first class. PRO : Everything is in the same class, it's maintainable CONS : I load the address for nothing. If I don't load the address I can : lazy loading, but I really don't like it, or when I use my class I have to know which method of my DAL i called, and I don't like it.

3/ I use inheritance : Code :

ClientBase
--ID
--Name

ClientWithBooks : ClientBase
--Books[]

ClientWithAdress : ClientBase
--Address

PRO: really maintenable, and I don't load data for nothing CONS : What do I do if in one UI I want to show the Books AND the Address ?

4/ ?? I hope there is a perfect solution

+1  A: 

I would to avoid 2 and 3, because it locks you into a restrictive hierarchy that doesn't really meet your needs. As you point out, there could be any combination of things that you want, such as customers and their books, and maybe their address, and maybe their ordering history. Or maybe you'll want a book with it's list of customers. Since your underlying business information is not really hierarchical, you should try to avoid making your object model unnecessarily hierarchical. Otherwise, you will build in restrictions that will cause you a lot of headaches later, because you can't think of all the scenerios now.

I think you're on the right track with 1. I would say to create some basic classes for Customers and Books, and then create a CustomerBook association class that contains an instance both the customer and the book. Then you can have you methods worry about how to load the data into that list for a given scenerio.

Mike Mooney
It sounds good. If I understand well, you say that I must avoid composition if there is no hierarchy, instead I have to make some "relation class".
remi bourgarel
+3  A: 

You option 1 is close to good, assuming I understand it correctly. A customer and a book are two completely different things. You want that data/functionality separate, and should not inherit from any common base class (that you have made).

As the "Con" you say: I build my class after my UI , and there is copy-paste.

  • A. If you mock up some UI to help clarify requirements before you settle on your design and code up classes, that's good, not bad.
  • B. Good arrangement of your domain objects helps eliminate copy/paste, not cause it. If you have some seemingly repetitive code within your well-arranged classes (often data access code) that's typical, don't worry. You can address with with a good data-access layer/tool, good shared logging resources, etc. Repetitive code within your classes just means you have more design improvement to do, not that having separate classes for all your domain realities is bad.

On the page where you need to deal with both customers and books, you will use customer objects and book objects, and probably a books collection object. And depending on how your db/object-model are set up, you might be dealing with other objects to get form customer to the books they bought. For example, the customers probably buy 1 or more books at the same time, and these are tied to an Order object, which has a reference to a customer. So, you'll probably go from a

  1. Customer to an
  2. Orders collection containing all of that customers orders to the individual
  3. Order objects and from there to a corresponding
  4. Books collection containing all the
  5. Book objects that relate to that Order object.

None of these need to inherit from each other. Now, let's say getting all the books bought by a customer is something you do a lot, and you want to streamline that. You then want to have a Books collection directly off of Customer that gives you that, though the sql queries you use to get those books still goes through Orders in the db. You must start with your object model (and tables behind the scenes) reflecting reality accurately. Even if this give you seemingly many classes, it is more simple in the end. You might end up with some inheritance, you might not.

Patrick Karcher
A: 

I would stick the address into Customer, and have a separate collection of books.

Bookshelf
--Books[]

This way, a Customer doesn't have, but can have, one or more books associated to him. PHP-code example following:

class BookshelfFactory {
  public static function getBookshelf(Customer $customer) {
     // perform some fetching here
     return $bookshelf;
  }
}
chelmertz
A: 

You're sort of designing backwards from an OOA&D standpoint. It's normal to use data-driven design at the persistence (usually a relational database) layer. But in OOA&D it's more normal to think of the messages an object will send and receive (you model an object's methods not its members). I would think about it this way:

Customer
+getBooks():List<Book>
+getAddress():Address
shadit
I don't really like it : you call your DAL inside of your Business class. They must ignore each other.
remi bourgarel
@remi bourgarel - I wouldn't presume to say how the Customer class fulfills those methods at this time; just making it possible to get a customer's address and list of books via the customer itself.
shadit
@remi bourgarel, it's quite common (and good) to have DAL used within business classes. Hence "3-tier", with each tier above the other, using the one below. In my current solution I'm doing this (even though I'm injecting my DAL tool for unit testing and other purposes). Being able to also instantiate your objects with data is also good, so that (for example) your books collection would only need one db call to instantiate 20 book objects.
Patrick Karcher
A: 

I think your problem is an issue for the implementation of your data mapping layer.

You can have highly performant queries with JOINS that return you the Customers as well as their Books.

Your mapping layer maps this into the appropriate unique objects and is responsible for creating the right 1-many aggregation for your objects.

In addition you could cater for shallow loading, for display properties to save unnecessary amounts of data to be transferred where you only need a few attributes per object.

Wim Hollebrandse
The problem with JOINS is that I'll have 1 request to get customer + books, and 1 request to get customer + adress. So i'll copy paste a lot of sql code (fields, filter etc...)
remi bourgarel
Not necessarily, if you see you will duplicate, you simply refactor and store it in a string const of the relevant class or abstraction to be reused. Always keep on refactoring...
Wim Hollebrandse
@remi bourgarel, having multiple sql queries mention the same table is absolutely fine, and it is unavoidable. This is all about balance, and one of the balances is between *efficiency* and *encapsulation*. Encapsulation is only a tool to achieve simplicity. A **simple** and efficient system will usually involve similar sql queries in multiple places. Perfectly fine. As Wim says, sometimes you can/should remove some of this duplication, which is of course good, but it is usually impossible to remove all of it, and that is fine.
Patrick Karcher