Pros and Cons of Interface constants

PhpInterfaceConstants

Php Problem Overview


PHP interfaces allow the definition of constants in an interface, e.g.

interface FooBar
{
    const FOO = 1;
    const BAR = 2;
}
echo FooBar::FOO; // 1

Any implementing class will automatically have these constants available, e.g.

class MyFooBar implement FooBar
{
}
echo MyFooBar::FOO; // 1

My own take on this is that anything Global is Evil. But I wonder if the same applies to Interface Constants. Given that Coding against an Interface is considered good practise in general, is using Interface Constants the only constants that are acceptable to use outside a class context?

While I am curious to hear your personal opinion and whether you use Interface constants or not, I'm mainly looking for objective reasons in your answers. I dont want this to be a Poll Type question. I'm interested in what effect using interface constants has on Maintainability. Coupling. Or Unit Testing. How does it relate to SOLID PHP? Does it violate any coding principles that are considered Good Practise in PHP? You get the idea …

Note: there is a similar question for Java that listed some quite good reasons why they are Bad Practice, but since Java isn't PHP, I felt it justified to ask it within the PHP tag again.

Php Solutions


Solution 1 - Php

Well, I think that it boils down to the difference between good and good enough.

While in most cases you can avoid the use of constants by implementing other patterns (strategy or perhaps flyweight), there is something to be said for not needing a half dozen other classes to represent a concept. I think what it boils down to, is how likely is there a need for other constants. In other words, is there a need to extend the ENUM provided by the constants on the interface. If you can foresee needing to expand it, then go with a more formal pattern. If not, then it may suffice (it'll be good enough, and hence be less code to write and test). Here's an example of a good enough and a bad use:

Bad:

interface User {
    const TYPE_ADMINISTRATOR = 1;
    const TYPE_USER          = 2;
    const TYPE_GUEST         = 3;
}

Good Enough:

interface HTTPRequest_1_1 {
    const TYPE_CONNECT = 'connect';
    const TYPE_DELETE  = 'delete';
    const TYPE_GET     = 'get';
    const TYPE_HEAD    = 'head';
    const TYPE_OPTIONS = 'options';
    const TYPE_POST    = 'post';
    const TYPE_PUT     = 'put';

    public function getType();
}

Now, the reason that I chose those examples is simple. The User interface is defining an enum of user types. This is very likely to expand over time and would be better suited by another pattern. But the HTTPRequest_1_1 is a decent use-case, since the enum is defined by RFC2616 and will not change for the lifetime of the class.

In general, I don't see the problem with constants and class constants as being a global problem. I see it as a dependency problem. It's a narrow distinction, but a definite one. I see global problems as in global variables which are not enforced, and as such create a soft global dependency. But a hard-coded class creates an enforced dependency, and as such create a hard global dependency. So both are dependencies. But I consider the global to be far worse since it's not enforced... Which is why I don't like to lump class dependencies with global dependencies under the same banner...

If you write MyClass::FOO, you're hard-coded to the implementation details of MyClass. This creates a hard-coupling, which makes your code less flexible, and as such should be avoided. However, interfaces exist to permit exactly this type of coupling. Therefore MyInterface::FOO doesn't introduce any concrete coupling. With that said, I wouldn't introduce an interface just to add a constant to it.

So if you're using interfaces, and you're very sure that you (or anyone else for that matter) won't need additional values, then I don't really see a huge issue with the interface constants... The best designs wouldn't include any constants or conditionals or magic-numbers or magic-strings or hard-coded anything. However, that adds additional time to the development, as you must consider the uses. My view is that most times it's absolutely worth taking the additional time to build a great solid design. But there are times when good enough really is acceptable (and it takes an experienced developer to understand the difference), and in those cases it's fine.

Again, that's just my view on it...

Solution 2 - Php

I think that its usually better to handle constants, specially enumerated constants, as a separate type ("class") from your interface:

define(TYPE_CONNECT, 'connect');
define(TYPE_DELETE , 'delete');
define(TYPE_GET    , 'get');
define(TYPE_HEAD   , 'head');
define(TYPE_OPTIONS, 'options');
define(TYPE_POST   , 'post');
define(TYPE_PUT    , 'put');

interface IFoo
{
  function /* int */ readSomething();
  function /* void */ ExecuteSomething(/* int */ param);
}

class CBar implements IFoo
{
  function /* int */ readSomething() { ...}
  function /* void */ ExecuteSomething(/* int */ param) { ... }
}

or, if you want to use a class as a namespace:

class TypeHTTP_Enums
{
  const TYPE_CONNECT = 'connect';
  const TYPE_DELETE  = 'delete';
  const TYPE_GET     = 'get';
  const TYPE_HEAD    = 'head';
  const TYPE_OPTIONS = 'options';
  const TYPE_POST    = 'post';
  const TYPE_PUT     = 'put';
}

interface IFoo
{
  function /* int */ readSomething();
  function /* void */ ExecuteSomething(/* int */ param);
}

class CBar implements IFoo
{
  function /* int */ readSomething() { ...}
  function /* void */ ExecuteSomething(/* int */ param) { ... }
}

Its not that you are using just constants, you are using the concept of enumerated values or enumerations, which a set of restricted values, are considered a specific type, with a specific usage ("domain" ? )

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGordonView Question on Stackoverflow
Solution 1 - PhpircmaxellView Answer on Stackoverflow
Solution 2 - PhpumlcatView Answer on Stackoverflow