Page 1 of 1

Function reuse / duplication

Posted: Fri Mar 09, 2007 4:59 am
by Kadanis
I'm pretty new to OO especially in PHP. I touched on it a couple of years ago on a course, but never really tried programming it beyond what the course required, anyway now I've started trying to follow OO concepts with my PHP coding, to tidy it up and to make it more flexible, and I came up with this problem/question

Say you have a function that carries out a specific task, and this task is common, something like recursively removing a directory.

Next, lets say you have 2 unrelated classes with no common parent, but both need to be able to remove a directory.

Would you just copy and paste the same function into both classes, or try to build some kind of Utility class that contains stuff like this and just use it like a function library?

Is it a question of personal style or is there design principles behind the one or the other of these. (or a different option)

Posted: Fri Mar 09, 2007 5:14 am
by Jenk
Welcome to the world of OO :)

I'd possibly create a directory object, which will contain not only the ability to remove the directory, but other functionality such as listing the contents, and so forth. I would then pass this object as a parameter to the object that require any directory functionality.

Code: Select all

$object = new SomeObject();
$object->addDirectory(new Directory('/path/to/some/dir'));

// $object is now able to use the Directory object to manipulate '/path/to/some/dir' directory.

Posted: Fri Mar 09, 2007 5:30 am
by Kadanis
Ah right. I think I see where to go with this now.

Thanks for the suggestion.

Posted: Fri Mar 09, 2007 5:31 am
by Maugrim_The_Reaper
You ended up with a valid solution ;), so all I'll do is elaborate a little.

A key thing in OOP is to avoid code duplication. If you were to copy a function across a dozen classes, and you discovered later there was a bug in that code then you would be faced with making 12 edits instead of just one. Of course by that stage you may not know how many copies exist, and where they are in your applications (or even how many applications!).

Avoiding duplication isn't too difficult. If you encounter a task which is Class A, and later you realise you need to use it in Class B and Class C, then a common "refactoring" is to extract a new class which contains the common task. This is where your tentative solution hit the mark almost dead on. You can go a step further by remembering a class should be specific - so while a Utility class would work, as it gathers responsibilities and methods (functions) you'll realise it has lost a specific purpose. A good thing is to take a task, give it a name, and slot into a class which seems related - perhaps DirectoryManager for example where you can bundle general directory management code.

After that it's simply a matter of getting your new Utility class into the other two classes where it can be used. This is another topic, but a common solution is to pass it in as a constructor parameter, or through a setter method; example in PHP5:

Code: Select all

class App_DirectoryManager
{
    public function removeRecursive($directory)
    {
        // Remove a directory tree from the file system recursively.
    }
}

class App_Cache
{
    protected $cacheDirectory = '';
    protected $dirManager = null;

    public function __construct($cacheDirectory, App_DirectoryManager $manager)
    {
        $this->dirManager = $manager;
        $this->cacheDirectory = $cacheDirectory;
    }
}

// or

class App_Cache
{

    protected $cacheDirectory = '';
    protected $dirManager = null;

    public function __construct($cacheDirectory)
    {
        $this->cacheDirectory = $cacheDirectory;
    }

    public function setDirectoryManager(App_DirectoryManager $manager)
    {
        
    }
}
There are other options where all this parameter passing is getting too numerous like the Registry Design Pattern, Singleton, etc. But the above is the most basic type of usage.

You seem to be moving in the right direction for OOP - so good luck on that :).

Posted: Fri Mar 09, 2007 8:05 am
by Begby
FYI there are really good recursive directory classes, interfaces, and iterators in php 5.1+ as part of SPL. The discussion of OO is good, but you might not need to recreate this stuff.

Posted: Fri Mar 09, 2007 8:09 am
by Kadanis
Thanks for the replies. All very helpful

@Begby, I think the servers run 5.0.4 at the moment, and I don't know when there is going to be an upgrade. I will look into the classes you've mentioned though.

Re: Function reuse / duplication

Posted: Fri Mar 09, 2007 8:15 pm
by alvinphp
Kadanis wrote:Would you just copy and paste the same function into both classes, or try to build some kind of Utility class that contains stuff like this and just use it like a function library?
A utility class (or some call a master class) is not OOP. If you have to put this method into two classes then you probaby are not building your classes correctly.

Posted: Fri Mar 09, 2007 9:22 pm
by Christopher
You really have two main options -- extension and composition:

Code: Select all

class App_DirectoryManager
{
    public function removeRecursive($directory)
    {
        // Remove a directory tree from the file system recursively.
    }
}


// Extension
class MyClass extends App_DirectoryManager
{
    // inherits App_DirectoryManager methods

    function myfunction($directory)
    {
        $this->removeRecursive($directory)

    }
}

// Composition
class MyClass
{
    var $dir_mgr;

    // composite App_DirectoryManager object
    function __construct($dir_mgr)
    {
        $this->dir_mgr = $dir_mgr;

    }

    function myfunction($directory)
    {
        $this->dir_mgr->removeRecursive($directory)

    }
}
The both have there advantages ... the main benefit to Composition is that you can change which removeRecursive() method is used at run-time.

Posted: Sat Mar 10, 2007 2:00 am
by jmut
Maugrim_The_Reaper wrote:.....You can go a step further by remembering a class should be specific - so while a Utility class would work, as it gathers responsibilities and methods (functions) you'll realise it has lost a specific purpose. A good thing is to take a task, give it a name, and slot into a class which seems related - perhaps DirectoryManager for example where you can bundle general directory management code.
....
In this context...
A keyword for this would be high cohesion http://en.wikipedia.org/wiki/Cohesion_( ... r_science)
Another key word is low coupling
This is what it is all about :)
Having simple objects doing specific/related tasks only (high cohesion) and at the same time have these as independant as possible (low coupling) to promot reusability etc.
That is why Utility class is not a great idea...doing all kind of things.
Have fun.

Posted: Sun Mar 11, 2007 9:13 am
by Ollie Saunders
Don't
Repeat
Yourself
- that means copy and paste is never the answer.

Posted: Mon Mar 12, 2007 9:41 am
by Kadanis
I've read through and followed up on your responses. Thanks for all the input. I'm sure its going to take some time for me to get my head round it all.

Basically, the built in PHP Directory class doesn't seem to work (probably the 5.0.4 thing mentioned earlier), so I've settled on grouping only directory specific functions into a DirectoryManager class. Like Maugrim suggested.

I've then replaced all occurences of the function (the recursive remove directory) with an instance of the class DirectoryManager (using composition) and called its class function remove(). I've also bundled in functions for listing, counting and retrieving directory contents and replaced those as well, but keeping it specific to directory functions.

I hope this is the right thing to do :) it seems to have removed quite a lot of duplicated code and, over time, I hope to replace more in the same way by creating more specific yet reusable classes.

Thanks again for all the help, I think its a long old road though ;)

Posted: Mon Mar 12, 2007 11:48 am
by Maugrim_The_Reaper
I think the thread threw a lot of good principles and terms at you - that what you get for posting to the Theory and Design thread ;). You'll find that as you continue to practice OOP these sorts of decisions will quickly become habits. The cool thing is that OOP as a skill is language independent, so you could put your future experience to good use in most other languages outside PHP.

Your own observation that duplicated code has been removed shows your change has has a desireable effect - stick with it and don't be afraid to experiment ;). And of course always feel free to throw any questions out way :).