SitePoint Sponsor

User Tag List

Results 1 to 12 of 12
  1. #1
    SitePoint Member
    Join Date
    Apr 2005
    Posts
    11
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Objects using objects - I don't get it...

    I don't for the life of me see how multiple classes can work together to produce a result without each of them being aware of the others.

    And as they have to be aware of eachother - the problem is: how?

    How does one object use another object?!

    My nice address book application can create each object passing it the database object as a reference. Great, that's step one.

    Now to use my email class I need to get the information from the database. Why? Well, having seperated everything into individual classes the simple $email->sendTo('westbroek') all of a sudden isn't simple anymore...

    In order to $email->sendTo('westbroek') my email class needs to look up the name and find the email address.

    How does $email do this? Does it create another object? How does it know about this object? From a config file?

    The scope resolution operator looks so tempting but now doing names::getEmailAddress('westbroek') doesn't work. Why? Well, the database object passed by reference to the newly created names object doesn't exist because I'm not working with the object but with the class now.

    Stuck again.

    In the end the only solution I see is for all those classes which need to use one or more other classes to add code. Lots of it. "Ah, we need an address object .. let's see if it exists ... no, not in my namespace .... let's create it... if something goes wrong then.... blah blah blah"

    The idea can't be that I put all objects which need to be used by other objects in the global namespace, is it?

    Writing classes, components, seems so clear. Seems so to be the right solution. But somehow I cannot make a Car() from Body(), Wheels() and Motor().... I just end up with a bunch of tiny mini applications....

  2. #2
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by westbroek
    And as they have to be aware of eachother - the problem is: how?
    Class names are intrinsicly global. This means that any class can create an instance of another at will. However this is not usually a good idea unless such classes are very closely related. Here are all the options that I can think of...

    1) Pass it in as needed in the method. This is the approach with the least coupling and so, if you can, you want to do this as a first choice...
    PHP Code:
    $template = new Template();
    $template->substitute('title''You & me are here');
    $template->paint(new XhtmlWriter()); 
    In this case I would imagine that the "&" would be automatically escaped before it was printed to the screen.

    2) Pass it in the constructor so that it is available when needed...
    PHP Code:
    $template = new Template(new XhtmlWriter());
    $template->substitute('title''You & me are here');
    $template->paint(); 
    This is handy if you are going to call more than just the paint() method and the writer will be needed in each case. Duplication is bad and this avoids it. This is called the Strategy pattern and the tool of choice in OO.

    3) Create the object internally...
    PHP Code:
    $template = new Template();
    $template->substitute('title''You & me are here');
    $template->paint();

    class 
    Template {
        ...
        function 
    paint() {
            
    $writer = new XHtmlWriter();
            ...
        }

    The least flexible approach as this seals in the choice of writer forever.

    4) Use an internal factory method...
    PHP Code:
    $template = new Template();
    $template->substitute('title''You & me are here');
    $template->paint();

    class 
    Template {
        ...
        function 
    paint() {
            
    $writer $this->createWriter();
            ...
        }

        protected function 
    createWriter() {
            return new 
    XhtmlWriter();
        }

    This is marginally better, because now other developers can subclass the Template and override createWriter() to return a different kind of writer. However you can only inherit once so this allows only one option per class. It also won't help if Template itself is inherited from something more complicated. Things just get too tangled. The pattern itself is called TemplateMethod or sometimes "programming by differences".

    5) Pass in a factory. This is very highly factored solution and will solve even the most complex of cases...
    PHP Code:
    $template = new Template(new WebInfrastructure());
    $template->substitute('title''You & me are here');
    $template->paint();

    class 
    Template {
        ...
        function 
    paint() {
            
    $writer $this->_infrastructure->createWriter();
            ...
        }

    You rarely need this much firepower, but once you are used to the idiom itis quite clean. Use this especially when you want to completely swap out the infrastructure of a system. That is you are not just changing the writer but other parts as well as a group. For example a command line infrastructure would have a TextWriter and might have getArguments() return the $argv values rather than $_REQUEST. The pattern is called the AbstractFactory when it creates families.

    6) A static factory...
    PHP Code:
    class Xhtml {
        function 
    createWriter() {
            return new 
    XhtmlWriter();
        }
    }

    $template = new Template(new WebInfrastructure());
    $template->substitute('title''You & me are here');
    $template->paint();

    class 
    Template {
        ...
        function 
    paint() {
            
    $writer Xhtml::createWriter();
            ...
        }

    This is a classic case of people copying Java idioms without thinking. There are language restrictions in Java regarding the number of public classes and tricks involving the use of inner classes to avoid exposing too many interfaces. These idioms are unnecessary and unavailable in PHP. This trick buys nothing that the "new" operator doesn't give you already, other than the ability to change the delivered class by editing the code in just one place. Completely useless and yet you see it all over the place.

    7) Make the object global. This is so bad I find it painful to even type in the example...
    PHP Code:
    global $writer;
    $writer = new XhtmlWriter();

    $template = new Template();
    $template->substitute('title''You & me are here');
    $template->paint();

    class 
    Template {
        ...
        function 
    paint() {
            global 
    $writer;
            ...
        }

    The chances of accidents happening heads rapidly to 100% on any project that is remotely complicated. Besides some other method overwriting the global, you have to make sure it is properly set up once only before you use it. Programers will come up with all sorts of naming conventions and practices to prevent accidents not realising that they are creating more work than if they just added some design. When the disaster occours anyway, debugging is a nightmare.

    8) The Singleton pattern is a very clever way to implement a global. Basically this is a static method that returns only one instance. You might want to do this when you only want one object created ever, either because of creation cost or because it needs a memory. A database connection is a good example because it usually has to keep track of transactions. Here is the writer again. Imagine it has to send some headers once only on first use...
    PHP Code:
    class XhtmlWriter {
        ...
        function 
    getInstance() {
            static 
    $writer;
            if (! isset(
    $writer)) {
                
    $writer = new XhtmlWriter();
            }
            return 
    $writer;
        }
    }

    $template = new Template();
    $template->substitute('title''You & me are here');
    $template->paint();

    class 
    Template {
        ...
        function 
    paint() {
            
    $writer XhtmlWriter::getInstance();
            ...
        }

    By hiding the variable inside a "closure", here named getInstance(), you ensure it can only be accessed via that method. This ensures that it is read only and that it is correctly set up. A clever little pattern, but the static method makes this pattern very inflexible. It also makes the application difficult to test because, that single instance is impossible to reset between each test. Code that you cannot test is probably broken and the Singleton these days has a bad reputation.

    9) The registry, which usually forms the basis of "dependency injection", is a major upgrade to the Singleton. Essentially the registry object is the singleton to which you add the global services...
    PHP Code:
    $registry Registry::getInstance();
    $registry->setWriter(new XhtmlWriter());

    $template = new Template();
    $template->substitute('title''You & me are here');
    $template->paint();

    class 
    Template {
        ...
        function 
    paint() {
            
    $registry Registry::getInstance();
            
    $writer $registry->getWriter();
            ...
        }

    You have to ensure that the registry entry is set up before it is used of course. The advantages of teh registry approach is that the registry can be set up differently for each and every test in your test scripts, and that additional flexibility can be added as needed such as a lookup order for a resource. This is a complex solution to say the least.

    I have probably missed a few, but that should give you enough options.

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  3. #3
    SitePoint Member
    Join Date
    Apr 2005
    Posts
    11
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thank you Marcus.

    Is it a good idea to combine these in one factory-like class which is responsible for creating any class you want? Per create method you could then decide/program if it should be a singleton or in a registry?

    It seems to be that delegating the actual task of creating an object to one central class makes the coupling of classes easier? At the same time each class would know less about the actual object needed: if I want to change Addresses for Cities I can do so in that factory class?

    ...

    It reads like an idea to me. To use Factory::createNames() and the like everywhere.

    Personally I find this the hardest part ... the talking of objects to eachother.

  4. #4
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    A Factory allows you do return one of many instances of one class you specify. A simple example of a Factory for example,

    PHP Code:
    // ...
    $db DbFactory::create'mysql' );
    // ... rest of script

    class DbFactory {
    public function 
    __construct() {
    }

    public function 
    create$type ) {
    switch( 
    $type ) {
    case 
    'mysql':
    return new 
    MySqlDatabase;
    break;
    case 
    'postgre':
    return new 
    PostgreDatabase;
    break;
    // ... other databases
    }
    }

    You could if required, implement a Registry within a Factory also. That is a key benifit of design patterns, is their flexibility

  5. #5
    SitePoint Member
    Join Date
    Apr 2005
    Posts
    11
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    If I understand well then moving the creation of additionally needed objects outside of your worker classes is a good idea.

    PHP Code:
    class Factory {

    function 
    Factory() {
    }

    function 
    createNames() {}

    function 
    createAddresses() {}

    etc.


    One more question then: is it a sign of good or bad design if classes rely heavily on other classes? I would say it is good as in 'each object does what ti does best', on the other hand I would say bad as each object needs to know more and more about the other object, right?

  6. #6
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by westbroek
    If I understand well then moving the creation of additionally needed objects outside of your worker classes is a good idea.
    Not necessarily. That means you are exposing the passed object in the calling script. It's better not to do that unless you have a good reason to do so. Encapsulation means jealously guarding as much as possible behind an interface. Exactly what to do would depend on the specific case. You might need to weave the second object into other parts of the code in which case you'll want to pass it in.

    Incidentally, the internal factory method in Lastcraft's example (4) is good for testing since you can create a partial mock of the main class and then return a mock object from the factory method (which you knocked out with the partial mock). You can set return values and expectations for the mocked factory object in order to investigate its interaction with the main class. If you're not testing already, that's not as complicated as it sounds.

    Quote Originally Posted by westbroek
    is it a sign of good or bad design if classes rely heavily on other classes? I would say it is good as in 'each object does what it does best', on the other hand I would say bad as each object needs to know more and more about the other object, right?
    Objects have to talk to each other - but no more than they have to. I'd recommend trying test driven design. This helps you to focus on the interface of the class being tested as well as its immediate neighbours, and how they should interact.

  7. #7
    SitePoint Member
    Join Date
    Apr 2005
    Posts
    11
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Thumbs up

    Quote Originally Posted by McGruff
    I'd recommend trying test driven design. This helps you to focus on the interface of the class being tested as well as its immediate neighbours, and how they should interact.
    That has to be the single best recommendation ever - and one of the best links on the case, for a newcomer to testing.

    I've come across references to Simple Test but it didn't seem tempting. Boy, did I miss out! I've been playing around for several hours now, together with the article you linked to, and this is brilliant! Thank you Marcus for writing this!

    Who would have thought that doing it the other way around, write the test first, would make so much more sense and clarify so much!? Wow.

    <added> See, just now I did a db class with connection parameters in the constructor and a table class which extends the db class. Doing it step by step using Simple Test I quickly learned that testCreateTable() errored on the missing db connection info... put a db::db(connection info) in the table class... test works... let's try if I can move that out of there to another point... test fails... In 2 or 3 minutes time I prevented myself from making a (stupid) mistake I would otherwise not see ...

    Testing before coding feels like the extended version of inline syntax error highlighting.... </added>

  8. #8
    SitePoint Guru
    Join Date
    Nov 2004
    Location
    Parry Sound, ON
    Posts
    725
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I agree. I think I finally understand what all this test crap is about. I'm going to try it on my next project.

  9. #9
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    McGruff recommends Domain Driven Design, and I can recommend this book as well. Been reading it constantly over the last month and some of the stuff is just starting to make sense.

    But it'll take another reading just to put what I read to memory

    For unit testing, there is Test Driven Development, but it doesn't go into Mocks, so beaware

  10. #10
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by westbroek
    Wow.
    It is one of those eureka moments, isn't it

  11. #11
    SitePoint Member
    Join Date
    Apr 2005
    Posts
    11
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Wink

    What I find bugging with these other creation methods is that my editor doesn't show me the methods available in a class anymore

    Quote Originally Posted by McGruff
    It is one of those eureka moments, isn't it
    You have no idea... or maybe you do I feel like I skipped several classes, trials & errors and have jumped to another level all together.

    At first I followed the examples very, very close. Then now in the last couple of hours I've written a bunch of tests for several conditions and situations and from those tests I was able to see how I best could implement all those together. In one case I ended up with a class but with something I wrote just now I ended up with a function library.

    The tests remind me a bit of user persona in designing web sites. I think they're called User Stories in programming design patterns.

    Quote Originally Posted by Dr Livingstone
    Domain Driven Design
    You know, my problem with design is recognizing design from buzzwords and buzzconcepts. 2-3 years ago when I started to read up on OOP passing the database object in the constructor of another object was old school. Non-OOP almost. Now it is OK, almost new school, and you could call it Dependency Injection.

    I spoke with another programmer, a serious one, on an SEO forum the other day. He has been using this testing a longer time and said to forget about reading up on design patterns: the tests will show you how something should be implented.

    I had no idea he was right at that time.

  12. #12
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think I see where your friend is coming from but I wouldn't abandon studying design patterns altogether. I think there are a lot of useful ideas and concepts to be found - and it provides a common vocabulary to discuss design ideas.

    I find testing is a whole lot of fun at times. Every now and then when something's not working you can just try a wild guess at how to fix it without fully understanding what's going on. Sometimes you get lucky and it works, saving you taking the time to figure it out properly. If you've got the green bar it must be correct.


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
  •