Qafoo GmbH - passion for software quality

Help you and your team benefit from new perspectives on cutting-edge quality engineering techniques and tools through the Qafoo team weblog.

By Kore Nordmann, first published at Thu, 11 Jul 2013 14:51:23 +0200

Ducks Do Not Type

Even in ecosystems which generally follow a high standard of code quality I tend to find public methods in classes which do not originate from an interface or an abstract class. I think that this is a really bad habit for several reasons I will explain below. To give this some context, let's start with a simple example:

abstract class HttpClient { public function request($method, $path, $body = null, array $headers = array()); } class MyHttpClient extends HttpClient { public function request($method, $path, $body = null, array $headers = array()) { // … } public function setConfiguration($key, $value) { // … } }

If you need help refactoring your code, or your developers could need training on Object Oriented Design, do not hesitate to get in contact with us.

We have an abstract base type for HTTP clients (which could also be an interface) and one implementation. Implementations then sometimes tend to define additional public methods, like setConfiguration() in this case, for various different reasons. I want to explain why I consider this a code smell.

Semantically, the main point behind abstract classes and interfaces is to build an abstraction other components can implement against. In practice, abstract classes are also often used for code-re-use, but this is also a bad practice and might be a topic of a different blog post.

To stay with the example above, any other component in the application, which wants to use an HTTP client, defines a dependency on the abstract type HttpClient. The actually implementation used would then depend on the application configuration. This is the basic idea of Dependency Inversion (S.O.L.I.D.), or to say it with the words of Martin Fowler:

A. High-level modules should not depend on low level modules. Both should depend on abstractions.

B. Abstractions should not depend upon details. Details should depend upon abstractions.

Abstractions can be both: interface definitions or abstract classes.

Duck Typing

Then there is Duck Typing. Let's start the explanation with the opposite of Duck Typing: Java.

If you pass something to any method in Java, you define a type hint for the value you are expecting. If the type hint is on HttpClient, the compiler verifies that you only use methods / properties defined by that interface. You cannot (should not) use the concrete implementation as something else. It does not matter which concrete instance is passed to the method.

When starting to develop with Java I thought this is horribly annoying. And I still think it is – more on that later.

Duck Typing on the other hand means, that you can use anything passed to your method as anything. You usually would check if a method exists and just call it right away. You do not enforce a base type or a formal interface definition.

In the example above this would mean, that anything which can act as a HTTP client would implement a method request() and you just go and use those classes as a HTTP client. The underlying problem here is, of course, that entirely different concepts also might define a request() method, which then might break a lot.

Prototyping

When developing prototypes (not talking about prototype based object orientation, even though it also gets interesting there) or Proof Of Concepts the concept of Duck Typing is extremely important. And this is one of the reasons I usually prefer PHP over Java.

In PHP you can pass stuff around as you like and use the full power of duck typing. Want to see if a couple of components work together well and implement your business requirement? Want to play with a new library? Want to hack a feature seconds before a release date? Just do it.

BUT: If you are developing infrastructure components, Open Source libraries or any code other developers depend on: Please design and use your abstraction as if a small Java Compiler sits on your shoulder and pours hot coffee over you anytime you misuse an object.

It is the only way to make code extensible by others. A fact, even advocates of Duck Typing are aware of is that you must know your software really well, if you are using Duck Typing. If synonyms occur in method / property names, you can easily get code behaving in really strange ways. Also you cannot easily identify your ducks, since there is no clearly documented concept behind it. Wikipedia says:

In essence, the problem is that, if it walks like a duck and quacks like a duck, it could be a dragon doing a duck impersonation. One may not always want to let dragons into a pond, even if they can impersonate a duck.

Using Foreign Code

The Open Closed Principle (S.O.L.I.D.) wants us to change the behavior of our application without modifying code. This requires code to define abstractions which we can replace in the application configuration.

When using type hints on abstractions in your library you make the user of your code assume that she / he can just implement a new class based on this abstract concept and be done. If your code then calls additional methods it will just break. If it breaks immediately one can fix the code, but often enough it happens that the code will only break in obscure error conditions. And since a call to an undefined method even triggers a fatal error this will kill PHP immediately.

And often enough I want to replace such implementations / dependencies in foreign libraries. The most common thing is, that I want your library to use my Logger through an adapter, or anything similar. If you then call undocumented methods on that logger (in a special error condition)…

Package Visibility

There is one case where public methods are OK, even if they are not defined by a base concept: PHP misses package visibility. It occurs seldom "enough" (to me), but sometimes you want "internal" access to methods on a package level, even if this might not be part of the external API. Until we have package visibility in PHP, from my point of view, it is OK to define additional public methods. But please flag them clearly, to make sure everyone knows it is not part of the public API, like this:

class MyHttpClient extends HttpClient { /** * @private */ public function setCookie($key, $value) { // … } }

Conclusion

If you need help refactoring your code, or your developers could need training on Object Oriented Design, do not hesitate to get in contact with us.

It is fine to employ duck typing. At the end of the day PHP is popular because the language does not impose any limits on the way you want to work with it.

But if you start with class based object orientation, please do it right. Mixing the concepts of class based object orientation and other stuff like Duck Typing messes with everybody's head. It will break code in obscure situations which might be really hard to cover by tests.

Abstractions are documentation. The best you can provide for developers. They are not easy to find, but you should really stay with them, once defined. Interfaces can, by the way, be used to extend them over time.

Comments

  • Frank Becker on Thu, 11 Jul 2013 17:12:51 +0200

    So, in essence you say that static typing is bad and then go on to say that dynamic typing is more fun but has problems - and then suggest to solve that with coding as if you had static typing... odd.

    I'd say your code smell is only a code smell for dynamic languages - in languages like java and c# this is a non issue, because you can't use the extra public method if you get passed the abstract class or interface.

    Further: The way you suggest to use abstract classes sounds like an interface to me. Abstract classes are very well intended for code reuse (for example the strategy pattern); and this is a GOOD practice...

  • Kore on Thu, 11 Jul 2013 17:25:15 +0200

    @Frank: I do not want to say that static typing is bad, on the contrary. Sorry if that is not clear.

    You might ignore that problem when talking about Java or C#, yes, but I am talking about PHP here. And there it definitely can become a problem. That's the point of the blog post.

    I will write another blog post, why code re-use by inheritance is almost always a bad thing. On my point regarding abstract classes vs. interfaces, please see: http://qafoo.com/blog/026_abstract_classes_vs_interfaces.html

  • Ben on Thu, 11 Jul 2013 17:53:40 +0200

    Too bad php will never support type hinting for scalars. Kind of forces a certain amount of duck typing.

  • Frank Becker on Thu, 11 Jul 2013 17:54:43 +0200

    Yeah, I exaggerated a little (on purpose), but the post does sound more general; the PHP centricity was not obvious to me.
    And: I also do agree on inheritance being a (potential) bad thing; although I also think that Switch-To-Polimorphism is a good refactoring (which may use inheritance)

  • beberlei on Thu, 11 Jul 2013 18:00:15 +0200

    @Ben If some scalar values are very important for you, you can easily wrap them in an object. Say for example an E-Mail or an IP Address. Those objects can often ship a bunch of very nice helper and validation logic as well.

  • Maximilian Berghoff on Thu, 11 Jul 2013 21:29:06 +0200

    @beberlei and @kore this post shows me a mistake i made all the time. Under the headline "code-re-use" and "template pattern" i created lots of child classes with the extra behavior extending from parents with the common behavior. I am shure, that i used extra public methods too. I had had an discussion with @beberlei two weeks ago about this with writing tests for those inherit constructs.
    Cause of that i am very interessted in a post about using Aggregation instaed and the way how i can refactor my old code.

  • John Behrens on Fri, 12 Jul 2013 16:02:54 +0200

    Nice Article Kore,
    however think just too many php developers.
    However if you work correctly by the solid principles you always develop your application against the interface and never get in trouble to use this additional public methods, cause they are not defined in the interface you use.
    However PHP-World is a world where not any developer knows solid and language make thinks possible here so it might get a problem.

    If you say a class should not have any other methods than defined in the interface how do you cover Interface Segregation Principle then which engages you to have a better smaller interfaces and use if needed multiple of then?
    By ISP correct way might be implement another interface for the "unintefaced" public methods would be absolute correct.

  • pctgrass on Fri, 12 Jul 2013 23:33:57 +0200

    s/The actually implementation used/The actual implementation used/

  • Luisa_McClung on Fri, 28 Mar 2014 15:47:07 +0100

    Well @Kore, I am agree with your suggestion about php user and it helps me finally after reading your reply to frank because I have also the confusion like frank but now I understood Thanks.