PHP interfaces allow the definition of constants in an interface, e.g.
PHP接口允许定义接口中的常量,例如。
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 …
虽然我很想听听你的个人意见,以及你是否使用了接口常量,但我主要是在寻找你的答案中的客观原因。我不希望这是一个民意调查类型的问题。我对使用接口常量对可维护性有什么影响很感兴趣。耦合。或单元测试。它与固态PHP有什么关系?它是否违反了在PHP中被认为是良好实践的任何编码原则?你明白了……
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.
注意:Java也有一个类似的问题,它列出了一些很好的理由来说明为什么它们是不好的实践,但是由于Java不是PHP,所以我认为再次在PHP标记中询问它是合理的。
2 个解决方案
#1
110
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:
虽然在大多数情况下,您可以通过实现其他模式(策略或flyweight)来避免使用常量,但是需要说明的是,不需要六个其他类来表示概念。我认为它可以归结为,需要其他常数的可能性有多大。换句话说,是否需要扩展接口上常量提供的ENUM。如果您能够预见到需要扩展它,那么请使用更正式的模式。如果不是,那么它就足够了(它足够好,因此编写和测试的代码就更少了)。这里有一个好例子和坏例子:
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.
我选择这些例子的原因很简单。用户界面正在定义用户类型的枚举。这很可能随着时间的推移而扩展,并且更适合另一种模式。但是HTTPRequest_1_1是一个不错的用例,因为enum是由RFC2616定义的,在类的生命周期中不会改变。
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.
如果您编写MyClass::FOO,您将硬编码到MyClass的实现细节。这将创建一个硬耦合,从而降低代码的灵活性,因此应该避免这种情况。然而,接口的存在恰好允许这种类型的耦合。因此,MyInterface::FOO不引入任何具体的耦合。尽管如此,我不会仅仅为了给它添加一个常数而引入一个接口。
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...
这也是我的看法……
#2
9
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" ? )
这并不是说您只使用常量,而是使用了枚举值或枚举的概念,枚举值是一组受限制的值,它们被认为是特定的类型,具有特定的用法(“domain”?)
#1
110
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:
虽然在大多数情况下,您可以通过实现其他模式(策略或flyweight)来避免使用常量,但是需要说明的是,不需要六个其他类来表示概念。我认为它可以归结为,需要其他常数的可能性有多大。换句话说,是否需要扩展接口上常量提供的ENUM。如果您能够预见到需要扩展它,那么请使用更正式的模式。如果不是,那么它就足够了(它足够好,因此编写和测试的代码就更少了)。这里有一个好例子和坏例子:
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.
我选择这些例子的原因很简单。用户界面正在定义用户类型的枚举。这很可能随着时间的推移而扩展,并且更适合另一种模式。但是HTTPRequest_1_1是一个不错的用例,因为enum是由RFC2616定义的,在类的生命周期中不会改变。
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.
如果您编写MyClass::FOO,您将硬编码到MyClass的实现细节。这将创建一个硬耦合,从而降低代码的灵活性,因此应该避免这种情况。然而,接口的存在恰好允许这种类型的耦合。因此,MyInterface::FOO不引入任何具体的耦合。尽管如此,我不会仅仅为了给它添加一个常数而引入一个接口。
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...
这也是我的看法……
#2
9
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" ? )
这并不是说您只使用常量,而是使用了枚举值或枚举的概念,枚举值是一组受限制的值,它们被认为是特定的类型,具有特定的用法(“domain”?)