Duplication of work and Project Tracker

Duplication of work and Project Tracker

Old forum URL: forums.lhotka.net/forums/t/191.aspx


cmay posted on Wednesday, May 24, 2006

I saw Rocky speak a little while back and he made the comment that if you are thinking like a relational database programmer when creating your classes you are going down the wrong path.

He was trying to show that, while in the database there may only be 1 Project table, that doesn't mean that you will only have 1 Project class in your application.

This is evident in the fact that there is a Project, and a ProjectInfo class which both represent a Project.

I am having a hard time understanding this concept.

I understand that in the application the ProjectInfo is used in places where the full Project class is not needed, but why not reuse the Project class and save the work of creating the ProjectInfo class? 

Couldn't you supply the criteria object with a value to indicate that you don't need to front load the children of Project (e.g. Resources collection) when you wanted to use the Project class in a place where right now they are using the ProjectInfo class?

I think when I saw Rocky speaking, he called this Responsibility Analysis or Responsibility Driven Design (if I remember correctly) and suggested Object Thinking by David West, but I didn't see anything from thumbing through it to give me an idea why I am wrong on this point.

Any thoughts, or ideas where I could find some more info on this?

pfeds replied on Wednesday, May 24, 2006

Do you have the 2005 book as there is a really good section of the BO Design chapter that explains modelling objects dependent on their behaviour.  You could also look into SRP (Single Responsibility Principle) as this might help.

I found it strange at first, especially as it seemed to duplicate code which I've always tried to avoid at all cost.  However, each object in the ProjectTracker library does have a concise behaviour, and object modelling is all about behaviour and not data.  The ProjectList and ProjectInfo objects are lightweight and readonly - ideal for displaying in a list.  If you wish to modify a project then it is then you create the full fat object with all the data, and this helps you avoid creating large collections incorporating masses of redundant data.

This has worked especially well in practice for me with some code I worked on recently, so this should be a good illustration.  The code involved working with Active Directory to retrieve a list of users (potentially around 5000), and to also add, remove and update these users.  A user object not only required data from Active Directory, but also about permissions for installed tools on the system.  If I were to retrieve a list of UserInfo objects with only the information from AD then this is far more efficient than having to find out all the extra redundant information that is not required at the time.  This means I can efficiently display the list or a filtered list, and only when I select a user for editing do I create the User object with all the information I need.

Hope this helps.

cmay replied on Wednesday, May 24, 2006

I understand that in cases like the one you suggest, there might be performance issues that would drive you to creating simple lightweight class, rather than reuse a heavy complex class

I went back and re-read the section I think you are talking about:

Chapter 6: Object Oriented Application Design : Object Design.

Interestingly, in the book it looks like Rocky was planning on using Project instead of ProjectInfo.  It looks like ProjectList is a collection of Project, not ProjectInfo as in the Project Tracker.

 

Let me approach your AD User example in a manner similar to how we have been doing stuff and maybe you can comment on it.

Lets say you have a User object that contains a InstalledToolPremissions collection.
When you "normally" use the User object, the InstalledToolPermissions collection is very important, but when working with AD, it isn't used.  The User info can be loaded from AD or from the database.

I would build a couple different factory methods to create a User.  Maybe one would take a datareader as a parameter and another would take a DirectoryEntry (AD) object.  If a datareader were provided it would populate itself and also pass the datareader on to the InstalledToolPermission factory to build that object.  The AD factory method wouldn't populate the InstalledToolPermission collection, but the property accessor would be built to lazy load it if needed.

What do you think?

 

xal replied on Wednesday, May 24, 2006

Well, There are several things here to take into account:
To do what you propose, you'd need a switchable object with lazy loading children.
These objects would need to be marked as child if they're called within the context of a collection. This brings issues with saving, since once you mark an item as child you can't individually save it. (And if you do, it'd be hacky).

Also, there's a performance issue that comes from two sides:
-Security: Generally you don't have security constraints for reading the lists, but you do for full blown objects. Using the same object for browsing and editing will add an unnecesary  overhead in this area.
-Quantity of data: Generally you make readonly lists contain just a couple of fields. The ones that are more relevant / important. If your bo has 20 fields, you're not going to show all those 20 in your list, but you'll be fetching them anyway. Other wise you'd have to lazy load the rest of the fields which is just crazy.

Again, separating objects by behaviour may seem to duplicate efforts sometimes, but it actually makes things more simple and agile for the developer and the user.

Andrés

phowatt replied on Thursday, May 25, 2006

When I first started with OOP via Rocky's book I had the relational view of the world.  I saw the caveats about making your business classes behavior oriented vs data oriented.  At about the same time I was introduced to a great code generating tool called CodeSmith.  CodeSmith makes it easy to generate business classed based on a table in your database.  I read lots of comments in our forum about that approach not being behavior oriented but I still did not appreciate what these folks were saying.  I was heavily involved in a development project and I was deep into the order entry part.  I started out by using the Customer object as the central object for order entry.  But I soon found that the Customer object graph was growing larger and larger.  Customer orders were a child collection to the customer and of course order line items were the grandchildren.  Then I had to handle returns.  And then there was a composite customer history that include orders, returns, and contacts with the customer.  On and on it went.  Some customers placed a lot of orders with the company and soon I could see that if a had a simple need to, lets say change the customers phone number I could potentially be retrieving a lot of data from the database just to change a phone number.  That did not seem advisable to me.

One day I was having a review of my code for a form I had written.  I had a really smart guy working for me.  After reviewing the code for a while he got a funny look on his face and ask me 'Just how many lines of code are in this form anyway?'  After getting to the end and seeing how big the form class was he told me that I was putting a lot of the business logic in the form when it belonged in a business class.  And I could see that what he said really made sense.  You do not want to have your business logic spread amoungst a bunch of forms making it hard to maintain and probably producing a consider amount of redundant code.  As best you can you want just one place for your business logic and that is in a business class.

We worked out a two layer architecture that solved our problem nicely.  What I now do is have one layer of business classes that are essentially data facing.  If I have a Customer table I will have a Customer class.  That class may have a child collection or two but the child collection will only include data that is required for all of the customer data like a collection of alternate addresses, or perhaps a collection of contacts for that customer.  But things like orders and history are not part of the Customer class.  The second layer of classes is what I call the application facing business classes.  In the case of order entry I may have a class called OrderEntryManager.  This class will use any of the data facing classes it needs to support the order entry behavior. But all of the complex business logic require to manage the order entry process will be found in this class. My forms have access to any and all data facing objects they need through the OrderEntryManager class. The property and methods that involve multiple data facing classes are written in the OrderEntryManager.  This class provides any methods necessary to take the burden of business logic out of the UI classes. 

This approach puts the basic data validation business logic in the data facing classes and lets me use CodeSmith to generate those classes quickly and effectively.  The manager classes I code from scratch.  They might include the need for a stored procedure to retrieve some data or not. 

This approach allows me to write UI code that has little or not business logic in it.  The code is mostly for making the UI work.  The business logic is mostly in a business class.

One way to look at it is to think of the database as the place where the business data is persisted and the business classes are where the business rules are persisted.

I am not an oop guru but I had to make the same change in thinking that you are facing.  Hopefully my experience will be helpful to you.

Good luck.

DansDreams replied on Thursday, May 25, 2006

cmay, I agree with you in principle and have posted similar message myself numerious times.  The thing I wrestle with in trying to decide whether or not to go forward with this idea is the complexities involved, as others have explained.

This is sort of a catch-22 if you ask me.  It's quite apparent that there often needs to be at least a couple classes pointing to the same underlying data when you're looking at things from the single responsibility point of view.  But I think it's also apparent that doing so can lead to a violation of the "normalization of behavior" principle via duplication of code/functionality. 

A couple of solutions have been discussed here over the years.  The first is a "helper class" where all the common business functionality is represented in an static/shared class used by the various BOs.  For example, a FullName property in both the Customer and CustomerInfo classes that's derived from concatenating 2 or 3 name fields could use a static method PersonHelper.FullName(string firstName, string middleName, string lastName).  Being able to use Rules in an OO way also helps avoid the duplication of code.

The other solution is to have a series of classes building more and more complex through composition rather than as distinct classes.  The full Customer class would contain the business logic in the simple CustomerInfo class this way, so there's never any duplication.

I think most, if not all, have taken the pragmatic path and used the first approach as the second is of course considerably more complex.

hurcane replied on Thursday, May 25, 2006

I've been using helper classes more frequently lately. One of the first that I created was an Address class. Our database doesn't have a normalized address table. We have address fields in the customer table, the vendor table, the order table, the invoice table, etc. Fortunately, the column names follow a certain convention among the tables, so I can easily populate the class by passing in the data reader that is used to populate the primary business object.

I'm working now on a quote and sales order project. Quotes and sales orders share behavior, such as estimating the cost of an item. This behavior works as a separate class because it is not exclusive to a single domain object. I have a lot more CommandBase and ReadOnly objects in the project than I do editable objects in my projects.

Copyright (c) Marimer LLC