6

I have a library that I use in several applications. It has a method that returns a URL:

class UrlBuilder {
  public function url($config) {
    $config = do_some_checks($config);
    return make_url($config));
  }
}

Often an application wants to alter the way the URL is built, and I would like to make that clear from the way the library is implemented. I came up with these solutions:

  1. Mention it in the library docs, but otherwise just let the application override the method like any other:

    class MyAppUrlBuilder extends UrlBuilder {
      public function url($config) {
        $config = do_some_checks($config);
        return make_url_differently($config);
      }
    }
    
  2. Call a separate function that is just there to be overridden:

    class UrlBuilder {
      public function url($config) {
        $config = do_some_checks($config);
        $config = $this->alterConfig($config);
        return make_url($config));
      }
    
      protected function alterConfig($config) { return $config; }
    }
    
  3. Provide a setter to set a customization callback:

    class UrlBuilder {
      public function url($config) {
        $config = do_some_checks($config);
        if ($this->configModifier)
          $config = $this->configModifier($config);
        return make_url($config));
      }
    
      public function setConfigModifier($configModifier) {
        $this->configModifier = $configModifier;
      }
    }
    

Is there a semantic difference and (if that is not a matter of taste) is any of them preferable?

  • For option 3, you could describe it as "provide a setter to provide customization instructions" – alexanderbird Apr 28 '16 at 3:34
8

I'd prefer Option #4: Avoid relying on inheritance.

Just make UrlBuilder an interface, and give people some way to inject their own implementation to the process, overriding the default you provided.

interface UrlBuilder {
    public function url($config);
}

class DefaultUrlBuilder implements UrlBuilder{
  public function url($config) {
    $config = do_some_checks($config);
    return make_url($config));
  }
}

This way it shifts the responsibility: You don't need to worry too much about all the possible ways they might want to subclass your implementation, because they don't have to. If their needs are simple and they subclass it, well, good for them, they found a shortcut. If their needs are complex, well, they've got the freedom to do something else.

... That said, of the three options, my favorite is #2, if you have a very clear vision of exactly what they'll want to customize.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.