【问题标题】:'Named parameters' using arrays使用数组的“命名参数”
【发布时间】:2015-12-04 05:11:39
【问题描述】:

我的项目中有一些实用程序类,并且正在考虑使用数组来创建“命名参数”。

我的代码允许我链接方法或单独使用它们。例如,我有一个处理字符串的字符串类(有点明显:))

现在一组带有链接的方法示例如下所示:

print Utils\String::Set('my string')->UcFirst()->UcLast()->Get()

虽然没有链接的相同内容看起来像这样:

$string = Utils\String::UcFirst('my string');
print Utils\String::UcLast($string);

这适用于某些方法,但是对于具有以下参数的“后缀”等方法存在问题:$string、$suffix。

这会导致链接问题,因为我最终会这样做

Suffix(null, ' New')

这看起来不太好。

在这种情况下是否可以使用数组来提供命名参数? 然后我就可以为链接做到这一点:

Suffix(['suffix' => ' New'])

但是我不确定这会使维护项目变得多么困难,以及是否值得放弃非链接方法以便不需要数组。

【问题讨论】:

    标签: php arrays oop methods


    【解决方案1】:

    看来您正在做的是将函数应用程序的前缀符号转换为中缀符号。也就是说,你写的是x1->f(x2,…,xn),而不是f(x1,x2,…,xn)

    您始终可以只使用前缀表示法来编写S::UcLast(S::UcFirst("my string"))。简洁来自use Utils\String as S

    要将-> 用作中缀表示法的一部分,您需要一个如下所示的类定义(我假设您的实现也是这样的)。

    class UtilStringProxy
    {
      public $string;
    
      public function __construct($string)
      {
        $this->string = $string;
      }
    
      public function UcFirst()
      {
        $this->string = S::UcFirst($this->string);
        return $this;
      }
    
      public function UcLast()
      {
        $this->string = S::UcLast($this->string);
        return $this;
      }
    
      public function Suffix($suffix)
      {
        $this->string = S::Suffix($this->string, $suffix);
        return $this;
      }
    
    }
    

    第一个论点被剥离。这就是我们如何将f 置于中缀位置(在操作数 1 和 2 之间)。然后你可以写:

    (new UtilStringProxy("my string"))->UcFirst()->UcLast()->Suffix("New")->string
    

    就我个人而言,我并没有看到这个愿望:

    S::Suffix(S::UcLast(S::UcFirst("my string")), "New")
    

    附录

    函数组合是查看这一点的另一种方式。函数应用程序提供从左到右的读数,而函数组合可以通过翻转参数为您提供从左到右或从右到左的读数。

    UtilStringProxy 的示例基本上是为特定函数集 UcFirst, UcLast, Suffix 定义的从左到右的函数组合。你可以从中概括。

    class Compose
    {
      public $f;
    
      public function __construct(callable $f)
      {
        $this->f = $f;
      }
    
      public function lr(callable $g)
      {
        $f = $this->f;
        return new Compose(function ($x) use ($f, $g) {
          return $g($f($x));
        });
      }
    
      public function rl(callable $g)
      {
        $f = $this->f;
        return new Compose(function ($x) use ($f, $g) {
          return $f($g($x));
        });
      }
    
      public function call($x)
      {
        $f = $this->f;
        return $f($x);
      }
    
    }
    

    我添加了call 方法,因为PHP 在其解析器中存在一个不幸的弱点,因此您不能编写expr(x) 来将expr 应用于x 以用于任何表达式——只有一些。如果您只想返回组合函数,请使用->f,如果您想立即应用它,请使用->call(x)

    使用这个你可以写从左到右的作文:

    (new Compose('strtolower'))->lr('ucfirst')->call("hEllo World")
    

    或从右到左合成:

    (new Compose('ucfirst'))->rl('strtolower')->call("hEllo World")
    

    【讨论】:

      【解决方案2】:

      我不会走这条路。使用数组替代命名参数的问题在于,您无法将参数定义为必需或可选,也无法键入提示它们,这使得错误更难发现或增加了额外验证的要求。

      想一想如何使用这个库编写代码,如果你不知道的话。任何 IDE 都可以快速显示方法采用的参数,但如果它只显示 array,我必须仔细查看文档。

      我什至会更进一步,将静态方法与对象链接方法分开。现在看来String::Suffix 可以用值参数静态调用,也可以在没有值参数的情况下非静态调用。除了违反 E_STRICT 标准之外,这还把两个完全不同的接口混合到一个方法中。

      这就是我将如何实现您的 API:

      namespace Util;
      
      class String // you might want to change the name to "Strings", "string" is a reserved keyword in PHP 7
      {
          public static function Suffix($string, $suffix)
          {
              return (new StringWrapper($string))->Suffix($suffix);
          }
      }
      class StringWrapper
      {
          private $value;
          public function __construct($string)
          {
              $this->value = $string;
          }
          public function Suffix($suffix)
          {
              return new static($this->value . $suffix);
          }
          public function __toString()
          {
              return $this->value; 
          }
      }
      

      您会看到String 中的静态接口只是StringWrapper 类中实际实现的一个薄包装。 StringWrapper 是一种不可变的值对象类型,并提供了字符串操作的方法。每个方法调用都会返回一个包含修改后值的类的新实例。使用__toString,您最终可以将其转换为标量字符串值(这就是您使用Get() 方法所做的)

      瞧,不再需要命名参数了

      【讨论】:

      • 感谢您的回复。这个解决方案看起来好多了,我必须同意在一种方法中有 2 个接口是一个坏主意。您在 StringWrapper::Suffix 中使用了静态类,这是什么意思?我在 PHP 手册中找不到它。
      • new static 使用late static binding。如果您不打算允许 StringWrapper 的子类,您也可以编写 new selfnew StringWrapper
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-06
      相关资源
      最近更新 更多