Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

OOP Confusion

by packetstormer (Monk)
on Mar 21, 2014 at 13:03 UTC ( [id://1079254]=perlquestion: print w/replies, xml ) Need Help??

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

Hi Monks

EDIT: Changed Package name from DB to MyDB to avoid conflicts - same query applies

I am struggling (still) with the practical use of OOP. I have written quite a lot of procedural code but I would like to start moving into OOP some more. My main question is less to do with code and more to do with logic!

A simple example is using OOP with a database.
Suppose I create a simple class called DB and within it a method called 'new' and a method called 'get_sites' as:

package MyDB; use strict; use DBI; use Config::Tiny; use Data::Dumper; sub new { my $class = shift; my $self = {}; my $file = shift || 'config.cfg'; my $cnf = Config::Tiny->read($file); my $dbh = DBI->connect("DBI:mysql:dbname=$cnf->{database}->{DATABA +SE};host=$cnf->{database}->{IP};port=$cnf->{database}->{PORT}", $cnf->{database}->{USER}, $cnf->{database}- +>{PASS}, {RaiseError=>1, mysql_enable_utf8 => 1}); $self->{dbh} = $dbh; bless $self,$class; return $self; } sub get_sites { my $self = shift; my $sth = $self->{dbh}->prepare("SELECT * from sites"); $sth->execute(); $self->{sites} = $sth->fetchall_arrayref({}); } 1;

Firstly, is it wise to place the database handle in the instance?.
Secondly, all the example I see manipulate (add, remove)to $self. However, I am unsure why this is needed. Take the above example, if I do:

#!/usr/bin/perl use strict; use MyDB; use Data::Dumper; my $dbh = MyDB->new('config.cfg'); my $test = $dbh->get_sites(); print Dumper $test;
I get the output of the sites table into my instance. However, I am not sure what I would want to use that again in this instance. So, if I called another method called, say, add_site, the $self->{sites} is still there, needlessly. I feel that logically, I should called DB->new() ahead of every other call i.e before I call delete_site or add_user because I can't see anything common between the methods!

I realise I am not explaining this too well but I am sort of wondering if there is even a need for OOP in this type of setup. I am having trouble understanding the beneifit. I have read "Object Orinented Perl" and I am still unsure how I would apply it to an easy web applicaiton

Replies are listed 'Best First'.
Re: OOP Confusion
by CountZero (Bishop) on Mar 21, 2014 at 17:28 UTC
    I suggest you take a look at DBIx::Class and the philosophy used there.

    In a OO-database object, you should generally avoid to store the data itself (unless you want to implement some caching system) as data is ephemeral. The object generally should represent a certain table or query, with methods e.g. that give you a row-object and methods of the row-object that give you access to the fields and its data.

    Mind you, this is not something for the faint-hearted and pitfalls abound.

    Personally I like DBIx::Class a lot, but there are other similar solutions which all do more or less the same in a slightly different way: Rose::DB is one module that springs to mind. SQL::DB is also an object-oriented database module but with a more "close to the metal" feel, yet not as basic as the venerable DBI.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

    My blog: Imperial Deltronics

      Whereas I, always the contrarian, do not find it to be particularly useful.   I really don’t need a tool to write the SQL for me, especially since the SQL in an application, once written, rarely if ever changes.   (The notion that “you can just change the schema and you won’t have to change your code!” is something that has never worked-out for me in real life, since substantial database changes, when they occur, always precipitate deeper changes in logic.)   This is just me, and my opinion won’t buy you a cup of tea.

      Also, I would want my “data acess objects” to completely conceal the existence of the database from all their clients ... not merely to provide, as it were, a database-API for them to use.   More than one of my data-access objects might well wind up interacting with the same database table or tables, should the implementation of the “application data ‘thing’” happen to wind up that way.   But, once again, just me, no tea.

      So, who’s “right, wrong?”   No such luck.   There’s more than one way to do it.™   The design has to be clean and maintainable, for a very long time.   And, I think, it should always have a boots-on the-ground reason that is not merely philosophical ... that is very firmly grounded in your application at its present state.   Whatever you wind up with, it should visibly make your application easier to design and to maintain, and it should neatly encapsulate a fair amount of its complexity.   It will need to work easily alongside whatever older code the app already contains, without forcing itself upon the older code in order for either one of them to [continue to] work properly.   If you have an existing app with a substantial amount of database work already in place, seriously consider not trying to make it “–er” by introducing another different way to do the same thing.   In the end, it’s all an engineering judgment-call.

        I'll tell you my experience. I have a large database (close to 700 000 records now) and the design remained as good as unchanged over more than a decade. I have set up all the usual DBIx::Class classes and getting the info I need out of the database and into LaTeX, Excel, R, ... is so easy with all the standard methods of DBIx::Class applied to my classes. I don't even have to enter passwords and such. Chaining its objects (in other words apply an SQL query to the result of a previous SQL-query) is a real time-saver. For hundreds of "one off" scripts, the boilerplate to connect, set-up, select and loop through the query results almost entirely disappears.

        And once you have to work with many-to-many relationships, you will really appreciate all the convenience DBIx::Class gives you.

        CountZero

        A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

        My blog: Imperial Deltronics
Re: OOP Confusion
by sundialsvc4 (Abbot) on Mar 21, 2014 at 13:59 UTC

    I would suggest this general approach:   create a base-class (use base blah;) which will be the ancestor of all of the data-access objects in your application (even if, as is quite likely, they are in turn based on something else from CPAN).   All of the functionality which is truly common between all of these objects is moved up to the base class.   (Constructors and so-forth exist within the children and they first call their SUPER::s.)   Issues that need not be visible to any client of the class, such as how to get database-handles when needed, would be dealt with there.)

    But now, let’s talk about philosophy.   (The following is, of course, IMHO.)

    A data-access object like this one should represent what this application needs to do with a particular item-of-data that is of interest to it.   This item-of-data might correspond to a single database table; or, it might not.   The public methods that are exposed should represent the means by which the rest of the application does things and how it knows things, such that unnecessary implementation details are concealed inside.

    Provided that the interface is “purposeful,” consistent, and clear, many other things are quibbles.   “There’s More Than One Way To Do It.™”   I suggest that you plan (before actually writing any code ...) what you think the set of objects and their associated public methods ought to be, so as to do what your application does, and to provide your application with what it needs to know along the way.   Put some serious advance thought into it, focused first on “what, not how.”

Re: OOP Confusion (package DB;)
by Anonymous Monk on Mar 21, 2014 at 13:07 UTC

    package DB;

    Yeah, DB is special, you want to stay out of DB :)

Re: OOP Confusion
by Bloodnok (Vicar) on Mar 22, 2014 at 12:07 UTC
    To a certain degree, I share your confusion - ordinarily you wouldn't expect the result of a database query to be stored within an object, however this may be the author's attempt at memoization whereby the results of a previous query are stored for efficiency reasons.

    That being said, the code snippet gives no clues as to the rationale behind the existence of the method e.g. why is the result of a get_sites() invocation not implemented within the constructor since it always returns a list of all sites i.e. takes no arg(s) that might constrain the result set in some way - unless it's available purely and simply to refresh the result set.

    Of course, as has been said elsewhere, the memoization directly raises it's own problems e.g. caching, and so is not a really sensible way of working; The method should, IMO, merely return the result set from the query, letting the caller determine to what use it should be put - by doing so it would provide the necessary degree of abstraction between the structure of the query on the backend DB and the data returned from the query - a la the GoF Facade Pattern.

    A user level that continues to overstate my experience :-))

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1079254]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (7)
As of 2024-03-19 02:49 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found