SitePoint Sponsor

User Tag List

Results 1 to 6 of 6
  1. #1
    Twitter: @AnthonySterling silver trophy AnthonySterling's Avatar
    Join Date
    Apr 2008
    Location
    North-East, UK.
    Posts
    6,111
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)

    A question of abstraction and exceptions, I think.

    I'd just like to run this past some of you fine folk, I'm sure the solution is obvious but despite a rewrite or two I always end up and the same place.

    I have a connection object, it can(could?) use many different sources to perform general CRUD operations. Soap, Pdo, XML etc...

    So, I made this object pretty much a decorator around an adaptor object, and this is where i think it gets messy.

    As such, here's some pseudo code (we all like code).
    PHP Code:
    class Connection
    {
        protected
            
    $adapter;
            
        public function 
    __construct(Connection_Adapter_Interface $adapter){
            
    $this->adapter $adapter;
        }
        
        public function 
    login(Connection_Credentials $credentials){
            
    $this->adapter->login($credentials);
        }
        
    }

    interface 
    Connection_Adapter_Interface
    {
        public function 
    login(Connection_Credentials $credentials);
    }

    class 
    Connection_Adapter_Soap implements Connection_Adapter_Interface
    {
        public function 
    login(Connection_Credentials $credentials){
            
    #login n stuff
        
    }
    }

    class 
    Connection_Adapter_Pdo implements Connection_Adapter_Interface
    {
        public function 
    login(Connection_Credentials $credentials){
            
    #login n stuff
        
    }

    Firstly, I'm worried about the maintenance of it. If I want to add a new feature, I have to add a method to the Connection object then to the Interface, and then to every Adapter.

    Messy.

    Secondly, I'm worried about exceptions. Say for example the PDO object throws an exception in the adapter, I'll catch it and throw a generic Adapter_Exception but I lose the backtrace.

    Even worse, what if the Soap Client throws an exception? I cannot do the same because Soap likes throwing exceptions for everything! In this instance, it throws an exception for invalid login details.

    This is context which I'd like to get back to the connection object, but how aware should it be of these exceptions. It feels wrong to be letting a Soap exception bubble up out of the Adapter context.

    So, given this example, Pdo throws an exception, I catch it and re-throw as an Adapter exception, I catch this in the Connection object and do what?

    I'm struggling a little, and maybe because I just have me, myself and I to discuss this with.

    This is where you come in...

    Suggestions, comments, chastisement most welcome.
    @AnthonySterling: I'm a PHP developer, a consultant for oopnorth.com and the organiser of @phpne, a PHP User Group covering the North-East of England.

  2. #2
    Theoretical Physics Student bronze trophy Jake Arkinstall's Avatar
    Join Date
    May 2006
    Location
    Lancaster University, UK
    Posts
    7,062
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    The first point you made is typical of all systems - extending functionality in interfaces always means alot of code due to the classes which extend it.

    The solution I can think of for that situation is composition. Get the data connection objects to have basic functions - inserting, updating, etc etc. Some extra things for anything you can think of.

    Then for a new feature, simply write a class for that feature. That class holds the chosen data connection object and uses those basic step methods to add functionality.

    The exceptions - unfortunately I can see the solutions using a lot of catches and throws.
    Jake Arkinstall
    "Sometimes you don't need to reinvent the wheel;
    Sometimes its enough to make that wheel more rounded"-Molona

  3. #3
    Twitter: @AnthonySterling silver trophy AnthonySterling's Avatar
    Join Date
    Apr 2008
    Location
    North-East, UK.
    Posts
    6,111
    Mentioned
    3 Post(s)
    Tagged
    0 Thread(s)
    Thanks for helping out Jake.

    With regards to the interfaces, I think you're right, after all that's what they're there for - to enforce a contract.

    If i didn't extend the implementing Adapters to suit, then system integrity would be affected. Which of course is why I'm using them after all!

    The composition idea is pretty much what I'm planning to do, however, I'm trying not to go the full DAO route(Fig 9.3 & 9.4 scary the hell out of me!) as this would mean creating DAO objects for each Adapter.

    I guess, in my head, I'd like to be able to keep the abstraction level with the adaptors to minimise the amount of code I'd have to write.

    Something along the lines of...
    PHP Code:
    class ProductDAO
    {
        protected
            
    $connection;
        
        public function 
    __construct(Connection $connection){
            
    $this->connection $connection;
        }
        
        public function 
    findProductById($id){
            try{
                
    $result $this->connection->query('FIND Product WHERE Id = ' . (int)$id);
                if(
    $result->products[0] instanceof Product){
                    return 
    $result->products[0];
                }
            }catch(
    Connection_Exception $exception){
                
            }
            return new 
    NullProduct();
        }

    Still, the exceptions do worry me somewhat.
    @AnthonySterling: I'm a PHP developer, a consultant for oopnorth.com and the organiser of @phpne, a PHP User Group covering the North-East of England.

  4. #4
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,729
    Mentioned
    104 Post(s)
    Tagged
    4 Thread(s)
    Well if it helps, you may find it useful to expose this question to the PHP Application Design forum.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  5. #5
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by AnthonySterling View Post
    Firstly, I'm worried about the maintenance of it. If I want to add a new feature, I have to add a method to the Connection object then to the Interface, and then to every Adapter.
    It seems like there's a level of indirection too many here. How about this:

    PHP Code:
    interface Connection
    {
        public function 
    login(Connection_Credentials $credentials);
    }

    class 
    Connection_Adapter_Soap implements Connection
    {
        public function 
    login(Connection_Credentials $credentials){
            
    #login n stuff
        
    }
    }

    class 
    Connection_Adapter_Pdo implements Connection
    {
        public function 
    login(Connection_Credentials $credentials){
            
    #login n stuff
        
    }


    Regarding the exceptions, you can either catch them and throw some of your own, or you can let them through. Most of the time, when an exception occurs, you need to worry about the gritty details anyway, so wrapping them often doesn't really make that much sense. If you think it does, you can still create your own exception and let this have the original exception as a property, thus allowing debugging.

  6. #6
    SitePoint Guru
    Join Date
    Oct 2006
    Location
    Queensland, Australia
    Posts
    852
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    In regards to the exception matter. From what I gather, you're concerned that by letting all the exception bubble-up, you expose higher level code to lower-level exceptions, which can increase complexity with the higher level code when it comes to dealing with this wide range of potentially unexpected exception, or otherwise bubbling through to the global exception handler which usually results in script abortion - both of which are unideal.

    You're concern then with dealing with these exceptions within the lower-level Connection class, is that you fear that if you keep the exception handling simple and dumb, you'll lose important information such as login failures and exception stacks, which higher level objects could use. On the other hand, if you make the exception handling smart in the Connection class, you introduce complexity, and worst of all, you have to make the Connection class aware of the all the implementations of the Connection_Adapter_Interface, which would reduce cohesion and create a bit of a maintenance problem.

    The solution I'd propose then, is to make all implementations of Connection_Adapter_Interface responsible for catching their own exceptions, and converting them to a standardised exception which can be easily understood by the Connection class. For example...

    So to extend on your example, it may look like this...

    PHP Code:
    <?php

    class ConnectionAdapterException extends RuntimeException {}

    class 
    Connection
    {
        protected 
    $adapter;
        
        public function 
    __construct(IConnectionAdapter $adapter) {
            
    $this->adapter $adapter;
        }
        
        public function 
    login(ConnectionCredentials $credentials) {
            
    $this->adapter->login($credentials);
        }
        
    }

    interface 
    IConnectionAdapter
    {
        public function 
    login(ConnectionCredentials $credentials);
    }

    class 
    ConnectionAdapter_SOAP implements IConnectionAdapter
    {
        public function 
    login(ConnectionCredentials $credentials) {
            try {
                
    // Login attempt here
            
    } catch (Exception $e) {
                if(<
    invalid username>)
                    throw new 
    ConnectionAdapterException("Username is incorrect."001);
                else if(<
    invalid password>)
                    throw new 
    ConnectionAdapterException("Password is incorrect."002);
                else
                    throw new 
    ConnectionAdapterException("Unknown exception."003);
            }
        }
    }

    class 
    ConnectionAdapter_PDO implements IConnectionAdapter
    {
        public function 
    login(ConnectionCredentials $credentials) {
            
    #login n stuff
        
    }
    }
    Note the use of the exception code field. This can be handy for communicating the type of error to the Connection class, and even means you could leave out a custom message, instead defining the message in the Connection class rather rather than in each implementation of IConnectionAdapter. Do remember though that it's just an example, and there are other ways of tackling this.

    Also note that I changed your class and interface names to make them more idiomatic. You can change them back though if you wish of course.


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
  •