Boldra has asked for the wisdom of the Perl Monks concerning the following question:

I read somewhere that it's sensible to define search methods as class methods - put them inside the class definition of the objects they return. eg if I have a class called 'File', then as well has having 'File->new' return an object , I can have 'File->search' return an array of objects. This is supposed to be better than creating an object especially for searching.

(I can't remember exactly why this is meant to be good, nor where I read it - maybe there are exceptions?)

If I do do it this way, how do I pass the necessary information to another class about where it should search? I could just pass the class name, like so:
my $ui = UI->new; $ui->query_dialog( { search_with => 'DB::File' } ); package UI; sub query_dialog { my( $self, $args ) =@_; print "Name? "; my $name = <STDIN>; my $search_class = $args->{search_with}; my @files = $search_class->search( { name => $name } ); ... }
but this would require my 'UI' package to 'use' or 'require' the class, which seems messy.

How, if I want to keep my search object and my UI object totally ignorant of each others class, should I pass the search object?


- Boldra

Replies are listed 'Best First'.
Re: search methods as class methods?
by ELISHEVA (Prior) on Mar 02, 2009 at 11:48 UTC
    As to your first question, to extend moritz's feedback - it will be hard to define a class level search unless all found objects take the same parameter set. Basically class level search methods are factory methods that call new for a set of objects, rather than one object at a time. They are easier/more efficient for your users if and only if users actually need to batch create objects.

    They don't have to be parameter-less (e.g. new()), but your search method does need to have a consistent way to create multiple objects and to generate the parameters needed for each object. For example, you might have a search method that takes a URI that identifies the physical storage to search. Your factory method could use the URI to deduce a physical implementation (i.e. class) for the found objects. Then your class level search method would convert the data retrieved for each found object into an instance of that class, perhaps by passing the data as parameters to the class.

    As to your second question - Trying to design an interface without a clear idea of your use cases or your target user group and their needs I fear is going to be an exercise in futility. There are just too many alternatives.

    Take, for example, your idea of passing in a closure or other subroutine for searching. This can be a very good idea, but it tends to be most effective (for your users) if the selection rules used by a search involve complex logic that is difficult to express using just data. If a list of fill-in-the-blank search criteria is what your users need, then making them write closures is going to be a big turn-off.

    Or consider search criteria. In my opinion, it is a bit dubious to try to model search criteria without a clear idea of your user community. You might have some luck if your search finds things that you have a lot of experience with (e.g. past exposure to users/yourself as a user) or if X's are well defined and have industry standards and search conventions (e.g. books are usually categorized by title, subject, and author). If you don't have either of these, you are likely to either over or underscope the proposed search criteria.

    You also indicate that your search strategy may have to be applied to a wide range of physical storage mechanisms. Implementing even one can be time consuming, so here it is especially important to prioritize which ones are likely to be more or less important. It is also highly likely that the data and functions needed for each storage mechanism will vary widely. You might be able to define a common set of access functions, but then again you might not. Just for starters, consider the difference between searching an in-memory and database storage pool. One uses variables and chains of hash keys for access. The other might require a URI, password, and search criteria morphed into an SQL statement.

    So my first recommendation is to choose some use cases before you decide whether you need fancy things like code references passed in as data or class methods X,Y,Z.

    If you are planning a project proposal and don't have any actual users, then try some market research and take a stab at what will most likely be needed and focus on those use cases.

    If you are afraid of missing some general themes by focusing on specific use cases, then choose two or three very different use cases. You can use the tension between the different use cases to sort out which is specific to the use case and which represents aspects of the general problem. The main thing though is to focus on use cases that are likely to have a real market/user community.

    Best, beth

      Wow, what a detailed reply! Many thanks Elisheva, you've convinced me to go back think about specific use cases and even write some tests before I go any further.


      - Boldra
        .oO(Hmmm, isn't that what you've been doing thus far? You naughty developer, you :-D)

        A user level that continues to overstate my experience :-))
Re: search methods as class methods?
by moritz (Cardinal) on Mar 02, 2009 at 10:49 UTC
    If the usual use case of your module looks like this:
    use YourModule; my $obj = YourModule->new(); my @result = $obj->search($params)

    Then it's useful to offer ->search as a class method.

    If however there are a few parameters to ->new() or some other state information is added between the calls to new and search then I wouldn't go through all the trouble of making these informations available as extra parameters to a class method search.

    So don't go through the trouble of changing your interface because you read somewhere that it might be better that way. Only do that if you understand the concept, and you're convinced that it's actually better.

      At this point, these are completely new classes. I'm trying to build a UI prototype (Win32::GUI), and simultaneously keep the interface to my search as flexible as possible for two reasons:

      1. I don't know yet whether that search will be running on a local database, a local filesystem, or some kind of RPC call.

      2. I'm fully expecting the customer to say "we want it to run on linux too"

      I've also now hit upon the idea of passing the search method as a coderef. Does anyone have any comments on that approach?


      - Boldra
Re: search methods as class methods?
by tilly (Archbishop) on Mar 02, 2009 at 14:46 UTC
    Amplifying on what moritz said, it is important for you to develop your own opinions and go off of them, then monitor the results. That will help you learn, and will help ensure that your designs implement the ideas in them fairly well. Furthermore in programming there are trade-offs in every decision, which means that you can construct arguments for or against opposed approaches. As you analyze your own experience, focus on what those trade-offs are. Then when you're faced with a new situation you'll be in a better position to say, "These are the ways we could go and I would prefer to do X because it is what I have done before."

    With this in mind, what are the trade-offs in having search methods as class methods of the class of thing that you are searching for? The benefit is simplicity. When every class has a second class that is about how to search for those things, it becomes easy to create a twisty maze of classes, all alike. The drawback that it isn't semantically clean, and requires your class to know about all of the different ways it might be stored and organized. Conceptually that is the wrong place to put that information.

    Good programmers can fall on either side of the fence. For instance if you look at the world of object relational mappers, DBIx::Class puts search methods in the object's class, while Rose::DB::Object puts searching in a manager class (but grudgingly provides the ability to create search methods in the object's class).

    My personal opinion is that if you anticipate storing objects in multiple places in multiple ways, then have searching be in a different class. That will give you the flexibility to add new ways of searching in new places that require different kinds of information. If you know the data will always be stored in just one way in just one place, I would be personally inclined towards not creating a new class for the search method. Though I have no trouble using code that went the other way instead. (I do prefer Rose::DB::Object over DBIx::Class.)