December 2006 - Posts

In the task details interface, when we create or update the task, certain properties were created that we no longer need.  We have this setup right now:

task.Name = this.TaskName;
task.Category = this.Category;
task.SetPropertyValue<bool>("IsReoccurring", this.IsReoccuring);
if (this.cboReoccurrenceMeasurement.Visible)
     task.SetPropertyValue<string>("ReoccurrenceMeasurement", this.ReoccurrenceMeasurement);
if (this.txtReoccurrenceAmount.Visible)
     task.SetPropertyValue<int>("ReoccurrenceAmount", this.ReoccurrenceAmount);
task.SetPropertyValue<string>("Description", this.Description);
task.SetPropertyValue<DateTime>("CreationDate", DateTime.Today);
task.SetPropertyValue<DateTime>("DueDate", this.DueDate); 

But, we don't need most of the reoccurring information and the due date; this will now be rolled into the Triggers collection that belongs to the task.  The reason is, maybe we don't want due date limitations, but would rather want limitations based upon other factors, such as mileage or another evaluation method.  With the new features coming up, task reoccurring criteria will be more advanced.

So, instead of using this, I loop through and create what I call trigger objects.  What is this trigger and value objects I mentioned before?  What I am using a trigger for is to allow the user to setup multiple constraints.  For instance, if you want to set multiple constraints on an item, you can do so.  Take an "Oil Change" task, for example, you can set two triggers on it: a trigger based on the mileage and a trigger based on a specific period of time.  The mileage trigger can be based on a value; a static list of values that can be updated by the user, and whenever the value is updated, any trigger associated with it can be updated as well.  This value will be in a list that can be updated whenever you would like.

I have a method that looked like this:

public override float EvaluatePercentComplete(MeasurementTrigger trigger)
{
    int range = (DateTime.Today.Subtract((DateTime)trigger.LastValue).Days);
    int total = ((int)trigger.TriggerValue);
    if (total == 0) throw new ArgumentOutOfRangeException("total", "The total value denominator is zero");
    return Convert.ToSingle(range / total);
}

But, I ended up refactoring it like this:

public override float EvaluatePercentComplete(MeasurementTrigger trigger)
{
    if (((int)trigger.TriggerValue) == 0) throw new ArgumentOutOfRangeException("total", "The total value denominator is zero");
    return Convert.ToSingle((DateTime.Today.Subtract((DateTime)trigger.LastValue).Days) / ((int)trigger.TriggerValue));
}

Is that a useful refactoring?  After all, it is a complicated division, and is untested code as of now.  But, I remember reading Martin Fowler's comments about avoiding the declaration of variables when possible; he found that it was often just as quick to perform the conversion directly and use the object instead.  But, to really know if it is a performance problem requires some sort of profiling tool at the end of the process.

I leave the final word up to you...

Some of the properties that were created look something like this:

public string Category
{
 get
 {
  if (this.Properties.Contains("Category"))
   return this.Properties["Category"].GetValue<string>();
  else
   return null;
 }
 set
 {
  if (this.Properties.Contains("Category"))
   this.Properties["Category"].Value = value;
  else
   this.Properties.Add(new MetadataProperty("Category", value));
 }
}

I created a get and set method to do the same thing that you see above, which reduces the lines of code, but uses generics to be type-specific.  The new version of the property looks like this:

public string Category
{
 get { return this.GetPropertyValue<string>("Category"); }
 set { this.SetPropertyValue<string>("Category", value); }
}

I also had an event argument class that I could get rid of.  There will be other slight changes that need made like this, like renaming method, object, or property names that will be made.

Some other refactorings that were found, as I work through  the code to add triggers/values (to appear next), I found that there will be changes to the Task object.  PercentageDue will not be needed, as there will be many possible values that are due, as well as the complete method.  Complete method will be moved to a lower level during refactoring that will make it more useful.  I don't know completely how that will work, so these values will be refactored later; however, I know they need to change, and they've been identified.

I also have a TaskCollection class, which has three special events that I used:  TaskAdded, TaskCleared, and TaskRemoved, which I used in the application; however, the MetadataEntityCollection generic class has these methods, as well as going beyond, to handle this capability.  So, I plan to remove this class.  I did a find on the TaskAdded event, and found it in one place in a form.  So I changed the event declarations to my base class:

MGR.Tasks.ItemAdded += new DataEventHandler<Task>(Tasks_ItemAdded);
MGR.Tasks.ItemCleared += new EventHandler(Tasks_ItemCleared);
MGR.Tasks.ItemRemoved += new DataEventHandler<Task>(Tasks_ItemRemoved);

I moved the code so it appears as this:

void Tasks_ItemAdded(object sender, DataEventArgs<Task> e)
{
    //Code to add a new task in the Task interface
}

void Tasks_ItemCleared(object sender, EventArgs e)
{
    //Code to clear tasks in Task interface
}

void Tasks_ItemRemoved(object sender, DataEventArgs<Task> e)
{
    //Code to remove task in Task interface
}

Tasks_ItemAdded(object sender, DataEventArgs<Task> e)
{
    //Code to add a new task in the Task interface
}

void Tasks_ItemCleared(object sender, EventArgs e)
{
    //Code to clear tasks in Task interface
}

void Tasks_ItemRemoved(object sender, DataEventArgs<Task> e)
{
    //Code to remove task in Task interface
}

void Tasks_ItemCleared(object sender, EventArgs e)
{
    //Code to clear tasks in Task interface
}

void Tasks_ItemRemoved(object sender, DataEventArgs<Task> e)
{
    //Code to remove task in Task interface
}

void Tasks_ItemRemoved(object sender, DataEventArgs<Task> e)
{
    //Code to remove task in Task interface
}

I just wanted to drop a note that I will be beginning the 1.1 version of Reminder.NET, which will have some new features planned.  The next set of features, after talking with the users, will include some additions to the expirations part of the project, such as allowing additional custom values to expire by.  In addition, notifications is an important feature, and this was deemed necessary.

There were some readability issues in the names we chosen, such as the base class MetadataElement/MetadataAttribute.  Entity/Properties was a better association, so this change was made to MetadataEntity/MetadataProperty.  In addition, several refactorings need to be made, as other projects I did had a better approach, so I will be making those changes as well, as you will see.  This should be a very useful several iterations to increase the value of the product.

Posted by bmains | with no comments
Filed under: