Monday, April 9, 2012

Duplication of work (oops) and Recipe Creation Sequence

With the clock ticking down WAY faster than I'd like, and with a week and a half hiatus on the horizon, I'm squeezing in every possible minute to work on Chef Helper. Then I'm writing code as fast as my hands can type, hoping that what I'm doing will work.

Unfortunately, I spent the last couple of days working on some functions that apparently I'd already completed. I guess a week ago or so, I built the methods to load my validation classes from the database and put them all into a separate class called validations. I decided a couple of days ago that each class should have the methods to load itself and keep a list of all the created objects of its type. I forgot I'd created loaders for the validations and just retyped them all into their classes from scratch. A simple copy and paste would have saved hours of work. Oy!

I did finish all of the loading methods and members of the validations and am moving on to the more complicated classes. I keep getting mixed up and turned around regarding some of the relationships. So I've had to go back to my initial database loader SQL script and try putting in at least a recipe or two. I've avoided putting in any recipes as yet, simply because they are a lot of work to enter directly and will be significantly easier to load through the finished program. But, of course, I need examples to work with to build the finished program.

I grabbed a recipe that looked good for my first example - Eggplant Parmesan. After I added the needed validations, I realized there was one recipe layout issue I hadn't counted on in ChefHelper. This recipe has the ingredients listed in three groups. How can I accomplish ingredient grouping, I wonder? I did go ahead and create all the necessary validations to make a blank ingredient, so I can at least create spacing. Which will do for now.

With that concern dealt with, I proceeded to create all the requisite components of the Eggplant Parmesan recipe, starting from the ground up. This is the same procedure that I will have to do programmatically in my user interface, so it's good to delineate here:

  1. Check that the requisite measure, fraction, meal part, and commitment exist. Since these validations are meant to be static, they should already exist and shouldn't be extended. 
  2. Check to make sure the needed food type exists. If it doesn't, create it. If it does, bring it in from the food types master list.
  3. Check to make sure the needed ethnicity exists.  If it doesn't, create it. If it does, bring it in from the ethnicities master list.
  4. Check to make sure the needed method exists.  If it doesn't, create it. If it does, bring it in from the methods master list.
  5. Check to make sure the needed foodstuff exists.  If it doesn't, create it. If it does, bring it in from the foodstuffs master list.
  6. Create a new Recipe. Feed into the constructor the known and applicable values. The only requirement at this point is a name. 
    • I realized at this point that the name needs to be unique because I won't get an ID until I store the record and I need the ID to store everything else. I don't want to have to refactor all the connections in the database this far into development, so instead of changing the name to be a primary key I instead require simply that it be unique and then check it against the master list before allowing the user to store a recipe.
  7. I now search for the recipe's ID by querying the database by name. This ID will be used in Ingredients, Instructions, Notes, and Meals.
  8. I created each Ingredient. Since I've already checked that the needed foodstuffs exist, this is simply a matter of plugging in the needed constructor and feeding it all the values. One of these values is the recipe ID I determined earlier.
    • It was perhaps a good idea to use a complicated recipe for my demo, because it showed me that I needed more space for the preparation. I didn't anticipate really long instructions but when adding a whole item, I found I needed space for the description of the size and volume of that item. In my example, this amounted to: 1-pound, peeled and cut crosswise into 1/2-inch-thick-slices, each, for two eggplants. I increased the varchar for prep from 45 characters to 100.
  9. I created each Instruction. This was simply a matter of plugging in the needed constructor and feeding it all the values. One of these values is the recipe ID I determined earlier, and it's the only validated value.
Wow, it was only 9 steps but for 19 ingredients and 4 instructions, it took me in the neighborhood of 2-3 hours to create it using direct SQL. I am quite looking forward to accomplishing the same feat far more quickly using my user interface.


Now comes the fun parts I've been dreading - creating constructors for these last 3 classes.  Perhaps the best way to go about this is to create a very basic constructor and use the getter and setter methods to handle all the object attributes...

I tried to start with Ingredient, but it needed a Recipe object to associate itself with. So I tried to start with Instruction, but it also needed a Recipe object to associate itself with. It became clear that I needed to start with Recipe. But guess what Recipe needs? An ArrayList of Ingredients and an ArrayList of Instructions. Oh, and a primary Ingredient and a secondary Ingredient. Yeah, I kinda painted myself into a corner here.

So how did I get to this state? To handle the multiplicity of the Ingredients and Instructions, I had to associate them with their recipe through a foreign key in their tables. To build the objects in Java, I needed the Recipe to contain lists of Ingredients and a list of Instructions.

I've got some ideas on how to refactor to get away from this, but I figure this post is already way too long. If you're still awake, I'll see you next time!

No comments:

Post a Comment