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

Download our free e-book "Crafting Quality Software" with a selection of the finest blog posts as PDF or EPub.

Crafting Quality Software

You can also buy a printed version of the book on Amazon or on epubli.

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.

Download our free e-book "Crafting Quality Software" with a selection of the finest blog posts as PDF or EPub.

Crafting Quality Software

You can also buy a printed version of the book on Amazon or on epubli.

Get Technical Insights With Our Newsletter

Stay up to date with regular new technological insights by subscribing to our newsletter. We will send you articles to improve your developments skills.

Comments

  • Sofi on Thu, 23 Feb 2017 22:53:39 +0100

    I wanted to leave you a feedback on the text. In Section "Using Foreign Code", you are describing a problem that is breaking Liskov substitution principle and not the Open/closed principle.

    I think this is wrongly formed and miss-leading, you should improve it.