SitePoint Sponsor

User Tag List

Page 1 of 3 123 LastLast
Results 1 to 25 of 67
  1. #1
    SitePoint Enthusiast
    Join Date
    May 2002
    Posts
    75
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    What's so good about OOP?

    Now, in Perl or PHP, OOP is nothing more than having subroutines and variables separated into a different file (or module). I really don't see the point of declaring a package or class in Perl and PHP respectively.

    The only difference I see is that the methods can access attributes declared outside the methods itself and within the class or package. But besides this, I don't see any benefits of using OOP.

    Maybe someone can quote me with a few examples? You don't have to quote complex ones like databases. Just a simple example like the classic "hello world" will do.

    Many people told me that with OOP, you can add functions to a script very easily. I don't know why.

  2. #2
    SitePoint Addict
    Join Date
    Feb 2002
    Location
    Atlanta, GA
    Posts
    342
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The stock answer is: Abstraction, encapsulation, inheritance and polymorphism.

    As PHP is OO capable but not really OOP, you can usually program a solution faster using a procedural approach.

    In the long run, however, by taking an OOP approach and putting in the time to "objectify" the problem(s) at hand, you'll have a more robust and extensible application.

    I hear that PHP 5 will go further down the OOP road, and I hope they do. I for one would like to see an OOP paradigm approximating that implmented in JAVA.
    To start, adding multiple constructor support to PHP-Objects would be a great.

    As ever,

    Pete
    Last edited by phpPete; May 12, 2002 at 05:16.


  3. #3
    SitePoint Enthusiast
    Join Date
    Apr 2002
    Posts
    54
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I would argue that as it stands at the moment OOP is not all that useful for short or even moderately sized scripts; however I can understand where it would come into play for larger projects. Apart from the standard OOP efficiencies, the extensibility and manageability factor would probably come most into play here - code a class definition into a separate file, then just drop in a require_once("whatever.php"). Easy!

  4. #4
    SitePoint Addict
    Join Date
    Feb 2002
    Location
    Atlanta, GA
    Posts
    342
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    ...and I would agree. Since when the scipt finishes executing PHPs' work is completed til the next script. To truly make PHP an 100% OOP language would, I beleive, be a detriment to its speed and be a big hit in development time for us developers.

    Where OOP can be employed is for code that we reuse, and that is how I've gone about it in my work.


  5. #5
    SitePoint Enthusiast
    Join Date
    May 2002
    Posts
    75
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Why don't you guys quote examples? I've heard from many people the benefits, but I just don't see them. I really need examples to understand........

    Ok, you guys say OOP code is more manageable, right? Any example to illustrate this point?

    Also, OOP code can be extended easily? Any examples?

  6. #6
    SitePoint Addict
    Join Date
    Feb 2002
    Location
    Atlanta, GA
    Posts
    342
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Here's a class definition for adding menu items to a DB:
    PHP Code:
    <?php
    //definition of class properties

    class MenuItem
    {
        var 
    $menuItemName "Unset Property";
        var 
    $menuItemCat "Unset Property";
        var 
    $menuItemLevel 5;
        var 
    $menuItemPrice 0.00;
        var 
    $menuRecipeLink "Unset Property";
        var 
    $rec_code "Unset Property";


    //methods definition
    //FIRST METHOD IS THE DEFAULT CONSTRUCTOR, AND THE ONLY CTOR.  NO MULTIPLE CTORS IN PHP

    //DEFAULT CTOR
    /*
     The default ctor will take the properties set above ^^, 
    -- set them as needed in the script using the accessor methods below
    -- to enforce user/program definition uncomment the assingments
    -- in the default ctor and paste these arguments in the parentheses
    $min, $mic, $mil, $mip, $recID
    */

    function MenuItem($min$mic$mil$mip$recID)        
        {
        
        
    $this->menuItemName $min;
        
    $this->menuItemCat $mic;
        
    $this->menuItemLevel $mil;
        
    $this->menuItemPrice $mip;
        
    $this->menuRecipeLink $recID;
        
        
        }
    //end ctor

    function toString()
        {
        print(
    "<br><br><br>Properties for this Menu Item are:<br>Item Name: $this->menuItemName
        <br>Category: 
    $this->menuItemCat
        <br>Item Level: 
    $this->menuItemLevel
        <br>Item Price: 
    $this->menuItemPrice
        <br>Recipe Links on value: 
    $this->menuRecipeLink<br><br>");
        }
    //ACCESSOR METHODS:: Get and Set

    //GET methods for accessing the properties
    function getMenuItemName(){ return ($this->menuItemName ); }
    function 
    getMenuItemCat() { return ( $this->menuItemCat ); }
    function 
    getMenuItemLevel() { return ( $this->menuItemLevel ); }
    function 
    getmenuItemPrice() { return ( $this->menuItemPrice ); }
    function 
    getMenuRecipeLink() { return ( $this->menuRecipeLink ); } 
    function 
    getRecCode() { return ( $this->rec_code ); }


    //SET methods for Setting property values
    function setMenuItemName$n ){ $this->menuItemName $n; }
    function 
    setMenuItemCat$c ) { $this->menuItemCat $c; }
    function 
    setMenuItemLevel$l ) { $this->menuItemLevel $l; }
    function 
    setMenuItemPrice$p ) { $this->menuItemPrice $p; }
    function 
    setMenuRecipeLink$r ) { $this->menuRecipeLink $r; }
    function 
    setRecCode$rc ) {$this->rec_code $rc; }
    /*
    These methods interact with the Database.
    Remember to include the class MyConnect into any page
    that will call these methods
    */
    function addMenuItem()
        {
        
    $conxion = new MyConnect();
        
    $mnuName $this->getMenuItemName();
        
    $mnuCat $this->getMenuItemCat();
        
    $mnuLevel $this->getMenuItemLevel();
        
    $mnuPrice $this->getMenuItemPrice();
        
    $mnuRecipeLink $this->getMenuRecipeLink();
        
    $sql "INSERT into menu_item(menu_name, menu_cat, menu_level, menu_price, recipe_id) VALUES(\"$mnuName\", \"$mnuCat\", $mnuLevel$mnuPrice, \"$mnuRecipeLink\")";
        
    $rs mysql_query($sql$conxion->conn );
        
    $err mysql_error();
        echo 
    "<br>$err";
        
    $affectedRows mysql_affected_rows();
            if(
    $affectedRows 0)
                {
                
    printf("Menu item &nbsp;<font color=\"blue\">$mnuName</font>&nbsp; was successfully added to the database<br><br><a href=\"AddMenuItem.php\">Click to add another Item</a>");
                }
            else
                {
                
    printf("<br><font color=\"red\">Unable to add $mnuName to the database.</font>  <br>Notify the administrator at <a href=\"mailto:peter@panvox.net\">Admin</a>");
                }
        }
    function 
    deleteMenuItem()
        {
        
    $conxion = new MyConnect();
        
    $mnuName $this->getMenuItemName();
        
    $rec_code $this->getRecCode();
        
    $sql "DELETE from menu_item WHERE item_name = '" $mnuName "'";
        
    $rs mysql_query($sql$conxion->conn );
        
    $err mysql_error();
        echo 
    "<br>$err";
        
    $rowsDeleted mysql_affected_rows();
                if(
    $rowsDeleted == )
                    {
                    
    printf("Query successfully executed, but there was no item <b><font color=\"red\">$mnuName</font></b> in the database.\n");
                    }
                else
                    {
                    
    printf("The menu item&nbsp;<font color=\"blue\"><b>$mnuName</b></font>&nbsp;has been permanantly removed from the database.");
                    
    printf("<BR><font color=\"blue\">" $rowsDeleted " </font>records were deleted.");
                    }
        
    $sql "DELETE from recipe_ingredient_data WHERE rec_code =  '" $rec_code "'";
        
    $rs mysql_query($sql$conxion->conn );
        
    $err mysql_error();
        echo 
    "<br>$err";
        
    $rowsDeleted mysql_affected_rows();
                if(
    $rowsDeleted == )
                    {
                    
    printf("There were no recipe-ingredients listed for <font color=\"blue\">$mnuName </font>in the database. in the database for " $mnuName);
                    
    printf("<BR><BR><input type=\"Button\" Value=\"Close and Exit\" onClick=\"window.close();\">");

                    }
                else
                    {
                    
    printf("<br><font color=\"blue\">" $mnuName " </font> recipe - ingredient(s) have also been deleted.");
                    
    printf("<BR><font color=\"blue\">" $rowsDeleted "</font>  records were deleted.");
                    }
        
    $sql "DELETE from rec_blurb WHERE rec_code =  '" $rec_code "'" ;
        
    $rs mysql_query($sql$conxion->conn );
        
    $err mysql_error();
        echo 
    "<br>$err";
        
    $rowsDeleted mysql_affected_rows();
                if(
    $rowsDeleted == )
                    {
                    
    printf("<BR>There were no preparation instructions in the database to remove for this item:<BR><font color=\"red\">" $rec_code "<BR> " $mnuName "</font>");
                    
    printf("<BR>This simply means no instructions were provided when the recipe was originally written.");
                    
    printf("<BR><i><strong>All data pertaining to this recipe/menu item have been deleted.</strong>");
                    
    printf("<BR><BR><a href=\"DeleteMenuItem.php\">Delete Another Item?</a>");
                    
    printf("<BR><BR><input type=\"Button\" Value=\"Close and Exit\" onClick=\"window.close();\">");

                    }
                else
                    {
                    
    printf("<br><font color=\"blue\">" $rowsDeleted "</font> recipe preparation instructions successfully deleted.<br>");
                    
    printf("<br><b><i>All data have been removed for this item.</i></b>");
                    
    printf("<BR><BR><a href=\"DeleteMenuItem.php\">Delete Another Item?</a>");
                    
    printf("<BR><BR><input type=\"Button\" Value=\"Close and Exit\" onClick=\"window.close();\">");
                    }
        }
    }
    //End Class Definition

    ?>
    Now, to use this you instantiate a new MenuItem Object, passing form values to the constructor:
    PHP Code:
    $mi = new MenuItem($min$mic$mil$mip$recID); 
    To add the item to the DB:
    PHP Code:
    $mi->addMenuItem(); 
    So you see? The class definition is the code intensive part. Can you envision PHP pages which are fully funtional and only 3 lines of code?

    That goes to code reusability.

    Cheerio


  7. #7
    SitePoint Enthusiast
    Join Date
    May 2002
    Posts
    75
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    But what I don't understand is, why must I do it in OOP? I mean, I can achieve the same convenience with procedural. I just have to declare normal functions on a separate file without declaring them in a class. Then I just "require" the file. By doing this, I'm also reusing my code, aren't I?

    Another thing. I've heard from many people that by using OOP, you write shorter code. I don't really agree. You see, you just said that the class definition is intensive, so isn't it still the same?

    Sorry, but pls don't take offence to any of my words. You see, I want to learn. When I want to learn, I will make sure that I understand 100%. I counter your arguments so that I have a higher chance of learning.

  8. #8
    SitePoint Addict
    Join Date
    Feb 2002
    Location
    Atlanta, GA
    Posts
    342
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You can do much if not all with functions and includes/requires etc. It's very modular. And in that case you're only writing the function once then including as needed. One can argue that procedural vs. OOP is a matter of taste.

    When you define a class, you're modelling an Object, not merely a behavior. Functions go to behavior. So a class has properites, as well as behavior ( functions ), thus a class, when well designed is more versatile.

    Either way you look at it, you're still going to have to code a fair amount. It's that with OOP, you're localising it within the class, not the include file.


  9. #9
    SitePoint Enthusiast
    Join Date
    May 2002
    Posts
    75
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    So when should I consider using OOP, and when must I avoid it?

  10. #10
    SitePoint Addict
    Join Date
    Feb 2002
    Location
    Atlanta, GA
    Posts
    342
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    That's a good question, as very often you can code something rather quickly in a procedural manner. i'd say when you are working on an application and you determine that certain charactersitics of the application will be re-used, and have multiple properties, then a class is a good way to go.

    For example, if you're building an application that has members, then each memebr has basic characteristic, or properites:
    Name, gender, email, whatever you like. Here would be a good candidate to make a class definition for members.

    An excellent book on Object Modelling is "Beginning Java Objects" from Wrox Press, written by Jaqui barker. The first 1/3 of the book is devoted to modelling objects in such a way that any language that supports OOP can be adapted, not just JAVA specific.


  11. #11
    SitePoint Enthusiast
    Join Date
    May 2002
    Posts
    75
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Going into your members example, are you trying to say that using OOP is enabling you to write neater code? Like your example, all properties are stored in an object, which is neater, compared to many variables.

  12. #12
    SitePoint Addict
    Join Date
    Feb 2002
    Location
    Atlanta, GA
    Posts
    342
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    That's one way of looking at it. But in essence, think of an object as a variable on steroids...you're making one definition with whatever properties/methods you think you'll need, then each object instantiated from that class has all those properties and methods inherent to it. Further along then, you make objects which inherit from other objects, and in doing so, you have all the methods/properties of the parent object, as well as any new methods & properites defined in the child objects' class definition.


  13. #13
    SitePoint Enthusiast
    Join Date
    May 2002
    Posts
    75
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ok, thanks for all replies.

  14. #14
    SitePoint Evangelist
    Join Date
    Oct 2001
    Posts
    592
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    And here's another one .

    One of the reasons that procedural programmers are hard to convince that object-oriented programming is so great, is that the examples given aren't very spectacular: most of them can be rewritten easily in procedural code. Also, much of the so-called OOP code out there isn't object-oriented at all. The PEAR library for example, is in fact not a real object-oriented library, even though the developers claim it is. (Don't worry, I'll explain my reasons for saying this below .)

    The biggest advantage of OOP is re-use. Re-use comes in three flavors (mostly), but only one is used a lot, while the second and third are almost never used. PEAR, for example, uses only two flavors (and the second flavor minimally).

    So what are these flavors of re-use? Well, here they are:

    1. Object instantiation

    This is the easiest and most popular form of re-use in OOP. phpPete gave a good example with his menusystem, so I won't give one here. You write a blueprint for some type, and you can reuse it over and over again simply by instantiating it (creating a variable and calling methods on it). In procedural programming you can achieve the same result by writing a module: a set of functions operating on the same datastructure. There is no advantage whatsoever in using OOP instead of PP for this type of code, except that OO-addicts (like me) say that using a class instead of a module makes the code easier to read (Example: '$database->query($sql)' instead of 'query($database, $sql)').

    2. Class inheritance

    The second form of re-use in OOP is class inheritance: given some class, you 'extend' it, adding new features or overriding existing ones. Inheritance comes in many forms, and I'll briefly describe three of the forms here, comparing each of them to procedular programming.

    - Abstract base classes

    An interface (or: abstract base class, or even: virtual base class) is a class that defines a number of methods, but doesn't implement them. A subclass of the interface class must implement all methods to be a valid interface implementation. For example:

    PHP Code:
    class Database
    {
        
    // create a database object
        
    function Database($host$dbname) { }
        
    // connect to the database
        
    function connect($username$password) { }
        
    // disconnect from the database
        
    function disconnect() { }
        
    // execute a query and return the result
        
    function query($sql) { }
        
    // are we connected?
        
    function isConnected()

    Now, subclasses of the Database interface must implement all methods to be a valid interface implementation. If we were to write Database classes for MySQL, PostgreSQL, Oracle or whatever, they must all conform to the interface above. Why would we want that? Because it allows us to create code that executes methods on any Database-object. Or maybe you need a plug-in system, allowing developers to add code to the software at a later time.

    The advantage of this kind of inheritance (and re-use) shows when using a strongly typed language (Java/C++). If I were to implement a subclass of class Database, the interpreter (or compiler) makes sure I do not forget to implement some method, that all methods have the right number of arguments, all of the right type, and that every method returns a result of the right type. There is no such thing in procedural programming, and the result is that programmers have to write code according to some long and dull manual. With OOP, you can throw away the manual, and trust your compiler to do the dirty work for you.

    Because PHP doesn't explictly check types, writing interfaces isn't as useful as it is in Java or C++. That isn't to say it has no use at all. You can still write an abstract class, and use that as the guidelines you must follow if you want to implement the interface (instead of the boring manual). Also, you can put a 'die("Method not implemented!");' in every method in the interface, so that when you call that method on an object with an incomplete implementation, the program halts immediately.

    - Class extension

    Say you have some procedural module, but it doesn't quite do what you want it to do. If the module consists of many functions, you might be able to replace a couple so that it does. However, if the old module must be maintained at the same time, this means that you have to write a new module with all the same functions as the old one. In the new module, every function simply calls the equivalent function in the old one, except the ones you want to redefine. The result is that you end up with large blocks of code that do virtually nothing.

    With OOP, you can achieve the same result by simply writing a subclass, and replacing only the methods you want to replace, keeping all other methods intact. The beauty of this is that the subclass is easier to write (less to type, so less chance of bugs) and easier to understand: only the code that adds something new must be written.

    The PEAR library implements this kind of re-use a lot. Take a look at classes DB_mysql, DB_pgsql and so on. Class DB_common is the superclass of both classes, and defines basic methods. The two subclasses override only the necessary methods to get their work done. (But one of the biggest problems of PEAR is that almost every method of class DB_common has to be overridden to get it working. That's a clear sign of bad design!)

    - Template classes

    Most procedural programmers are familiar with callback functions: you write some function that takes as an argument (a pointer to) a function, and calls that function with yet another set of arguments. Those who ever did that in C will hopefully agree with me that it's pretty hard to do, because the code looks frightening (unless you're a C-guru), and it's easy to make mistakes.

    As an example, take a function that implements 'bubble sort' on arrays with value of any type. To be able to sort the values, the function needs to call a user-specified function that compares two values. If the first value is bigger than the second, the function returns true, meaning that the values should be swapped:

    PHP Code:
    function bubbleSort(&$array$compare)
    {
        for (
    $i 0$i count($array) - 1$i++)
        {
            for (
    $j 1$j count($array); $j++)
            {
                if (
    $compare($array[$i], $array[$j]))
                {
                    
    $temp $array[$i];
                    
    $array[$i] = $array[$j];
                    
    $array[$j] = $temp;
                }
            }
        }
    }

    function 
    compareIntegers($i$j)
    {
        return 
    $i $j;
    }

    $array = array(8243197)
    bubbleSort($array"compareIntegers"); 
    By writing a new comparison function, the sort-algorithm can easily be reused. This example is pretty simple, but imagine what happens if some algorithm needs many callback functions! With OOP, we can achieve the same goal in a much cleaner way (with 'hooks'):

    PHP Code:
    class BubbleSort
    {
        function 
    BubbleSort(&$array)
        {
            
    // Same algorithm as before, except that the line:
            //   if ($compare($array[$i], $array[$j]))
            // is replaced by:
            //   if ($this->compare($array[$i], $array[$j]))
        
    }
        
        function 
    compare($i$j)
        {
            return 
    $i $j
        
    }
    }

    $array = array(8243197)
    $bs = new BubbleSort($array); 
    If I have to sort an array of 'Blob'-elements, all I need to do is override class BubbleSort and re-implement the compare method:

    PHP Code:
    class BlobBubbleSort
    {
        function 
    compare($blob1$blob2)
        {
            return 
    $blob1->getValue() > $blob2->getValue();
        }
    }

    $blobArray = array(new Blob("foo"), new Blob("bar"), new Blob("this"), new Blob("that"));
    $bbs = new BlobBubbleSort($blobArray); 
    Because this is such a small example, you might not see at once why this is 'better' than the equivalent procedural code with callback functions. But consider a complex system that needs many callback functions; in that case using classes leads to code that is much more readable and understandable. A classic example is a windowing toolkit. I won't go into the gory details here, so if you want to know more, take a look at one of the many windowing toolkits on the Internet like Swing (Java) or Qt (C++). Typically, those toolkits contain many classes with empty methods, that might or might not be implemented by subclasses. At other places in the toolkit calls are made to those methods, so as soon as you override an empty method and define its behavior, the framework immediately changes its behavior as well. As you can hopefully see by now, hooks provide a very powerful means to extend software easily.

    Sadly, I haven't found any occurrences of hooks in PEAR. Draw your own conclusions from that...

    There are more ways to use inheritance, but let's keep it at the three mentioned. I'll now continue with the third flavor of re-use in object-oriented programming:

    3. Object composition

    This is maybe the most powerful and beautiful application of OOP, but as it's also the most difficult one, almost nobody uses it. The idea behind object composition is that you implement classes that don't do a single job completely by themselves, but require additional classes to make them complete. That makes it easy to mix and match object of various types to get completely different behavior from the same classes. As you probably expected by now, PEAR does nothing of the sort.

    As a simple example, consider again the bubblesort algorithm given earlier. The BubbleSort class looks a bit weird, because:
    - I have to instantiate an object just to run the algorithm. However, the class has no member variables, so why would I need an object?
    - When I override class BubbleSort (as in BlobBubbleSort), there is a potential danger that the algorithm itself can be overridden as well. This is not a clear separation between the main algorithm (bubble sort) and the specialized behavior (comparing two objects)

    To solve this, I implement the algorithm in two classes, instead of one:

    PHP Code:
    class BubbleSort
    {
        
    // This is a static method
        
    function sort(&$array$compareObject)
        {
            
    // Same algorithm as before, but now we do
            // $compareObject->compare($i, $j);
        
    }
    }

    class 
    IntCompare
    {
        function 
    compare($i$j)
        {
            return 
    $i $j;
        }
    }

    $array = array(8243197)
    BubbleSort::sort($array, new IntCompare);

    class 
    BlobCompare
    {
        function 
    compare($blob1$blob2)
        {
            return 
    $blob1->getValue() > $blob2->getValue();
        }
    }

    $blobArray = array(new Blob("foo"), new Blob("bar"), new Blob("this"), new Blob("that"));
    BubbleSort::new BubbleSort($blobArray, new BlobCompare); 
    Again, this example is very simple, but hopefully you'll see I'm now using object composition instead of just inheritance: by supplying the 'sort'-method with a different object, the algorithm automatically behaves differently as well. Also note that this example looks a bit like the procedural-style callback function, which more or less shows that callbacks are very important indeed. Whether to use hooks or object composition depends on the problem at hand, but it can be a difficult decision to make.

    Object composition is something you just can't do without, as it allows you to abstract from the problem your working on, and lets you write code in layers. It's almost a requirement for a proper object-oriented programming library to support object composition. (Did I mention already that PEAR doesn't have this? Ah, never mind...)

    Final remarks

    The best object-oriented programs aren't the ones that use only one or two of the techniques I mentioned here, but the ones that combine all of them. For example, consider I write an interface 'Object', that defines a method 'getValue'. I use this class as a baseclass for all classes in the system to store all sorts of values. By writing one single class 'ObjectCompare' and implementing the simple method 'compare' in that class, I can instantly sort arrays of any kind of object! Thus, by combining inheritance and object composition, I can do a whole lot with very little code. Achieving this same result with procedural code not only requires more code, but also results in code that's difficult to understand.

    To end this long (and boring) post, consider a program I just wrote for one of the web sites I'm working on. The program reads a text file containing a menu system, and prints it in nicely formatted HTML. The file it reads is the following:

    PHP Code:
    category name      link
    PHP      
    SitePoint http://www.sitepoint.com
    PHP      PHP.net   http://www.php.net
    Search   Google    http://www.google.com
    Search   HotBot    http://hotbot.lycos.com 
    What I want to do is the following:
    - Each line in the file must be accessible as an array. The first line of the file contains the key. Thus if the first record would be in the variable $record, the value $record['name'] would be 'SitePoint'.
    - The complete file must be stored in memory, and each record must be accessible as described above.
    - The records in the file must be traversed.
    - Each record must be printed in HTML
    - The records must be ordered on category

    To be more specific, I want to map the above text file to the following HTML:

    PHP Code:
    <h1>PHP</h1>
    <
    p>
      <
    a href="http://www.sitepoint.com">SitePoint</a>
      <
    br>
      <
    a href="http://www.php.net">PHP.net</a>
    </
    p>
    <
    h1>Search</h1>
    <
    p>
      <
    a href="http://www.google.com">Google</a>
      <
    br>
      <
    a href="http://hotbot.lycos.com">HotBot</a>
    </
    p
    To do that, I need to write code. So I start hacking away, and end up with only one (1!) statement:

    PHP Code:
    Loop::run(
        new 
    DataFileIterator(new DataFile('menu.dat', new DataFileReader)),
        new 
    MenuPrinter
    ); 

    Let me try and explain what it does, from the outside to the inside:

    - Class Loop is a simple class that uses object composition. Its sole method 'run' requires an 'iterator' and a 'loop manipulator' as its arguments. The method implements a simple iteration, that is influenced by the manipulator.
    - Class DataFileIterator implements the 'Iterator'-interface for DataFiles. There are also iterators for trees, query results, built-in arrays, strings, you name it. They can all use the Loop class.
    - Class DataFile stores file-based tables in memory. It allows access to each record in the file. However, it doesn't know how to parse lines in files itself, so it has to be passed a DataFileReader class that does that specific job.
    - Class MenuPrinter is a 'loop manipulator' I wrote earlier that generates the required HTML. Although I haven't included the code for that class here, as it would require even more explanation of the classes I use, please believe me when I say that it is an extremely simple class.

    What if the text files have a different format? Well, then I replace the DataFileReader. What if I need to print the menu in a different layout? Well, then I replace the MenuPrinter. What if I stop using text files, and want to use a real database instead? Well, then I simply execute a query on a database and use a QueryIterator to process the results, like this:

    PHP Code:
    // $database is a Database connection object
    Loop::run(
        new 
    QueryIterator(
            
    $database->query('select category, name, link from menu, order by category')
        ),
        new 
    MenuPrinter
    ); 
    As you see, I don't have to re-implement a single thing. All I do is combine existing objects in a different manner, to get the behavior I want. And that, my dear ladies and gentlemen, is the true power of object-oriented programming!

    Vincent

  15. #15
    SitePoint Addict
    Join Date
    Feb 2002
    Location
    Atlanta, GA
    Posts
    342
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    KILLER EXAMPLE!


  16. #16
    SitePoint Guru Majglow's Avatar
    Join Date
    Aug 1999
    Location
    B-Town
    Posts
    645
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    However, PHP has not yet seen the full potential of OOP. Also, there is a lot of overhead with OOP in PHP right now. Hopefully, this will change as if you know what you are doing with OOP, you can write your code a shorter than if you were writting it in a procedural fashion.

    The main advantage of OOP is that when you write a class, you can reuse it over and over again, because you are completly separating your code in between objects.

    -cARL
    Ohai!

  17. #17
    What? Maelstrom's Avatar
    Join Date
    Oct 2001
    Location
    Whistler BC originally from Guelph Ontario
    Posts
    2,175
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I stand on the positive side of oop. I have spent a lot of time studying the concept and can promise you that I increased my production time by at least 3x. I also increased readability of the code and extensibility. That said it isn't really something you can explain to someone because procedural style programming 'CAN' do it. Oop doesn't do it better but differently. The way I think is very organized and very block oriented. Oop suits me perfectly.

    Like I said I believe oop is a line of thought. Both systems work and one may be more efficient than the other but really if you don't get oop you won't be able to use it properly.

    As for the overhead and slow down of oop. I have programmed in both styles and don't see the difference. If there is one it is of no consequence in the newest versions of php. This may have applied in pre ver 4 though.
    Maelstrom Personal - Apparition Visions
    Development - PhP || Mysql || Zend || Devshed
    Unix - FreeBSD || FreeBsdForums || Man Pages
    They made me a sitepoint Mentor - Feel free to PM me or Email me and I will see if I can help.

  18. #18
    Super Ninja Monkey Travis's Avatar
    Join Date
    Dec 2001
    Location
    Sioux City, Iowa
    Posts
    691
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    wow voostind you should write some tutorials or an ebook or something! all your posts on layering and OOP have totally blown me away and made me rethink the way i write PHP scripts.
    Travis Watkins - Hyperactive Coder
    My Blog: Realist Anew
    Projects: Alacarte - Gnome Menu Editor

  19. #19
    Making a better wheel silver trophy DR_LaRRY_PEpPeR's Avatar
    Join Date
    Jul 2001
    Location
    Missouri
    Posts
    3,428
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    i'm trying to decide whether to use OOP for a project that i haven't really started yet (so i can still do it right, from scratch ). now, i'd say the only thing that's holding me back is the speed concern if OOP is slower. that's just because i hate having anything slower than it could be. have any of you benchmarked procedural and OOP versions of the same, real-world code? i just made this as a test on my P3 800:

    PHP Code:
    class test
    {
        function 
    add()
        {
            
    $num 4;
        }
    }

    function 
    add()
    {
        
    $num 4;
    }

    $test = new test;

    $mt explode(' 'microtime());
    $before $mt[0] + $mt[1];

    for (
    $i 0$i 1000$i++)
    {
        
    $test->add();
    }

    $mt explode(' 'microtime());
    $after $mt[0] + $mt[1];

    echo 
    $after $before'<br />';

    /////////////////

    $mt explode(' 'microtime());
    $before $mt[0] + $mt[1];

    for (
    $i 0$i 1000$i++)
    {
        
    add();
    }

    $mt explode(' 'microtime());
    $after $mt[0] + $mt[1];

    echo 
    $after $before
    the first (OO) takes ~6.6ms
    the second takes ~4.9ms

    of course that's an extremely simple function, but i was just trying to get an idea of the OOP overhead. so it looks like less than 2ms for 1000 OO operations. i assume that accessing variables in a class would also be slightly slower than accessing "regular" variables?

    i like the fact that with OOP i can, i think, eliminate having to use `global $var;' to access variables between functions as i would have to with procedural.

    also, i was wondering since i've never used OO before, what would be the best way to make like a $db object available in another class' methods without having to pass it? would you do something like this:

    PHP Code:
    $db = new DB;
    $foo = new AnotherClass;
    $foo->db = &$db
    i have no idea if that's even right! i think you were explaining this in another thread, voostind. or would you have something in AnotherClass that creates the $db object, and then access it as, i guess, $foo->db->query()? or is that not a good idea? don't laugh! i don't know anything about this and want to know the best way to do it. others may be wondering the same thing.
    - Matt ** Ignore old signature for now... **
    Dr.BB - Highly optimized to be 2-3x faster than the "Big 3."
    "Do not enclose numeric values in quotes -- that is very non-standard and will only work on MySQL." - MattR

  20. #20
    What? Maelstrom's Avatar
    Join Date
    Oct 2001
    Location
    Whistler BC originally from Guelph Ontario
    Posts
    2,175
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think the speed difference starts to be even less of an issue with larger projects. I think if you compared say a cms built on good procedural compared to oop the difference would be insignificant

    $foo->db->query()

    That is similar to the way i design my classes. But according to Voostind current post this could be further optimized by something like (and I probably have this wrong as I am trying to grasp that level of oop design.

    $test=new Test(new Db("table_name"))

    Then you could have something like

    class Test
    {
    function Test($dbcnx)
    {
    $this->dbcnx=$dbcnx;
    }
    }

    Then later in that script you can further access the db class by referencing the $this->dbcnx->query(); for example.

    I think I have that right or at least close. Advanced oop tachniques are still a little over my head. And fully grasping the idea of layering in applications is also a little tough. But heh thats what hitting the books is suppost to do right
    Maelstrom Personal - Apparition Visions
    Development - PhP || Mysql || Zend || Devshed
    Unix - FreeBSD || FreeBsdForums || Man Pages
    They made me a sitepoint Mentor - Feel free to PM me or Email me and I will see if I can help.

  21. #21
    SitePoint Evangelist
    Join Date
    Oct 2001
    Posts
    592
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    it isn't really something you can explain to someone because procedural style programming 'CAN' do it. Oop doesn't do it better but differently
    I find that to be the case, indeed. Object composition as I described in my previous post is certainly possible with procedular programming, but it requires a lot of thought, and is almost counterintuitive. But it CAN be done. I also agree that OOP is a way of thinking. Myself, I find it extremely hard to write software in a procedural style, because I think in a somewhat object-oriented manner (and not just when it concerns programming).

    Okay, let's talk about databases .

    The Database interface I gave in my previous post is actually more or less the interface (give or take a few methods) I use every day. As you can see, there is no way to process some kind of query result. However when I do this:

    PHP Code:
    $database = new MyDatabase('localhost''test'); // MySQL implementation
    $database->connect('username''password');
    $result $database->query('SELECT * FROM table'); 
    That $result-thingie is not a simple variable, but it's an instance of class QueryResult, which looks like this:

    PHP Code:
    class QueryResult
    {
        
    // Create a new object; this is done by class Database
        
    function QueryResult(&$database$resultId) { }
        
    // Clear the query result
        
    function clear() { }
        
    // Was the query executed successfully?
        
    function isSuccess() { }
        
    // Get the error message in case of an error
        
    function getErrorMessage() { }
        
    // Get the total number of rows in the result (for a SELECT)
        
    function getRowCount() { }
        
    // Get a specific row in the result
        
    function getRow($index) { }

    Given an object of class QueryResult, I can traverse all rows in a SELECT-query, and find out if the query executed okay. If I wanted to, I could add methods specific for INSERT-, DELETE- and UPDATE-queries ("How many rows were affected?", "What is the last used insertion ID?"), but I haven't had the need for that yet.

    Now, classes Database and QueryResult are both interfaces, so they don't do anything by themselves. If I want to use some specific database, I have to implement both classes, as in MyDatabase and MyQueryResult for MySQL, and PgDatabase and PgQueryResult for PostgreSQL.

    The philosophy behind both interfaces is that every class should do what it does best. Class Database knows about connecting to databases and executing queries on it, so why bloat it with code to process the results from those queries? That's a whole different ballgame, so that's why I wrote the QueryResult interface. If you compare this to PEAR, you'll see the following:
    - Class DB_common (and thus classes DB_mysql, DB_pgsql, and so on) have many methods. It can be used to connect to databases, execute queries, process the results in these queries, and handle transactions (and that's not all!).
    - There is a class DB_result, but it's not meant to be subclassed. When using this class to process query results, it internally calls the query handling methods in the database class.

    Now what if I want to do something like this (and I do regularly):

    PHP Code:
    // Select all books
    $books $database->execute('select * from book order by title');
    // Select all authors
    $authors $database->execute('select * from author order by lastname');
    // Handle both the $books and $authors QueryResults 
    Executing queries one after another and processing their results at a later time is easy and straightforward. More importantly, it requires no extra coding in the classes to make sure it works. Also, once I've got the results from some query in a variable ($authors or $books), I don't need the Database object anymore! This is unlike PEAR, where class DB_* must be used to access the various rows in the result. Additional administration is required in the DB_*-classes to make sure rows from the right query are selected, and in my code I have to pass my database object around a lot, just to be able to process query results.

    Given the query result $books above, I can print booktitles like this:
    PHP Code:
    for ($i 0$i $books->getRowCount(); $i++)
    {
        
    $row $books->getRow($i);
        echo 
    $row['title'] . "<br>\n";

    This loop is simple, and there is no reference to the database object whatsoever, which makes sense: what has a database connection to do with a result-set from a query?

    In my previous post, I told about class Loop, which implements loops on iterators. Iterators are higher-level, generic objects. All iterators have the same interface, so I can use the same loop for arrays, strings, trees and query results. The iterator for QueryResult-objects looks like this:

    PHP Code:
    class QueryIterator
    {
        var 
    $queryResult;
        var 
    $current;
        var 
    $size;
        
        function 
    QueryIterator(&$queryResult)
        {
            
    $this->queryResult = &$queryResult;
            
    $this->size $queryResult->getRowCount();
            
    $this->reset();
        }
        
        function 
    reset()
        {
            
    $this->current 0;
        }
        
        function 
    next()
        {
            
    $this->current++;
        }

        function 
    isValid()
        {
            return 
    $this->current $this->size;
        }
        
        function &
    getCurrent()
        {
            return 
    $this->queryResult->getRow($this->current);
        }

    Again, you see that class QueryIterator doesn't need the Database class to do its work. Not only does this make more sense, it also makes software more layered: I don't need to pass my Database object to many objects or functions. Only the highest level code needs acccess to the database. The code at lower levels doesn't, so they don't need the Database object, so I don't need to pass it.

    To print book-titles, I can now write this:

    PHP Code:
    for ($it = new QueryIterator($books); $it->isValid(); $it->next())
    {
        
    $row = &$it->getCurrent();
        echo 
    $row['title'] . "<br>\n";

    In this case, this adds little or no benefit to traversing the query results directly from the QueryResult object. This changes if you take into account that I can now layer my code even more: by creating an iterator and passing this on to code at a lower level, that level needs to know nothing about the kind of data is traversing. More importantly, as all iterators have the same interface, I can reuse that code over and over again. A perfect example is class Loop:

    PHP Code:
    class Loop
    {
        
    // static member!
        
    function run(&$iterator, &$manipulator)
        {
            
    $index 0;
            
    $iterator->reset();
            if (
    $iterator->isValid()) 
            {
                
    $manipulator->prepare();
            }
            for ( ; 
    $iterator->isValid(); $iterator->next()) 
            {
                
    $current = &$iterator->getCurrent();
                if (
    $index
                {
                    
    $manipulator->between($index);
                }
                
    $manipulator->current($current$index++);
            }
            if (
    $index
            {
                
    $manipulator->finish($index);
            }
            return 
    $index;
        }

    The one and only method 'run' is extremely simple. It gets passed an iterator and implements the iteration loop, so that I don't have to write it ever again. At certain steps in the iteration, it calls methods on a manipulator, that must be passed as the second argument. The manipulator must implement these four methods:
    1. prepare(): is called right before the first item in the iteration is processed
    2. between(): is passed in between every two items, thus not before the first or after the last
    3. current(): is called for every item
    4. finish(): is called right after the last item in the iteration is processed

    If I were to print book-titles, I now have to write a manipulator. To make life easy, I can write this manipulator as a subclass of class LoopManipulator, that implements all four required methods as empty methods. For example:

    PHP Code:
    class BookTitlePrinter extends LoopManipulator
    {
        function 
    current(&$row$index)
        {
            echo 
    $row['title'], "<br>\n";
        }

    Now why is this better than the simple loop I had before? I'll get into that later, after I've shown the complete code for printing booktitles:

    PHP Code:
    // Layer 1: create a database connection
    $database = new MyDatabase('localhost''test');
    $database->connect('username''password');
    // Layer 2: select the books from the database
    $result $database->query('select * from book order by title');
    // Layer 3: set up the iterator and the manipulator
    $it     = new QueryIterator($result);
    $manip  = new BookTitlePrinter;
    // Layer 4: print the book titles
    Loop::run($it$manip); 
    All layers are shown in one block of code, but in large systems they typically aren't. Note that all code in layer 'n' only requires access to code in layer 'n - 1'.

    If you examine the code above more closely, you'll see that the variables $result, $it and $manip are all temporary: once the loop is completed, they aren't needed any more. The question then is: why create them at all? Thanks to the design of the various classes I can write the code above like this:

    PHP Code:
    $database = new MyDatabase('localhost''test');
    $database->connect('username''password');
    Loop::run(
        new 
    QueryIterator($database->query('select * from book order by title')),
        new 
    BookTitlePrinter
    ); 
    No more temporary variables, and simpler code as a result. On the other hand, this only makes sense if all 4 layers are placed at the same part of the code. Anyway, it does show how the same classes can be used to write layered code as well as compact code.

    To answer the question "Why is using manipulators a good idea?":
    - There is now a clear separation between the algorithm (the loop) and the behavior of the algorithm (printing booktitles). In other words: object composition.
    - The algorithm (the loop) is implemented just once in a generic way, instead of many times, specialized for specific problems.
    - As the manipulator is a class, I can reuse it by writing subclassing or wrapping.
    - An intelligently written manipulator can be reused for other loops.
    - Layering is supported 'out of the box'

    To end this post, I'd like to remind you that the example I've used here is pretty simple, and therefore may lead you to think that the various classes are bit 'over the top'. In larger systems they certainly aren't, I can vouch for that! Also, by using a set of compact and efficient classes instead of large, bloated ones (PEAR), I find no impact in execution speed whatsoever. But that's just my experience of course

    Vincent

  22. #22
    SitePoint Zealot infoxicated's Avatar
    Join Date
    Jun 2001
    Location
    UK
    Posts
    140
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Thumbs up Wooooooooosh!

    Vincent, I'm learning to love the wooshing sound that your posts make as they go waaaaaaay over my head! LOL!

    Seriously, though - I'm a PP man myself.. never quite grasped the OO approach in my time at Uni, and it cost me dearly. (my degree, as it happens!)

    I reckon if any of my lecturers had half the talent for breaking stuff down as you do, that wooshing sound would be the sound of my brain grasping the concept.

    Nice to see Larry foxed for once - you guys keep at it; I'll follow along.

    *scrolls back up the page and gets reading*

  23. #23
    gingham dress, army boots... silver trophy redux's Avatar
    Join Date
    Apr 2002
    Location
    Salford / Manchester / UK
    Posts
    4,838
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    voost, at last a description of OOP in PHP that makes me strongly consider at least dabbling with it. i can see the logic behind it, and yes, if you look at it that way, it does make a lot of sense indeed. i was under the (mistaken, admittedly) impression that OOP in PHP == PEAR, and what kept me from playing with it was indeed the bloated "one-size-fits-all-or-else" approach of all those classes. if i now start going OO and flood this forum with silly questions, remember: it's your fault for starting me down that path
    re·dux (adj.): brought back; returned. used postpositively
    [latin : re-, re- + dux, leader; see duke.]
    WaSP Accessibility Task Force Member
    splintered.co.uk | photographia.co.uk | redux.deviantart.com

  24. #24
    What? Maelstrom's Avatar
    Join Date
    Oct 2001
    Location
    Whistler BC originally from Guelph Ontario
    Posts
    2,175
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanx again voo. I think I have more posts by your printed out than anyone. You have also (indirectly) convinced me to go and grab some cbt's on advanced oop. I thought I had a good understanding of it but realize I have a 'good' understanding, I want to understand more

    Again, muchly appreciated on the fantastic artical.
    Maelstrom Personal - Apparition Visions
    Development - PhP || Mysql || Zend || Devshed
    Unix - FreeBSD || FreeBsdForums || Man Pages
    They made me a sitepoint Mentor - Feel free to PM me or Email me and I will see if I can help.

  25. #25
    SitePoint Addict
    Join Date
    Feb 2002
    Location
    Atlanta, GA
    Posts
    342
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I would also refer people to this article/tutorial on the ZEND WebSite.



Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •