接口常数的优缺点

PHP 接口允许在接口中定义常量,例如。

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

任何实现类都会自动拥有这些常量,例如。

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

我自己的看法是 环球公司的一切都是邪恶的。但我想知道是否同样适用于接口常量。鉴于 对接口进行编码通常被认为是良好的实践,使用接口常量是否是唯一可以在类上下文之外使用的常量?

虽然我很好奇听到你的个人意见,你是否使用接口常量与否,我主要是寻找客观原因,在你的答案。我不希望这是一个民意调查类型的问题。我感兴趣的是使用接口常量对可维护性有什么影响。连接。或单元测试。它与 很可靠 PHP 有什么关系?它是否违反了 PHP 中被认为是良好实践的任何编码原则?你知道..。

注意: < em > 有一个 类似的问题列出了一些很好的理由说明为什么它们是坏习惯,但是因为 Java 不是 PHP,我觉得在 PHP 标签中再次问它是合理的。

41636 次浏览

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...

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" ? )