design patterns - Can I make other developers aware that they can define certain functions for extra-functionality even if they're not required?

The language is PHP. I have the certain, in my opinion, code smell, it doesn't feel right: In my controller class that ingests objects of the same type (interface, it's ViewBlockInterface), I check if these certain objects have certain methods and based on that, do stuff, if not, just skip that object in the array.

if( method_exists( $loaded_module, 'getBlockViewStylesFilePath' ) ) {
    //logic with $loaded_module->getBlockViewStylesFilePath()
}

The problem with this approach is that, unless you read the documentation (which, of course, almost no one does), you won't know about it. I don't want to throw errors, but in my interface, ViewBlockInterface, I don't want to define these, because it's not necessary that every block have CSS / scripts attached to it.

In short, I want the developer to know that if he defines these functions, stuff will happen, but it's not required to have them. I also believe that the scope of this sub-system is so small that I don't want to create 4+ interfaces just for this.

What can I do here?

3 Answers

  1. kate- Reply

    2019-11-14

    I would prefer using interfaces to checking method_exists like your example. First, the getBlockViewStylesFilePath should be defined in a dedicated interface. Both the interface and the method should be documented to tell other developers what happen when implementing this interface in their classes.

    interface HavingStyleInterface {
        /**
         * PHPDoc here
         */
        public function getBlockViewStylesFilePath() {};
    }
    

    Next, instead of checking the existence of a method, I would prefer to check if an object implements an interface. This check supports type-hinting in IDEs and can be refactored easier.

    if ($loaded_module instanceof HavingStylesInterface) {
        // do stuff
    }
    

    Now, you can tell other developers that if he/she implements the HavingStylesInterface, something will happen. Obviously, implementing that interface is optional.

    Finally, remember the I in SOLID principles. Multiple small interfaces are prefered than a big one.

  2. Aaron- Reply

    2019-11-14

    Since the question originally did not mention PHP (just a code block without language specification), I've tried to come up with a couple of solutions in a few languages I've used:

    • Objective-C's version of interfaces, called 'protocols', can have optional methods.

    • In Swift, you can define a protocol extension with empty functions as default implementations for optional methods. They can (but do not have to be) overridden by other developers using the protocol (see here).

    • Java, version 8 and above, allow interfaces to have default methods which, like the Swift case, you should leave empty but might be overridden by other developers.

  3. Aaron- Reply

    2019-11-14

    I would go with construction parameters

    Provide a default constructor and overload it so you can optionally pass in more functions. All your objects can have the function, but by default it does nothing

    class A 
    { 
        function __construct() 
        { 
            $a = func_get_args(); 
            $i = func_num_args(); 
            if (method_exists($this,$f='__construct'.$i)) { 
                call_user_func_array(array($this,$f),$a); 
            } 
        } 
    
        function __construct1($getBlockViewStylesFilePath) 
        { 
            //set optional function to be what was passed in
        } 
    
    } 
    

Leave a Reply

Your email address will not be published. Required fields are marked *

You can use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>