【问题标题】:Pros and Cons of Interface constants [closed]接口常量的优缺点
【发布时间】:2011-07-18 01:33:55
【问题描述】:

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 标记。

【问题讨论】:

  • 嗯,我从来没有遇到过在接口中定义常量的需要。值得知道的是,实现接口的类不能覆盖常量,而只是相互扩展的类可以覆盖常量。
  • 我相信常量并不坏,因为即使我们关心单元可测试性,它们也具有可预测的值。全局变量是邪恶的,因为任何人都可以更改它,因为它是一个变量,并且所有内容都有范围,但常量永远不会改变它的值,因此术语常量。

标签: php interface constants


【解决方案1】:

我认为将常量(特别是枚举常量)作为与您的界面分开的类型(“类”)来处理通常会更好:

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

这并不是说您只使用常量,而是使用枚举值或枚举的概念,其中一组受限值被视为具有特定用途的特定类型(“域”?)

【讨论】:

    【解决方案2】:

    嗯,我认为这归结为 goodgood enough 之间的区别。

    虽然在大多数情况下,您可以通过实现其他模式(策略或可能是享元)来避免使用常量,但需要说明的是不需要六个其他类来表示一个概念。我认为归结为,需要其他常数的可能性有多大。也就是说,是否需要扩展接口上的常量提供的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 问题,就像未强制执行的全局变量一样,因此创建了软全局依赖项。但是硬编码的类会创建一个强制依赖,因此会创建一个硬全局依赖。所以两者都是依赖关系。但是我认为 global 更糟糕,因为它没有被强制执行......这就是为什么我不喜欢将 class dependenciesglobal dependencies em> 在同一旗帜下...

    如果您编写MyClass::FOO,您将被硬编码为MyClass 的实现细节。这会产生硬耦合,使您的代码不太灵活,因此应该避免这种情况。然而,接口的存在正好允许这种类型的耦合。因此MyInterface::FOO 没有引入任何具体的耦合。话虽如此,我不会仅仅为了给它添加一个常量而引入一个接口。

    因此,如果您正在使用接口,并且您非常确定您(或其他任何人)不需要额外的值,那么我并没有真正看到巨大的接口常量的问题......最好的设计不会包含任何常量或条件或幻数或幻数或硬编码任何东西。但是,这会增加开发时间,因为您必须考虑用途。我的观点是,大多数时候绝对值得花额外的时间来构建一个出色的可靠设计。但有时足够好确实是可以接受的(需要有经验的开发人员才能理解其中的区别),在这种情况下没关系。

    再次,这只是我的看法......

    【讨论】:

    • 在这种情况下,作为用户的不同模式,您会建议什么?
    • @Jacob:我会把它抽象出来。根据您的需要,我可能会构建一个 Access 类,该类将从数据库表中获取其数据。这样添加新级别就像插入新行一样简单。另一种选择是构建一个 ENUM 类集(每个权限角色都有一个类)。然后,您可以在必要时扩展类以提供适当的权限。但是还有其他方法也可以使用
    • 非常扎实且清晰的答案! +1
    • 具有公共常量的类不应该有任何方法。它应该只是数据结构或只是对象 - 不能两者兼而有之。
    • @FrederikKrautwald:您可以避免使用多态性的条件(在大多数情况下):检查 this answerwatch this Clean Code talk...
    猜你喜欢
    • 1970-01-01
    • 2010-12-28
    • 2010-10-07
    • 2011-09-08
    • 2014-01-03
    • 2016-01-15
    • 1970-01-01
    • 1970-01-01
    • 2013-11-30
    相关资源
    最近更新 更多