本文同步更新於blog
情境:原本的生物分类学(界门纲目科属种)
<?php
namespace App\CompositePattern\Taxonomy;
class Program
{
public function getTaxonomy()
{
echo '动物界
-- 脊索动物门
---- 哺乳纲
------ 双门齿目
-------- 无尾熊科
---------- 无尾熊属
------------ 无尾熊
------ 食肉目
-------- 熊科
---------- 大猫熊属
------------ 大猫熊
';
}
}
(注:排版是因为测试时不能有空格,也间接说明了这是个脆弱测试)
我们利用「-」来做出层级的分类概念。
经由分类发现,无尾熊与大猫熊同属动物界-脊索动物门-哺乳纲。
让我们透过组合模式,将其改写成树形架构!
需求一:运用组合模式
<?php
namespace App\CompositePattern\Taxonomy\Contracts;
interface Component
{
public function add(Component $component);
public function remove(Component $component);
public function displayClassifiaction(int $depth);
}
<?php
namespace App\CompositePattern\Taxonomy\Traits;
trait DashHelper
{
/**
* @param integer $count
* @return string
*/
public function getDashes(int $count)
{
$dash = '';
for ($i = 0; $i < $count; $i++) {
$dash = $dash . '-';
}
return $dash;
}
}
DashHelper目的是做出不同层的分类。
<?php
namespace App\CompositePattern\Taxonomy;
use App\CompositePattern\Taxonomy\Contracts\Component;
use App\CompositePattern\Taxonomy\Traits\DashHelper;
class Composite implements Component
{
use DashHelper;
/**
* @var string
*/
public $name;
/**
* @var Component[]
*/
protected $children = [];
/**
* @param string $name
*/
public function __construct(string $name)
{
$this->name = $name;
}
/**
* @param Component $component
* @return void
*/
public function add(Component $component)
{
$this->children[$component->name] = $component;
}
/**
* @param Component $component
* @return void
*/
public function remove(Component $component)
{
unset($this->children[$component->name]);
}
/**
* @param integer $depth
* @return void
*/
public function displayClassifiaction(int $depth)
{
$this->displaySelfClassification($depth);
$this->displayChildrenClassification($depth);
}
/**
* @param int $depth
* @return void
*/
private function displaySelfClassification(int $depth)
{
$dashes = $this->getDashes($depth);
if (strlen($dashes) == 0) {
echo "$this->name\n";
return;
}
echo "$dashes $this->name\n";
}
/**
* @param integer $depth
* @return void
*/
private function displayChildrenClassification(int $depth)
{
foreach ($this->children as $child) {
$child->displayClassifiaction($depth + 2);
}
}
}
枝节点会先印出自己的分类名称,接着加层数给子物件。
<?php
namespace App\CompositePattern\Taxonomy;
use App\CompositePattern\Taxonomy\Contracts\Component;
use App\CompositePattern\Taxonomy\Traits\DashHelper;
use Exception;
class Leaf implements Component
{
use DashHelper;
/**
* @var string
*/
public $name;
/**
* @param string $name
*/
public function __construct(string $name)
{
$this->name = $name;
}
/**
* @param Component $component
* @throws Exception
*/
public function add(Component $component)
{
throw new Exception('Cannot add to a leaf');
}
/**
* @param Component $component
* @throws Exception
*/
public function remove(Component $component)
{
throw new Exception('Cannot remove from a leaf');
}
/**
* @param integer $depth
* @return void
*/
public function displayClassifiaction(int $depth)
{
$dashes = $this->getDashes($depth);
echo "$dashes $this->name\n\n";
}
}
叶节点只会列出自己的分类名称,而且不允许对子物件的操作。
<?php
namespace App\CompositePattern\Taxonomy;
class Program
{
public function getTaxonomy()
{
$animalia = new Composite('动物界');
$chordata = new Composite('脊索动物门');
$mammalia = new Composite('哺乳纲');
$animalia->add($chordata);
$chordata->add($mammalia);
// koala
$diprotodontia = new Composite('双门齿目');
$phascolarctidae = new Composite('无尾熊科');
$phascolarctos = new Composite('无尾熊属');
$phascolarctosCinereus = new Leaf('无尾熊');
$diprotodontia->add($phascolarctidae);
$phascolarctidae->add($phascolarctos);
$phascolarctos->add($phascolarctosCinereus);
$mammalia->add($diprotodontia);
// panda
$carnivora = new Composite('食肉目');
$ursidae = new Composite('熊科');
$ailuropoda = new Composite('大猫熊属');
$ailuropodaMelanoleuca = new Leaf('大猫熊');
$carnivora->add($ursidae);
$ursidae->add($ailuropoda);
$ailuropoda->add($ailuropodaMelanoleuca);
$mammalia->add($carnivora);
$animalia->displayClassifiaction(0);
}
}
(注:此处的变数命名参考学名)
[单一职责原则]
透过找出可以继续递回的部分,分出枝节点与叶节点。
[开放封闭原则]
可以於组合中新增/修改某节点,不去影响其他节点的行为。
[依赖反转原则]
客户依赖於抽象的组合介面 (Component)。
枝节点与叶节点实现抽象的组合介面 (Component)。
最後附上类别图:
(注:若不熟悉 UML 类别图,可参考UML类别图说明。)
ʕ •ᴥ•ʔ:动物界-脊索动物门-哺乳纲-灵长目-人科-人属-人。
<<: Gulp npm install 中的 --save 与 --save-dev 差异 DAY94
Wireless Networking技术我们每天都在使用,但其便利性也存在许多安全问题。 基本上,...
在先前[Day 09] tinyML开胃菜Arduino IDE上桌(下)已经简单介绍过Arduin...
陈述式与表达式的差异 陈述式:不会回传结果,而是执行特定的程序码,如使用 if...else、swi...
前言 Hi, 我是鱼板伯爵今天要介绍Const,利用它来宣告一些重复使用且不变的值,教学内容只会撷取...
昨天我们介绍完如何建立一个元件,今天就来介绍 PropTypes,让建立的元件更加的完整吧! Pro...