复合
想象一下,一个音频系统由单个歌曲和歌曲播放列表组成。是的,播放列表由歌曲组成,但我们希望两者都能单独处理。两者都是音乐类型,都可以播放。
复合设计模式在这方面可以提供帮助;它允许我们忽略对象组合与单个对象之间的差异。它允许我们使用相同或几乎相同的代码来处理两者。
让我们举个小例子:歌曲是我们的 leaf
,播放列表是合成物。Music
是我们对播放列表和歌曲的抽象;因此,我们可以称其为我们的组件。所有这一切的客户端就是我们的 index.php
文件。
不区分叶节点和分支,我们的代码就不会那么复杂,也就不容易出错。
让我们先为 Music
定义一个接口:
Unresolved include directive in modules/ROOT/pages/ch04/ch4-05.adoc - include::example$/Chapter 4/Composite/Music.php[]
现在让我们从 Song
类开始整理一些实现:
Unresolved include directive in modules/ROOT/pages/ch04/ch4-05.adoc - include::example$/Chapter 4/Composite/Song.php[]
现在,我们可以开始组建 Playlist
类了。在这个示例中,你可能会注意到我使用了一个名为 spl_object_hash
的函数来设置歌曲数组中的键。在处理对象数组时,这个函数绝对是个好帮手。
这个函数的作用是为每个对象返回一个唯一的哈希值,只要对象不被销毁,无论类的哪些属性发生了变化,这个哈希值都会保持一致。它为任意对象提供了一种稳定的寻址方式。一旦对象被销毁,哈希值就可以重新用于其他对象。
该函数不会对对象的内容进行散列;它只是显示内部句柄和句柄表指针。这意味着如果更改对象的属性,哈希值也不会改变。尽管如此,它并不能保证唯一性。如果一个对象被销毁,随后又立即创建了一个相同类的对象,那么就会得到相同的哈希值,因为在第一个类被取消引用和销毁后,PHP 将重复使用相同的内部句柄。
这将是真实的,因为 PHP 可以使用内部句柄:
var_dump(spl_object_hash(new stdClass()) === spl_object_hash(new stdClass()));
然而,这将是错误的,因为 PHP 必须创建一个新的处理程序:
$object = new StdClass();
var_dump(spl_object_hash($object) === spl_object_hash(new stdClass()));
现在让我们回到 Playlist
类。让我们用它来实现我们的 Music 接口:
Unresolved include directive in modules/ROOT/pages/ch04/ch4-05.adoc - include::example$/Chapter 4/Composite/Playlist.php[]
现在,让我们在 index.php
文件中将这一切整合在一起。我们现在要做的是创建一些歌曲对象,其中一些将使用 addSong
函数分配给播放列表。
由于播放列表的实现方式与歌曲相同,我们甚至可以在其他播放列表中使用 addSong
函数(在这种情况下,我们最好将 addSong
函数重命名为 addMusic
)。
我们先播放父播放列表,然后播放子播放列表,进而播放这些播放列表中的所有歌曲:
Unresolved include directive in modules/ROOT/pages/ch04/ch4-05.adoc - include::example$/Chapter 4/Composite/index.php[]
当我们运行这个脚本时,我们可以看到预期的输出:
Playing song #57106d5adb364, Lost In Stereo.
Playing song #57106d5adb63a, Running From Lions.
Playing song #57106d5adb654, Guts.