Jump to content
Larry Ullman's Book Forums

Why Use Objects To Pass Parameters To Other Objects?


Recommended Posts

Guest Deleted

When I'm reading about OO PHP I sometimes see things like this:

$list->sort(new MultiAlphaSort('name'));

Why do that? Why not just set up the sort method so it accepts a string?

 

Here's somewhere I saw this in use:

require('iSort.php');


/* The StudentsList class.
 * The class contains one attribute: $_students.
 * The class contains three methods:
 * - __construct()
 * - sort()
 * - display()
 */
class StudentsList {


// Stores the list of students:
    private $_students = array();


// Constructors stores the list internally:
    function __construct($list) {
        $this->_students = $list;
    }


// Perform a sort using an iSort implementation:
    function sort(iSort $type) {
        $this->_students = $type->sort($this->_students);
    }


// Display the students as an HTML list:
function display() {
echo '<ol>';
foreach ($this->_students as $student) {
echo "<li>{$student['name']} {$student['grade']}</li>";
}
echo '</ol>';
}


} // End of StudentsList class.


// Create the array...
// Array structure:
// studentID => array('name' => 'Name', 'grade' => XX.X)
$students = array(
256 => array('name' => 'Jon', 'grade' => 98.5),
2 => array('name' => 'Vance', 'grade' => 85.1),
9 => array('name' => 'Stephen', 'grade' => 94.0),
364 => array('name' => 'Steve', 'grade' => 85.1),
68 => array('name' => 'Rob', 'grade' => 74.6)
);


// Create the main object:
$list = new StudentsList($students);


// Show the original array:
echo '<h2>Original Array</h2>';
$list->display();


// Sort by name:
$list->sort(new MultiAlphaSort('name'));
echo '<h2>Sorted by Name</h2>';
$list->display();


// Sort by grade:
$list->sort(new MultiNumberSort('grade', 'descending'));
echo '<h2>Sorted by Grade</h2>';
$list->display();


// Delete the object:
unset($list);

iSort.php:

interface iSort {
    function sort(array $list);
}


// The MultiAlphaSort sorts a multidimensional array alphabetically.
class MultiAlphaSort implements iSort {
    
    // How to sort:
    private $_order;
    
    // Sort index:
    private $_index;
    
    // Constructor sets the sort index and order:
    function __construct($index, $order = 'ascending') {
        $this->_index = $index;
        $this->_order = $order;
    }
    
    // Function does the actual sorting:
    function sort(array $list) {


        // Change the algorithm to match the sort preference:
        if ($this->_order == 'ascending') {
            uasort($list, array($this, 'ascSort'));
        } else {
            uasort($list, array($this, 'descSort'));
        }


        // Return the sorted list:
        return $list;
    
    }// End of sort() method.


    // Functions that compares two values:
    function ascSort($x, $y) {
        return strcasecmp($x[$this->_index], $y[$this->_index]);                                        
    }
    function descSort($x, $y) {
        return strcasecmp($y[$this->_index], $x[$this->_index]);                
    }


} // End of MultiAlphaSort class.


// The MultiNumberSort sorts a multidimensional array numerically.
class MultiNumberSort implements iSort {
    
    // How to sort:
    private $_order;
    
    // Sort index:
    private $_index;
    
    // Constructor sets the sort index and order:
    function __construct($index, $order = 'ascending') {
        $this->_index = $index;
        $this->_order = $order;
    }
    
    // Function does the actual sorting:
    function sort(array $list) {


        // Change the algorithm to match the sort preference:
        if ($this->_order == 'ascending') {
            uasort($list, array($this, 'ascSort'));
        } else {
            uasort($list, array($this, 'descSort'));
        }


        // Return the sorted list:
        return $list;


    }// End of sort() method.


    // Functions that compares two values:
    function ascSort($x, $y) {
        return ($x[$this->_index] > $y[$this->_index]);
    }
    function descSort($x, $y) {
        return ($x[$this->_index] < $y[$this->_index]);
    }


} // End of MultiNumberSort class.

 

 

Link to comment
Share on other sites

Why do that? Why not just set up the sort method so it accepts a string?

 

Good question. I tend to agree with you that it makes more sense to call a sort function in a procedural-style way than an OOP-style way.

 

With that said, I think there are two things to consider:

1) A lot of OOP people love OOP (and they want to use it for everything, even when it may not make the most sense).

2) I don't think it's too extreme to assign a sort method to the class in this case because you gotta figure that the way of sorting objects of a user-defined class is somewhat special. As such, it makes sense to associate all functions/methods required for a class with that class so that you have everything you need in one place.

Link to comment
Share on other sites

The parameter hinting requires an interface, not an object. That's a huge difference. What that basically means, is that you can add new sorting methods without affecting the actually sorter logic. That makes it a huge benefit over using simple Strings. Using String seriously limits what you can do with sorting here. I would disagree strongly with Jon here. It makes perfect sense to write code like this.

 

Add this method to a DB wrapper, and it makes hell of a lot of more sense. Write five comparators, and you've modularized all your logic for sorting query results. The problem here is that you cannot take an example literally.

Link to comment
Share on other sites

Good points, Antonio, but I still think it can go either way.

I have no problem with using OOP for this sort of thing (no pun intended), but to me, it just makes more logical sense to feed a list of info to a separate sorter function (not a method) and have it return a sorted list.

 

Again though, it really can go either way. Also, in the end, whether it's objects our whatever, when you sort, you're ultimately going to sort on strings and/or numbers. It really just depends on how your class is defined, and which strings/numbers you want to sort on.

Link to comment
Share on other sites

Guest Deleted

Thanks for the answers.

 

After reading them, and thinking some more, I guess it makes a little more sense now. Now that I think about it, I think the example is using the strategy pattern. That pattern is all about being able to change a method's implementation on the fly. So I guess the example had to somehow illustrate that being done. I guess Hartley is right that the situation presented in the example doesn't require OOP but I think Antonio is also right when he says that since it's an example, it can only be so practical. Perhaps if it was too realistic then it would be too complicated to grasp. Or at least.......this is how I understood what you guys said. I dunno. My brain feels about to explode. I've been up all night.

 

Anyhoo, I was sure that there are good reasons to feed an object into another object, I just couldn't tell from the example. I was hoping somebody would come along with an example. 

Oh, and Antonio. What did you mean by this? "Add this method to a DB wrapper, and it makes hell of a lot of more sense."

Link to comment
Share on other sites

If all objects created by with a DB wrapper implements a method such a compareTo, (or more like implements an interface that requires that method) object are extremely easy to sort. As the sorter only implements the looping logic, and the objects has comparison logic, all objects are sortable right out of the gate. That'll leave some seriously simple application code, like this:

// Fetch all object from SomeClass
$objects = SomeClass::fetchAll();

// Sort all object according to standard sorting
$sortedByFirstName = Container::sort($objects);

// Use comparators to sort
$address = Container::sort($objects, new AdressComparator());
$telephone = Container::sort($objects, new PhoneComparator());

This is the magic of good OOP. As you have interface based implementations, you can quickly switch out and modularize logic. If you go for a String solution, you'll have to create a sloppy mess of very application specific logic into your classes, and you don't really want that. It's harder to maintain, easier to break, and harder to build on down the line. Separation of concerns is a very important principle. 

Link to comment
Share on other sites

Guest Deleted

Soooo the jist of what you're saying is: use the strategy pattern because it makes code more flexible. That right?

Link to comment
Share on other sites

What Antonio is saying is that instead of writing a different function/method for each type of object you want to sort, you simply write a method for each type of object that shows how to compare objects of that type.

 

Then, you can abstract the sorting function/method so that you only have one sorting function/method for all objects.

Essentially, the sorting function/method runs a loops and calls a, for example, compareTo method on each object, and depending on how objects' compareTo method is defined, objects are sorted.

Link to comment
Share on other sites

 Share

×
×
  • Create New...