Shooting the Prototype

Ovid on 2008-02-20T12:50:24

I had a curious issue in some code I'm writing and the code works really well, but I finally compromised my design due to internal criticisms of a tiny complication I introduced.

# this is an oversimplification

while ( my $row = $dataset->next ) {
    my @data = $row->fetch_all;
    print "@data\n";
}

Basically, we create datasets and each dataset has rows. The dataset contains the row metadata, so each row needs to know which dataset it is contained in. So let's say I have 30 rows in a dataset. I could add a dataset slot to each row, but then I'm increasing the memory usage of each row (some datasets could be huge) and if we ever needed to swap out the dataset in the rows, we'd have to manually do that for every row. Basically, this sounds like class data.

I can't just make it class data, though, because if we have multiple datasets at the same time, setting the dataset as class data alters the dataset for all rows, not just the rows which need it.

What I had, internally, was this:

# in the constructor
my $row_class = DataSet::Row->spawn_class;

# later, in the 'add_row' method
push @{ $dataset->{rows} } => $row_class->new($row);

In other words, when I create a dataset, I create a new, anonymous subclass of my row objects. I can then have the dataset set as class data on the new anonymous row class and the problems I mentioned earlier go away. This seems rather like prototype-based programming and confused the heck out of the other programmers when they encountered it. They argued (correctly), that we're unlikely to ever need to change the datasets for rows once created and the memory usage is actually trivial. I made the design a bit too complicated while trying to anticipate future uses of this code.

Sigh. They're right. It was a quick change making the dataset instance data, tests still pass, and I can now finish my original task of adding virtual columns to the dataset.

Update: oh, the irony. As soon as I removed this code, I needed to implement virtual columns. To make this efficient, we needed to do a fair amount of data caching in the row class, requiring me to add this code back in :)