界面常数的优点和缺点

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

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

任何实现类将自动具有这些常量,例如

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

我自己的看法是,全球化是邪恶的 。 但是我想知道是否同样适用于接口常量。 鉴于对接口进行编码通常被认为是很好的做法,使用接口常量是唯一可以在类上下文之外使用的常量吗?

虽然我很好奇听到你的个人意见,而不pipe你是否使用接口常量,我主要是在你的答案中寻找客观的原因。 我不希望这是一个民意调查types的问题。 我对使用接口常量对可维护性有什么影响感兴趣。 耦合。 或者unit testing。 它与SOLID PHP有什么关系? 它是否违反在PHP中被认为是良好实践的任何编码原则? 你明白了

注意: 对于Java也有一个类似的问题 ,列出了一些很不好的理由,因为它们是错误的实践,但是由于Java不是PHP,所以我觉得在PHP标签中再次提出这个问题是合理的。

那么,我认为这可以归结为好的好的差别。

虽然在大多数情况下,你可以通过实现其他模式(策略或者轻量级)来避免使用常量,但是有些东西可以说是不需要其他六个类来表示一个概念。 我认为可以归结到的是,其他常量的可能性有多大。 换句话说,是否需要在接口上扩展由常量提供的ENUM。 如果你能预见到需要扩展它,那么就用更正式的模式。 如果没有,那么它可能就足够了(这将是足够好的,因此编写和testing的代码更less)。 这里有一个很好用的例子:

坏:

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

够好了:

 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(); } 

现在,我select这些例子的原因很简单。 User界面正在定义用户types的枚举。 这很可能会随着时间的推移而扩大,并会更好地适应另一种模式。 但HTTPRequest_1_1是一个体面的用例,因为枚举是由RFC2616定义的,并且在类的生命周期中不会改变。

一般来说,我不认为常量和类常量是一个全局性问题。 我把它看作是一个依赖问题。 这是一个狭隘的区别,而是一个确定的区别。 我看到全球性问题在全球variables中没有被强制执行,因此创造了一个软性的全球依赖性。 但是一个硬编码的类会创build一个强制的依赖关系,因此会创build一个强大的全局依赖关系。 所以都是依赖关系。 但我认为, 全球变得更糟,因为它没有被强制执行……这就是为什么我不喜欢在同一旗帜下将全局依赖关系 归类的原因…

如果您编写MyClass::FOO ,则将其硬编码为MyClass的实现细节。 这会产生一种硬耦合,这会使你的代码变得更加灵活,因此应该避免这种情况。 但是,存在的接口恰好允许这种types的耦合。 因此MyInterface::FOO不会引入任何具体的耦合。 这样说,我不会介绍一个接口只是添加一个常数。

所以,如果你使用的是接口,并且你确信你(或者其他任何人)不需要额外的值,那么接口常量就不会有太大的问题了。devise不包括任何常量或条件或魔法数字或魔术string或硬编码的任何东西。 但是,这会增加开发时间,因为您必须考虑使用情况。 我的观点是,大多数时候,花费额外的时间来构build一个伟大的坚实的devise是绝对值得的。 但有些时候真正可以接受的真的是可以接受的(需要有经验的开发人员来理解差异),在这种情况下就没有问题。

再次,这只是我的看法…

我认为它通常更好地处理常量,特别是常量,作为一个单独的types(“类”)从您的接口:

 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) { ... } } 

或者,如果你想使用一个类作为一个名字空间:

 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) { ... } } 

它并不是说你只是使用常量,你正在使用枚举值或枚举的概念,一组受限制的值被认为是一个特定的types,具有特定的用法(“域”?)