Object design question
Let's say that you have an object called Product. One of the properties of the Product object is an integer called "ID" that corresponds to a primary key in a database. (The object has the ability to persist itself to the database.)
Now you need the ability to delete these items from the database, so you create a utility class called ProductUtility that has a static method called Delete. These method is responsible for deleting the object from the database.
Ok...so what information do you pass into the Delete method? Do you pass you in the Product object itself or do you just pass in the ID property since that is all it needs to do the actual delete?
If I pass the Product object itself to the Delete method in the ProductUtility class, then the caller is left with an object that has an ID field that doesn't exist in the database.
Furthermore, if I pass I wish to delete several products at one time, then I have to instantiate several Product objects.
Just passing in the ID seems much cleaner, but if in the future the business logic requires another field to delete the product, then I've got a problem because I'm only passing in the single field.
Does this make any sense?
FWIW, I'm using C#, but this problem transcends languages.
Not me
Thursday, April 15, 2004
Why can you not put the delete behavior in the Product class itself?
colombo
Thursday, April 15, 2004
"Why can you not put the delete behavior in the Product class itself?"
Well, I could, but this brings up another question.
If I were to do that, then in order to delete a product you first have to instantiate an instance of it. Let's say I needed to delete 200 products, then I would have to instantiate 200 products from the database, only to turn around and delete them. If I had a static method in a utility class, then I can just call that method with a integer.
Even if I'm just deleting a single object, doesn't it seem awkard to create an instance of object just to delete it? Then I'm left with an object that it is an unusual state because it has an ID field that points to a record in a database that doesn't exists anymore.
I really don't know..I'm trying to do this right, but this issue has me wondering.
Not me
Thursday, April 15, 2004
As I've sat here and thought about it, an idea comes to mind.
Put the Delete method in my class, and when this method is called then clear the ID field and set a private "dirty" flag to indicate that the object has not been persisted.
Since the dirty flag is raised and the ID field has been cleared, then this gets around my objects to leaving the object in an awkard state. To the caller, the object will then look like it's never been persisted and all is well.
Or am I nuts?
Not me
Thursday, April 15, 2004
How I design classes is that every object has a Delete() method and a Save() method.
If the object is new (doesn't have an ID) then the save method does an INSERT SQL query to insert the record. If the object has an ID, the save method does an UPDATE SQL query to update the record.
The Delete() method deletes the record by doing a DELETE query on the ID. The ID is then erased so the object becomes "new" and subsequent Save() call will create a new record.
It's rare to need to delete a large number of entities by ID. It's most likely to delete a large number of entities by some condition. Either way, you can make bulk deletes static methods of the class. For example you could have DeleteByIDs() or DeleteByDate() as methods.
There are some instances when I load an object just to delete it. If the load fails, then I display an error to the user.
Almost Anonymous
Thursday, April 15, 2004
Does that solution take care of your "why do I have to instantiate something to delete it?" question?
I'd be inclined to have an overloaded method in the utility class: delete(int id) and delete(Product prod). (I'm a java person, but I think the syntax is clear enough.) Where delete(Product) does something like:
int i= prod.getID();
delete(i);
In the delete(Product prod) method, I'd also destroy the object once it has been deleted (prod=null). That seems easier to me than testing a "dirty" flag in the rest of the code that uses the Product class.
Boofus McGoofus
Thursday, April 15, 2004
Boofus,
prod = null;
will only relinquish the delete method's reference. The caller's reference is still valid and still in memory.
Koz
Thursday, April 15, 2004
Have a collection class with delete(), add(), append(), load().
* load() accepts a query or something telling it to go to the DB and select certain products, and then instantiate Product objects in the Products collection.
* delete() removes the Product object from the Products collection and also deletes it from the DB.
* remove() removes the Product object from the Products collection
* add() creates a new object in the collection and in the DB.
Etc. etc.
The Product object should only take care of things about itself - not manage the collection.
anon
Thursday, April 15, 2004
Oh, and of course, the Products collection class should have as many iterators as you need.
anon
Thursday, April 15, 2004
Read the book "Patterns of Enterprise Application Architecture". It's all there.
This book is a must read for any Java/C# enterprise programmer.
Rhys Keepence
Thursday, April 15, 2004
Not me:
I think you are actually asking two questions: (1) What should be the argument to the "Delete" method? (2) What should happen to Product objects whose ID has been removed from the database? I think these are unrelated questions, since no matter how you decide to answer (1) you are still going to have to deal with (2).
In (1) you are worried about the possibility that a change in business logic would require more than just the ID field to delete a Product. How likely is this? If it's not likely, don't worry about it. If it does happen you're going to have to modify the logic of the Delete method anyway, so if you also have to change the method signature at that time it wouldn't be a tragedy. Under no circumstances would I write a program that has to instantiate a Product object from a database in order to delete it. You should never let an abstract principle of software design cause a program to do something ridiculous; programming is a practical endeavor.
As far as (2) goes, I would be very reluctant to add a flag to Product that marks whether it's valid or not. You will have to test this flag every time you do anything with Product, and figure out what to do if's invalid. Very messy.
BTW, why not make Delete a static method in Product, rather than create another class ProductUtility?
Paul Cornelius
Friday, April 16, 2004
I second anon's suggesstion. Have a class representing collection (table) of all products, and add a delete method to it. Having a delete method in product itself doesn't hurt either - it can be handy at times.
Egor
Friday, April 16, 2004
Product is an abstraction not an object. Putting methods related to the Db in there would too closely couple it to the Db.
What you need is a thing called a Service. This is a class that has identity and action but no state. Your service is responsible for handling database stuff.
Services work on mixins. Mixins have identity and state, but no action (properties aside). You mixin would be the actual product, but in this case only containing the attribute of the field that identifies the record in the Db (one of its keys).
You can overload the service method to either take a single mixin or a collection. Thus you then have the ability to delete one or many records.
Services and mixins normally reside in the application framework.
Andy Watson
Friday, April 16, 2004
How about this:
Put a non-static Delete() method in your Product class.
When you instantiate a Product, give it a reference to a class that implements a Storage interface. In your particular case, this class will know how to "talk" to a DB. All the Product class knows is that it's stored somewhere.
When you call Product.Delete(), it simple calls Storage.Delete(), and passes it the necessary information to delete the product from storage - e.g., the product ID.
This covers the main use case - user locates product and deletes it (meaning, you instantiate a product in order to show it to the user).
As for the invalid Product hanging around - the Product doesn't exist by itself. "Something" must have created it, and invoked Product.Delete(). That same "something" has the responsibility to get rid of the Product reference.
E.g., the Product collection mentioned above. A user does some operation that returns a group of Products. The natural thing to do, IMHO, is to put these Products in a collection, and let the user navigate the collection. When the user deletes a product, you can make it the collection responsibility to carry out the order. The collection invokes Product.Delete(), and then gets rid of it (by deleting it from the collection, and setting it to null for the garbage collector).
This doesn't prevent you from considering other use cases, such as deleting a group of products, or deleting a product without locating it first (by this I mean without instantiating it). But, depending on your application, these may not be typical use cases.
Paulo Caetano
Friday, April 16, 2004
Thanks for the thoughtful replies everyone. I really appreciate the great advice.
Having a static Delete method in the product class makes perfect sense to me. It keeps the logic contained within the class, and since it's static I get around the problem of having to instantiate the product to delete it.
FWIW, I am using a data access layer that handles all DB communication. My entity class calls the static methods in my data access layer class when it needs to persist or retrieve any data.
Not me
Friday, April 16, 2004
Can I just second Andy's suggestions. My recent days have been made hell by other peoples' fondness for static methods :)
I'm not sure that your service layer would have to work with "mixins" however. My approach would be something like the following:
1. define a Product interface which has property getters and setters AND business methods
2. define a class that implements the Product interface, e.g. ProductImpl
3. define a ProductService that performs persistent operations on Products
e.g. (Java):
public class ProductServiceImpl implements ProductService {
// make this a singleton with a private constructor
public Product getProduct( long id ) {
// load Product from database
}
public void delete( Product product ) throws SomeKindOfException {
// delete from database
}
// etc
}
In the Java world a product like Hibernate makes this very easy - i.e. you would write a Hibernate-based implementation of ProductService and you don't have to write any JDBC code - I'm not sure if there is anything similar for .NET, but if so I'd recommend looking at that.
Patterns of Enterprise Architecture also gets my endorsement too.
Walter Rumsby
Friday, April 16, 2004
Recent Topics
Fog Creek Home
|