PHP接口允许在接口中定义常量,例如
interface FooBar
{
const FOO = 1;
const BAR = 2;
}
echo FooBar::FOO; // 1
任何实现类都会自动提供这些常量,例如
class MyFooBar implement FooBar
{
}
echo MyFooBar::FOO; // 1
我对此的看法是that anything Global is Evil。但我想知道接口常量是否同样适用。鉴于针对接口的编码通常被认为是良好的做法,使用接口常量是在类上下文之外使用的唯一常量吗?
虽然我很想听听您的个人意见以及您是否使用界面常量,但我主要是在您的答案中寻找客观原因。我不希望这是一个民意调查问题。我对使用接口常量对可维护性有什么影响感兴趣。耦合。或单元测试。它与SOLID PHP有什么关系?它是否违反了任何被认为是PHP良好实践的编码原则?你明白了......
注意: 有一个similar question for Java列出了一些很好的理由,为什么它们是Bad Practice,但由于Java不是PHP,我觉得有理由问它再次在PHP标记内。
答案 0 :(得分:132)
嗯,我认为它归结为 good 和足够好之间的区别。
虽然在大多数情况下你可以通过实现其他模式(策略或者轻量级)来避免使用常量,但是有一些东西可以说不需要其他六个类来表示一个概念。我认为它归结为,是否有可能需要其他常量。换句话说,是否需要扩展接口上常量提供的ENUM。如果您可以预见需要扩展它,那么请采用更正式的模式。如果没有,那么它就足够了(它足够好,因此编写和测试的代码更少)。这是一个足够好用的例子:
为:
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();
}
现在,我选择这些例子的原因很简单。 User
接口定义了用户类型的枚举。这很可能随着时间的推移而扩大,并且更适合另一种模式。但是HTTPRequest_1_1
是一个不错的用例,因为枚举是由RFC2616定义的,并且在类的生命周期内不会改变。
通常,我没有看到常量和类常量作为全局问题的问题。我认为这是一个依赖问题。这是一个狭隘的区别,但是一个明确的区别。我看到全局问题,如未强制执行的全局变量,因此创建了一个软全局依赖。但是,硬编码的类会创建强制依赖,因此会创建一个强大的全局依赖。所以两者都是依赖关系。但是我认为 global 要糟糕得多,因为它没有强制执行......这就是为什么我不喜欢将类依赖与全局依赖关系 em>在同一条旗帜下......
如果您撰写MyClass::FOO
,则会对MyClass
的实施细节进行硬编码。这会产生硬耦合,这会使您的代码不那么灵活,因此应该避免。但是,存在接口以允许这种类型的耦合。因此MyInterface::FOO
没有引入任何具体的耦合。话虽如此,我不会引入一个接口只是为它添加一个常量。
因此,如果你正在使用界面,并且你非常确定你(或其他任何人)不需要额外的值,那么我真的不会看到一个巨大的接口常量问题...最好的设计不包括任何常量或条件或魔术数字或魔术字符串或硬编码的任何东西。但是,这会增加开发时间,因为您必须考虑使用。我的观点是,大多数时候,绝对值得花费额外的时间来构建一个坚固的设计。但有时足够好确实可以接受(并且需要有经验的开发人员来理解差异),在这些情况下它很好。
同样,这只是我对它的看法......
答案 1 :(得分:10)
我认为通常更好地处理常量,特别是枚举常量,作为您界面中的单独类型(“类”):
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) { ... }
}
不是你只使用常量,而是使用枚举值或枚举的概念,其中一组受限值被视为特定类型,具有特定用法(“域”?)