Hello Monks

Recently I have been messing around with building an enhanced benchmark suite. In order to achieve my goals I have decided to use an object oriented approach. I completed a survey of Benchmark, spent some time coming up with a reasonable object model and interface and even completed most of the coding for my new functionality. So far so good.

Originally, for compatibility and reliability reasons I had intended on using the timing routines from Benchmark in order to do most of my actual benchmarks, with my code mostly handling administrative tasks. This seemed a reasonable approach as Benchmark is part of the standard distro and therefore should be well tested and so forth. My initial analysis showed that I could simply replace one routine with my own (ie not call it but do its job, plus my own) and thus my class would just be a seperate inteface into the module with additional functionality.

Wrong.

It turns out that I overlooked a couple of minor issues and will have to modify futher aspects of Benchmarks behaviour. And to make it worse it seems that the module is not in the slightest bit object oriented. Despite the fact that there are Benchmark Objects, the internal architecture of the module is primarily procedural and therefore doesn't really allow overriding methods. To achieve what I want to do I will have to resort to messy subroutine redefinitions or take the time to convert the module to a more OO structure.

And now for the question :-)

This seems to be an issue when dealing with some of the older modules on CPAN or modules in the standard distro. Sure they are good at what they do, but occasionally you want to extend them, but often they aren't really structured to be extended and reused. So what to do? Go through the contortions of a workaround? Contact the authors and hope there is an OO version coming down the pike? Roll yer own? Rewrite the module go from there?

I'm curious as to whether this has been an issue for other Monks, and what approach you all think is reasonable.

Thanks, Yves
--
You are not ready to use symrefs unless you already know why they are bad. -- tadmc (CLPM)

Replies are listed 'Best First'.
Re: Working with Standard Modules...
by jmcnamara (Monsignor) on Sep 26, 2001 at 12:47 UTC

    In general it is best to sub-class an existing module. Although it may not be easy or straightforward it is generally possible.

    For example the Mail::Box module gathers together several existing mail modules under a single OO interface. This entailed some of the problems that you are facing.

    The author, Mark Overmeer, gave an excellent talk about it at YAPC::Eu 2001. The slides are here.

    In particular he demonstrated a very clever idea for using AUTOLOAD to re-bless an object into a class that supported a called method. (This isn't directly applicable to your case but it is interesting nevertheless)

    John.
    --

      In general it is best to sub-class an existing module. Although it may not be easy or straightforward it is generally possible.

      Unfortunately in this case it doesn't seem to be very feasable. The problem is that almost all of the calls within the module are hard wired to each other. Even though there is a Benchmark object (blessed using the single argument form!) none of the routines take $self references so they can't be called as methods, and should I call one as a procedure then it will call all of its friends as procedures, completely ignoring what overriden methods I have provided.

      So the question for me was total ground up rewrite in my own module or to convert the entire thing into an OO version?

      I chose the later and have now completed the majority of work converting it into an OO structure. This basically meant trawling the code for subroutines and subroutine calls and adding in a $self pointer, splitting out functionality into different objects etc. Now I could reimplement the original package just through calls to mine, but no way I could go the other way.

      Once Im done I plan to post it here for revue and comments.

      Although as the module has no copyright information I think I might need permission.

      Yves
      --
      You are not ready to use symrefs unless you already know why they are bad. -- tadmc (CLPM)

Re: Working with Standard Modules...
by dragonchild (Archbishop) on Sep 26, 2001 at 19:08 UTC
    We're running into the exact same problem at my job with Spreadsheet::ParseExcel and OLE::StorageLite. Spreadsheet::ParseExcel does a number of horrific things, not limited to
    • No whitespace (It's free, guys!)
    • Hungarian notation (Why do you need it in Perl?!?)
    • Several packages in one file
    • A ton of magic numbers with no explanations
    A coworker of mine is working on OLE::StorageLite is frustrated by its interesting OO implemetation. For example, it uses magic numbers (defined by M$oft) to determine which class to instantiate. This is all done through a single constructor. Polymorphic constructors are all good, but the determination of which class is made using a series of if-else clauses, not a single function. And, it only gets better from there.

    Personally, I'd rewrite it, submit it as OO::Benchmark and start a new 'class' of module. :-)

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

      In general, I'd like to give a gentle shove in the direction of patching the module and keeping the name the same. If that turns out to not work, then any new module dealing with benchmarking should really go under Devel::, so I could also support patching Benchmark.pm into Devel::Benchmark and making Benchmark.pm deprecated.

      But I'd prefer to not have two separate places where the basic timing code is actively maintained. That type of thing happens a lot, but that doesn't mean that it doesn't suck. Take any number of interesting problem domains and try to find the best of the many CPAN modules that address it and you'll see what I mean by "sucks". (:

              - tye (but my friends call me "Tye")
        But I'd prefer to not have two separate places where the basic timing code is actively maintained

        Exactly! Thats why I wanted to use the timing code from Benchmark in the first place.

        Incidentally I think one problem with actually deprecating Benchmark is that there is a fundamental difference in psychology to using a module and using an object. In a module you expect to get everything you need. In an object you expect to only get what makes sense.

        Consider in Benchmark you have code from 4 different groups of functionality:

        • Time object manipulation routines
        • Timing routines (ie actually calculate th time elapsed)
        • Caching routines
        • Report production routines
        So how to rework this? My current plan is bascially this:
        Benchmark::TestBench -- Timing routines Benchmark::Testbench::Std -- The high level report Benchmark::Result -- A time object Benchmark::Testbench::Test -- new, used by ::Series Benchmark::Testbench::Series -- New, replaces ::Std
        Right now the question is whether timethis,timethese and cmpthese should go into the same class that has the actually timing routines. I say no, because they have more to do with handling user input and formatting and printing results than anything else.

        Anyway, i should have been in bed long ago so...

        Till next time. :-)

        Yves
        --
        You are not ready to use symrefs unless you already know why they are bad. -- tadmc (CLPM)

      You should see the source for SOAP::Lite!! Don't get me wrong - the functionality is great, i owe a lot to the authors, but open up the hood . . .
      • undocumented methods (seek and ye shall find!)
      • several more packages in one file
      • and my favorite first line in a method: shift->new()
      At least the code is well formatted. Sometimes, that's all i really need. :)

      jeffa