Ruby on Rails books teach wrong OO
November 22nd, 2006I was reading a lot of Ruby on Rails books in order to start my own project. I found the thing, which really disturbes me: though Ruby is a proper OO language, none of OO desugn features are presented in Rails books.
Proper Object Oriented Design should simplify software development by introducing abstraction from unneccessary logic constraints.
Take, for example, books dealing with making simple online store. They all say that there is no way to add products into shopping cart because products can be in many shopping carts. Because of that you have to add LineItems, which will point to a product description.
This is true when we speak about database design, but completely untrue when we speak about software design. From OO point of view there is no such thing as LiteItem. It has no value to a business logic and it must be dropped from model.
This model look much cleaner and causes no confusion. Additional work must be done on model layer, but this is just to avoid database limitations.
What the hell are you talking about?
LineItems have a very real business logic value. They keep the many-to-many association between Orders and Products, the quantity of the ordered products and their prices at the moment when the order was created. LineItem is called an association class, which has a special notation in UML.
What you drew on the second diagram means that Product objects are dependent from Order which means that a particular Product belongs to only ONE Order. Also the black diamond on the Order’s side of the association means the composition association, which states that a product must be deleted when the corresponding order is deleted.
That doesn’t make sense at all.
Many-to-many association through LineItems should be kept in the database logic, which has nothing to do with the object oriented world. And I don’t care if UML has a special notation for something, which has no sense in the object world.
Look at that from another perspective. You have shopping cart. You have products on the shelves. You take products and put them into shopping cart. Does behavior of the objects change once you put them into cart? In real world, which is Object Oriented world - product behavior is not changed. When why it needs to change in your computer implementation? The only way it has to change behavior is because relational database has no way to store objects and their state.
But what if you have a way to store state and object relationships? Would you still express your state in relational terms?
What I am trying to say is that objects are not 1-to-1 representation of the database and when you trying to create 1-to-1 you are probably doing something twice. And doing something twice is not good at all.
Many-to-many association through LineItems should be kept in the database logic, which has nothing to do with the object oriented world. And I don’t care if UML has a special notation for something, which has no sense in the object world.
Look at that from another perspective. You have shopping cart. You have products on the shelves. You take products and put them into shopping cart. Does behavior of the objects change once you put them into cart? In real world, which is Object Oriented world - product behavior is not changed. When why it needs to change in your computer implementation? The only way it has to change behavior is because relational database has no way to store objects and their state.
But what if you have a way to store state and object relationships? Would you still express your state in relational terms?
What I am trying to say is that objects are not 1-to-1 representation of the database and when you trying to create 1-to-1 you are probably doing something twice. And doing something twice is not good at all.
>Many-to-many association through LineItems should be kept in the database logic, which has nothing
>to do with the object oriented world. And I don’t care if UML has a special notation for something,
>which has no sense in the object world.
What I was trying to say is that if it were a plain many-to-many link then this statement would be correct. In OO world that would be something like:
Order.getProducts(): List of Products
But in case of Shopping Cart it is not true. LineItem has a very specific business purpose, it stores quantity of the ordered products and the price at the moment the order was created. It might also contain some additional logic like deducting the product inventory when the order is confirmed. Note that you must keep the price in LineItem, because the product price has a tendency to change and believe me you don’t want to have an order where the sum of prices of all ordered products doesn’t match order’s subtotal.
So we have these signatures:
Order.getLineItems(): List of LineItems
LineItem.getProduct(): Product
It has nothing to do with the way you store these objects in the database, this is the domain model of any Shopping Cart application.
>Look at that from another perspective. You have shopping cart. You have products on the shelves.
>You take products and put them into shopping cart. Does behavior of the objects change once you
>put them into cart? In real world, which is Object Oriented world - product behavior is not
>changed. When why it needs to change in your computer implementation? The only way it has to
>change behavior is because relational database has no way to store objects and their state.
I don’t understand your point here. We are looking at the class diagram here which has nothing to do with objects’ behavior, but only with their relationships.
>But what if you have a way to store state and object relationships? Would you still express your
>state in relational terms?
>What I am trying to say is that objects are not 1-to-1 representation of the database and when
>you trying to create 1-to-1 you are probably doing something twice. And doing something twice
>is not good at all.
The only case where this is not 1-to-1 representation is with pure many-to-many relationships (with no additional attributes), but I don’t think it applies to this particular case. Do you know of any other cases?
Line Items are critical to business logic. They contain much information, such as:
- shipping information (can the line item be shipped separately, to a different location, etc)
- quantity (albeit this could be done less efficiently by adding multiple pointers, but then you just have to count them later. you may also want to distinguish line items referencing the same product though, such as for different shipping addresses)
- price (price can change while someone is shopping, and you don’t always want that to affect carts. many stores will have a policy that a price is valid for x hours after added to a cart)
- discounts
- line item specific notes (e.g. “please make sure items are from lot #XX”)
Every ERP system has a line item concept for good reason. I think you spoke too soon here…
What am I trying to say is that in database realm product state is expressed as LineItem. In OO code there is no such thing as LineItem. There is only the state of Product object.
Product, freshly inserted into ShoppingCart has one state. Product retrieved from saved has different state. Product, retrieved from order history may have state different to those two. Its up to OO persistence layer (read OR mapper) to decide whether Product stored as LineItem. But who said that ROR has proper OR mapper?
Not sure who I’m responding to…
Anyway, LineItem has additional fields that do not belong on either Product or Cart. Putting that data on the Product object complicates the database mapping because it is wrong. Trying to hide the fact that LineItem is a necessary concept is a useless and complicated exercise, reflected in the fact that it is confusing and complicated (that’s usually an indication you are doing something wrong.)
LineItem may have additional fields and may not. Its implementation specific.
Putting additional attributes complicates db mapping, but simplifies control layer. I prefer to have pure OO in my control layer and let db layer to deal with the tables and mappings.