xp-forge / partial
部分类型
Requires
- php: >=7.0.0
- xp-forge/mirrors: ^6.0 | ^5.0 | ^4.0 | ^3.0
- xp-framework/core: ^10.0 | ^9.0 | ^8.0 | ^7.0
Requires (Dev)
- xp-framework/unittest: ^11.0 | ^10.0 | ^9.0 | ^8.0 | ^7.0
README
在需要比“编译器辅助的复制粘贴”更多逻辑的情况下,例如使用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(); }); }