xp-forge/partial

部分类型

v4.0.0 2020-04-10 17:27 UTC

README

Build Status on TravisCI XP Framework Module BSD Licence Requires PHP 7.0+ Latest Stable Version

在需要比“编译器辅助的复制粘贴”更多逻辑的情况下,例如使用PHP的特质,这个库在编译时提供了一个基于包含类的动态扩展的语法。

部分风味

此库提供的部分分为两种风味:种类和可组合的。

  • 种类定义了类型的一般概念。例如,可以说:此类型是某种列表,或对某种事物的引用。或者,使用更具体的例子:Customers类是客户列表(由Customer实例封装),而Name是对包含名称的(字符串)的引用。
  • 可组合的可以单独使用或组合使用以扩展基本类型或种类。例如,可以说,此类型具有某种功能。再次使用一个实际用例:Person类具有toString()compareTo()hashCode()方法。

无论风味如何,一些部分实际上是通过常规PHP特质实现的,而另一些则是在运行时动态创建的。然而,两种语法都是use [包含类型][is-or-with][部分名称]

教程

Box特质创建一个包装单个成员的值对象。它创建一个单参数构造函数,一个用于检索值的value(),并包括适当的hashCode()compareTo()toString()实现。

编写此内容

namespace example;

use lang\partial\Box;
use lang\Value;

class Name implements Value {
  use Name\is\Box;

  public function personal() { return '~' === $this->value{0}; }
}

...等同于

namespace example;

use lang\Value;

class Name implements Value {
  protected $value;

  public function __construct($value) { $this->value= $value; }

  public function value() { return $this->value; }

  public function personal() { return '~' === $this->value{0}; }

  public function hashCode() { /* ... */ }

  public function compareTo($value) { /* ... */ }

  public function toString() { /* ... */ }
}

参数化的Accessors特质为所有实例成员创建访问器。

编写此内容

namespace example;

use lang\partial\Accessors;

class Wall {
  use Wall\with\Accessors;

  private $name, $type, $posts;

  public function __construct(
    Name $name,
    Type $type,
    Posts $posts
  ) {
    $this->name= $name;
    $this->type= $type;
    $this->posts= $posts;
  }
}

...等同于

namespace example;

class Wall {
  private $name, $type, $posts;

  public function __construct(
    Name $name,
    Type $type,
    Posts $posts
  ) {
    $this->name= $name;
    $this->type= $type;
    $this->posts= $posts;
  }

  public function name() { return $this->name; }

  public function type() { return $this->type; }

  public function posts() { return $this->posts; }
}

如果构造函数仅由赋值组成,则可以包含Constructor特质并移除它。参数的声明顺序与字段声明顺序相同:从源代码的顶部到底部,从左到右。

编写此内容

namespace example;

use lang\partial\Constructor;

class Author {
  use Author\with\Constructor;

  private $handle, $name;
}

...等同于

namespace example;

class Author {
  private $handle, $name;

  public function __construct($handle, $name) {
    $this->handle= $handle;
    $this->name= $name;
  }
}

要组合所有这些,可以使用Value特质,该特质a)创建一个带有所有成员参数的构造函数,b)访问器用于读取这些,并c)实现hashCode()compareTo()toString()方法。

ListOf特质创建一个可以通过偏移量访问的元素列表,可以通过foreach迭代,并提供了equals()toString()默认实现。

编写此内容

namespace example;

use lang\partial\ListOf;

class Posts implements \lang\Value, \IteratorAggregate {
  use Posts\is\ListOf;
}

...等同于

namespace example;

class Posts implements \lang\Value, \IteratorAggregate {
  private $backing;

  public function __construct(...$elements) {
    $this->backing= $elements;
  }

  public function present() { return !empty($this->backing); }

  public function size() { return sizeof($this->backing); }

  public function at($offset) {
    if (isset($this->backing[$offset])) {
      return $this->backing[$offset];
    }
    throw new ElementNotFoundException(…);
  }

  public function first() {
    if (empty($this->backing)) {
      throw new ElementNotFoundException(…);
    }
    return $this->backing[0];
  }

  public function getIterator() {
    foreach ($this->backing as $element) {
      yield $element;
    }
  }

  public function compareTo($value) { /* ... */ }

  public function toString() { /* ... */ }

  public function hashCode() { /* ... */ }
}

Builder特质将向您的类添加一个静态的with()方法,生成一个流畅的接口来创建实例。这在有很多构造函数参数的情况下特别有用。

Comparators特质为每个成员添加静态的by[Member]方法,返回util.Comparator实例。这些实例可以使用then(例如Post::byDate()->then(Post::byAuthor()))或反转(例如Post::byDate()->reverse())组合。

namespace example;

use lang\partial\Accessors;
use lang\partial\Builder;
use lang\partial\Comparators;

class Post {
  use Wall\with\Accessors;
  use Wall\with\Builder;
  use Wall\with\Comparators;

  private $author, $text, $date;

  public function __construct($author, $text, Date $date) {
    $this->author= $author;
    $this->text= $text;
    $this->date= $date;
  }
}

ListIndexedBy特质创建一个可以通过名称查询的元素列表,也重载迭代,并以合理的方式创建equals()toString()。类需要实现抽象的index方法,并返回表示名称的字符串。

namespace example;

use lang\partial\ListIndexedBy;

class Walls implements \IteratorAggregate {
  use Walls\is\ListIndexedBy;

  protected function index($wall) { return $wall->name()->value(); }
}

将这些组合在一起,我们可以看到API

use util\data\Sequence;

$post= Post::with()->author('Timm')->text('Hello World!')->date(Date::now())->create();

$walls= new Walls(
  new Wall(new Name('one'), Type::$OPEN, new Posts()),
  new Wall(new Name('two'), Type::$CLOSED, new Posts($post))
);

$walls->present();        // TRUE, list is not empty
$walls->size();           // 2
$walls->provides('one');  // TRUE, wall named one found
$walls->provides('zero'); // FALSE, no such wall
$walls->first();          // Wall(name => Name("one"), type => OPEN)
$walls->named('two');     // Wall(name => Name("two"), type => CLOSED)
$walls->named('three');   // ***ElementNotFoundException

foreach ($walls as $wall) {
  Console::writeLine('== ', $wall->name()->value(), ' wall (', $wall->type(), ') ==');
  Sequence::of($wall->posts())->sorted(Post::byDate())->each(function($post) {
    Console::writeLine('Written by ', $post->author(), ' on ', $post->date());
    Console::writeLine($post->text());
    Console::writeLine();
  });
}

另请参阅