Entries from March 2008 ↓

Static method abuse

I dislike static methods, there I said it.

Like everything, they have their place; but as the old analogy says, when you have a hammer everything looks like a nail. Static methods are being abused.

Don’t make me instantiate

For some reason programmers seem to be allergic to instantiating objects, to the point of where half the functionality is implemented in static classes and methods. You can go for days without seeing an instance in my current project’s code-base.

It’s certainly interesting to see. As developers who program in object-oriented languages, when it comes down to it, are being paid to instantiate objects.

I think it’s symptomatic of a bigger problem. If you think creating instances is wasteful, maybe you’re creating too many. You’re most likely violating the Single Responsibility Principal (SRP) or Separation of Concerns (SoC). Too many dependencies usually means your class is trying to do too much, get it spit-up, and get it clean.

State vs. stateless

One justification for using static methods that is often touted around is that if a method doesn’t have any state, then it should be static. To me this is a fallacy, because state isn’t everything. Take the following repository example:

Using static methods
Customer customer = Repository<Customer>.FindByID(102);

customer.Name = "James";

Repository<Customer>.Save(customer);
Using an instance
IRepository<Customer> repos = new Repository<Customer>();

Customer customer = repos.FindByID(102);

customer.Name = "James";

repos.Save(customer);

Noticably the first example is one line less than the second. However, it’s compromising readability in removing that line. Instead of using the repos instance, you’re forced to fully-qualify every method call with Repository<Customer>, which is introducing more noise per-line.

Even though the methods FindByID and Save don’t have any shared state, they both form a part of the encapsulation of data-access (in this case).

If a method forms a key part of an encapsulation, then it shouldn’t be static.

When you make the decision of creating a static method due to it being stateless, you’re revealing more implementation details than necessary. A big part of encapsulation is hiding implementation details from the consumer.

While the methods are stateless, they might not always be that way; perhaps the repository could share a session between calls in the future. With an instance based design, you won’t have any problems. Try introducing state into a static method, and things either get very messy, or you end up converting everything to instances and having to rewrite any usages.

Static methods tie you in at an early stage to a specific design, making it very difficult to refactor out later.

Not all bad

So where are the good static methods? The Math class is a good example. It contains a set of functions that are only loosely related (apart from being mathematical), that are guaranteed to be stateless, and most importantly are simple. Architecturally the methods in the Math class could be applied to a wide swath of objects (int, float, double, decimal etc…) and to have them as instance methods would complicate the class hierarchy more than it would benefit it.

Static methods should be fire-and-forget, disposable, simple, and effective.

Object-orientation

Static methods aren’t associated with an object, they’re tied to a type. This is a big distinction. People are under the belief that because statics sit in the same class definition as instance methods, that it makes them object-oriented.

Two key points of object-oriented design that static methods violate are inheritance and polymorphism. The inability to substitute a method with an implementation in a derived class is pretty unforgivable, and heavily restricting.

Finally, testing

The argument that you’re most likely to encounter against using static methods is that of testing. It’s certainly one that I agree with, I just didn’t want it to seem like that’s the main reason of my argument.

Statics are bad for testing because they can’t be substituted easily. A major part of unit testing is being able to isolate what you’re testing from it’s dependencies; isolation allows you to verify one part of the system at a time. Static methods aren’t overridable in a sub-class, and so they aren’t mockable either (without the use of TypeMock).

Of the two examples used above, in the first example it would be very hard to test that code without actually going to the database, because of the ties to the static methods. The second example could be refactored so the IRepository dependency is injected, and thus replaceable at test time.

Not being able to test in isolation is the final nail in the coffin for static methods and me.

Plug-in’s and browsers

Jeff Atwood (of Coding Horror) tweeted earlier: “On Firefox: ‘add-ons aren’t a big draw for me — I just need a browser, not a circus.’”. I don’t know whether this was a quotation of himself, or someone else; but it’s something that I’ve heard mentioned before.

Just because they’re there, doesn’t mean you have to install them.

A plug-in model in your browser allows you to install as many, or as few, plug-ins as you need. A lot of them are useless, but if there’s one that can boost your productivity, isn’t that worth it? I’d rather have a browser with a plug-in model, and one really useful plug-in, than a browser without both.

A prime example is FireBug, I couldn’t live without that plug-in. I feel stranded in IE or Safari when it comes to interrogating pages.

FireBug possibly highlights a hole in the developer support from the Firefox development team itself, but with a plug-in model you can fill that hole yourself rather than relying on their team; like we’ve had to do for years with Microsoft and Internet Explorer.

/rant off

getfilename NAnt task

As part of my current quest to fully automate our build, I found my self needing the ability to copy a database backup from our remote server. The backup is in a folder along with several other backups, with a filename based on the date. I didn’t fancy trying to programmatically guess the filename, so I wrote an NAnt task to grab the newest file in a directory. Thanks to Richard Case for his overview of how to create a custom NAnt task.

The getfilename task simply gets the filename of a file in a directory, then pushes the name into the specified property. The filename to find can be based on the creation date, last modified date etc…

The attributes are as follows:

Attribute Description Required
in The directory to search Yes
property The property to push the filename into. Yes
searchPattern Wildcard search pattern for finding the file. No
of The file to get the filename of.

NewestFile - The most recently created file
OldestFile - The oldest file in the directory
FirstModifiedFile - The file with the oldest last modified date
LastModifiedFile - The most recently modified file
FirstFile - The first file in the directory, using default sorting
LastFile - The last file in the directory, using default sorting

No - Defaults to NewestFile

An example usage is:

<?xml version="1.0"?>
<project name="Example" default="run">
    <target name="run">
      <getfilename of="NewestFile" in="C:\path\to\backups" searchPattern="*.bak" property="filename" />
      <echo message="Filename: ${filename}" />
    </target>
</project>

Foreseeable usage situations revolve around anything where you’d need to get the last modified, or newest file in a directory; backups, database scripts etc…

Downloads

The DeleGrid is open-source under the new BSD License; read the license for what you’re allowed to do.

You can download the source here: Download Source.
You can download the latest binary here: Download Binary.

The source is also accessible from Subversion at: http://jagregory.googlecode.com/svn/trunk/JAGregory.NAntTasks/ (using user jagregory-read-only)

kick it on DotNetKicks.com

Introducing the filterable DeleGrid

The DeleGrid is a paged GridView control that handles data-binding through the use of events and delegates rather than with a traditional collection.

What this means is that you have full control over the data that is shown in the currently displayed page. Traditionally you’d retrieve the whole recordset then page it locally, but with the DeleGrid you can utilise your database/ORMs paging features.

To quote myself from when I first introduced the DeleGrid:

[The DeleGrid] came about because I wanted a nice way of implementing paging using NHibernate without having the grid know about it. I really didn’t want NHibernate to leave my data layer, so I needed a nice way of the grid calling my DAL with the paging parameters.

What’s new?

The biggest change in version 1.1 is the introduction of filtering. The filter isn’t generated in some black-box fashion, instead it’s defined by the programmer. It’s built up from the columns in the grid, which define their own filtering behavior.

The filter acts upon any columns in the grid that implement the IFilterableField interface. Implementing this interface in your own fields is easy, so you’re quickly able to create custom filtering behavior for your grid. An example would be a date column, that has a date-picker as a filter control.

Filter Screenshot

I’ve chosen not to implement the wealth of appearance customisations that are available in the normal templated controls. This is down to two reasons, firstly I don’t agree with it, appearance should be controlled soley through CSS. Secondly, there are so many, I couldn’t be bothered. So you’re only able to attach CSS classes to the buttons and cells, and specify the image urls for the buttons.

An example implementation

The grid now has a companion project, it’s own data library. This separation is to keep you from having to reference System.Web in your data-layer. To use the DeleGrid in your project you’ll need to reference JAGregory.Controls.DeleGrid.dll and the JAGregory.Controls.Data.dll in your web project, then reference JAGregory.Controls.Data.dll in your data layer, if they’re separate projects.

So to begin with, add the reference to JAGregory.Controls.DeleGrid.dll and JAGregory.Controls.Data.dll into your web project. This will allow you to use the DeleGrid in your page. Once you’ve done that, you’ll need to reference the control in your page, you can either do this using a Register tag in your page, or in the web.config as so:

<pages>
  <controls>
    <add tagPrefix="jag" namespace="JAGregory.Controls"
      assembly="JAGregory.Controls.DeleGrid" />
  </controls>
</pages>

With that in place, you can now put the DeleGrid into your page:

<jag:DeleGrid ID="grid" Runat="server" AllowFiltering="true"
  AutoGenerateColumns="false">
    <FilterStyle ToggleOnImageUrl="img/find.png" ToggleOffImageUrl="img/find.png"
      ExecuteImageUrl="img/go.png" />
    <Columns>
        <jag:FilterableTextField HeaderText="Name" DataField="Name" />
        <jag:FilterableBooleanField HeaderText="Active" DataField="Active" />
    </Columns>
</jag:DeleGrid>

n.b. In this example I’m using a Customer object to bind against it, which simply has the Name and Active properties.

For this grid we’ve set AllowFiltering to true, which enables the filter, then we’ve set AutoGenerateColumns to false so we can add our own custom columns. The two columns both implement the aforementioned IFilterableField interface, which allows them to define their own filtering behavior.

I’ve also set the image urls so the buttons will be visible.

Now that the page is set up, we need to get down to the binding. In your code-behind:

protected override void OnInit(EventArgs e)
{
    base.OnInit(e);

    // attach the events for requesting the data and totals
    grid.TotalRecordCountRequest += grid_TotalRecordCountRequest;
    grid.PageDataRequest += grid_PageDataRequest;
}

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);

    if (!IsPostBack)
        grid.DataBind();
}

What’ve just done is attach the TotalRecordCountRequest and PageDataRequest handlers to the grid, which respectively fetch the total record count for the full grid, and fetch the current page of data from the database; the implementations are below.

private IEnumerable grid_PageDataRequest(object sender, PageDataRequestEventArgs e)
{
    CustomerRepository repos = new CustomerRepository();

    // get the requested page of data from the database
    return repos.FindAllPaged(e.Range, e.Sort, e.Filters);
}

private int grid_TotalRecordCountRequest(object sender, DataRequestEventArgs e)
{
    CustomerRepository repos = new CustomerRepository();

    // get the total records
    return repos.GetAllCount(e.Filters);
}

I’m using a repository pattern to handle data-access. In the PageDataRequest handler we’re taking the range, sort, and filter info that the grid passed us and sending it off to the repository to get the data. Similarily the TotalRecordCountRequest handler does a similar thing but without the range or sort info.

That’s it really for using the DeleGrid, you just need to take the filter info and handle it using your specific ORM.

Repository implementation

Ok I’ll throw you a bone, here’s the repository implementation to show how easy it is using NHibernate:

public class CustomerRepository
{
    /// <summary>
    /// Creates a NHibernate ICriteria based on the filters.
    /// </summary>
    /// <param name="filters">Filters to apply.</param>
    /// <returns>ICriteria</returns>
    private ICriteria CreateFilteredCriteria(FilterCriterionCollection filters)
    {
        ICriteria criteria = SessionManager.CurrentSession
            .CreateCriteria(typeof(Customer));

        // criterion handling - write this yourself depending on how your
        // db filters (and what filter types you're supporting)
        foreach (FilterCriterion filter in filters)
        {
            if (filter.Type == typeof(string))
                criteria.Add(Expression.Like(filter.FieldName, "%" + filter.Value + "%"));
            else if (filter.Type == typeof(bool))
                criteria.Add(Expression.Eq(filter.FieldName, filter.Value));
        }

        return criteria;
    }

    /// <summary>
    /// Gets the total record count from the database using the filters.
    /// </summary>
    /// <param name="filters">Filters to apply before getting the count.</param>
    /// <returns>Total number of records in the filtered list</returns>
    public int GetAllCount(FilterCriterionCollection filters)
    {
        return CreateFilteredCriteria(filters)
            .SetProjection(Projections.Count("ID"))
            .UniqueResult<int>();
    }

    /// <summary>
    /// Gets one page of data from the database.
    /// </summary>
    /// <param name="range">Select range (start ID and number of records).</param>
    /// <param name="sort">Sorting to apply.</param>
    /// <param name="filters">Filters to apply.</param>
    /// <returns>List for one page of data.</returns>
    public IList<Customer> FindAllPaged(SelectRange range, SortInfo sort, FilterCriterionCollection filters)
    {
        // create the criteria using the filters, then set the range
        ICriteria criteria = CreateFilteredCriteria(filters)
            .SetFirstResult(range.Start)
            .SetMaxResults(range.Size);

        // only add the sort if one is specified
        if (!string.IsNullOrEmpty(sort.Field))
        {
            if (sort.Direction == Direction.Asc)
                criteria.AddOrder(Order.Asc(sort.Field));
            else
                criteria.AddOrder(Order.Desc(sort.Field));
        }

        return criteria.List<Customer>();
    }
}

The CreateFilteredCriteria method is doing most of the leg work. It takes creates an NHibernate criteria, then adds any filter criterions to it. It iterates the filters collection, checking their type and adding the appropriate NHibernate criterion. Simple!

The example project

I’ve attached a sample project that uses the grid to display a collection of customers that are paged and filtered. The example uses a SQLite database with NHibernate for data-access, I’ve done this to keep the extraneous code to a minimum.

Downloads

The DeleGrid is open-source under the new BSD License; read the license for what you’re allowed to do.

You can download the source here: Download Source.
You can download the latest binary here: Download Binary.
You can download the example project here: Download Example.

The source is also accessible from Subversion at: http://jagregory.googlecode.com/svn/trunk/DeleGrid/ (using user jagregory-read-only)