接口常数的优缺点 [已关闭]

2022-08-30 07:23:06

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

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

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

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

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

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

注意:对于Java,有一个类似的问题,列出了一些很好的理由,为什么它们是坏做法,但是由于Java不是PHP,我觉得在PHP标签中再次询问它是有道理的。


答案 1

好吧,我认为这归结为足够好之间的区别。

虽然在大多数情况下,您可以通过实现其他模式(策略或蝇量)来避免使用常量,但对于不需要六个其他类来表示一个概念,有一些话要说。我认为归根结底,需要其他常数的可能性有多大。换句话说,是否需要扩展接口上的常量提供的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();
}

现在,我选择这些例子的原因很简单。该接口正在定义用户类型的枚举。这很可能会随着时间的推移而扩展,并且更适合另一种模式。但这是一个不错的用例,因为枚举由RFC2616定义,并且在类的生命周期内不会更改。UserHTTPRequest_1_1

总的来说,我不认为常量和类常量的问题是一个全局问题。我认为这是一个依赖问题。这是一个狭隘的区别,但却是一个明确的区别。我认为全球问题就像全局变量一样,这些变量没有被强制执行,因此会产生一种软的全球依赖性。但是,硬编码类会创建强制依赖项,从而创建硬全局依赖项。所以两者都是依赖关系。但我认为全球情况要糟糕得多,因为它没有得到执行......这就是为什么我不喜欢将类依赖关系全局依赖关系混为一谈的原因......

如果您编写 ,则会硬编码到 的实现细节。这会产生硬耦合,这会降低代码的灵活性,因此应避免使用。但是,接口的存在恰好允许这种类型的耦合。因此不引入任何混凝土联轴器。话虽如此,我不会仅仅为了向它添加一个常量而引入一个接口。MyClass::FOOMyClassMyInterface::FOO

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

再说一遍,这只是我对它的看法...


答案 2

我认为通常最好处理常量,特别是枚举的常量,作为接口的单独类型(“类”):

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

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


推荐