一、什么是行为型模式?

    1. 1、设计模式:
    2.   是一套被反复使用、多数人知晓的、经过分类编目的、代码设计的总结。就好像杯子,是被前人设计出来的,实现了储存水的功能,解决了人们的喝水问题。大多数人喝水都用杯子。但是你不必自己再重做另外方法再做一种容器,而实现的也是同样的功能,只要会用别人做出来的杯子喝水就能达到目的。但是杯子有很多中,实现喝水的方式也不同,比如茶水杯子,咖啡杯,啤酒杯子等等,要选择适合自己的杯子,就如茶水要用带过滤网的杯子,如果用不带过滤网的会喝一嘴茶叶。合适的才是最好的。
    3. 总结出来设计模式的特性如下:
    4. a.普遍性:经过前辈们的使用,大多人都实用的,总结提炼不断的提升被普遍认为是实现某种事物的最有效的方法。
    5. b.封装性:既然是方法那我们就不用太关心细节是怎么实现的,而主要是学习怎么用这些方法模式,从而达到自己的目的。
    6. c.面向对象性:指挥对象做事。把复杂问题简单化。
    7. d.最优性:要充分相信适合的就是最好的。
    8. 2、面向对象:
    9.   或许你对面向对象还有疑问?指挥对象做事,把复杂问题简单化。那么我们就来举个例子说明。比如我们去饭店吃饭,会叫服务员然后点菜,那我们就是指挥服务员做事,至于服务员怎么让厨师做,厨师怎么做,这些我们都不管。我们只管是否能吃到我们叫到的菜。这个就是把复杂事情简单化了。我们不用自己做菜,也不用知道菜怎么做,我们指挥服务员就行了,吃饭这件事就由复杂的做菜之类的变成吃这个简单的事了。
    10. 3、行为型模式:
    11. 就是描述类和对象之间的通信和职责的。简而言之,就是类和对象扮演什么角色,还有怎么扮演这个角色的问题。

    二、行为型模式的种类

    1. 大体上分为三个大类:常见模式、已知模式、深度模式
    2. 常见模式包括: 模版方法模式命令模式 迭代器模式 观察者模式 中介者模式 状态式 职责链模式 策略模式
    3. 已知模式包括:备忘录模式
    4. 深度模式包括:解释器模式 访问者模式
    5. 下面来介绍常见模式
    6. Ø 常见模式
    7. 1、模版方法模式(Template):
    8.   定义一个操作中的算法骨架,而将一些实现步骤延迟到子类当中实现。就像一个豆浆机,不管放进去的是红豆还是黑豆,出来的都是豆浆。
    9.   好处:扩展性好,封装不变的代码,扩展可变的代码
    10.   弊端:灵活性差,不能改变骨架部分。
    11.   应用场景:一类或一组具有共性的事物中
    12. 代码实现
    13. <?php
    14. /**
    15. * 优才网公开课示例代码
    16. *
    17. * 模板方法模式 Template
    18. *
    19. * @author 优才网全栈工程师教研组
    20. * @seehttp://www.ucai.cn
    21. */
    22. function output($string) {
    23. echo $string . "\n";
    24. }
    25. class Request {
    26. public$token = '';
    27. publicfunction __construct() {
    28. $this->token ='0c6b7289f5334ed2b697dd461eaf9812';
    29. }
    30. }
    31. class Response {
    32. publicfunction render($content) {
    33. output(sprintf('response-render: %s',$content));
    34. }
    35. publicfunction redirect($uri) {
    36. output(sprintf('response-redirect: %s', $uri));
    37. }
    38. publicfunction json($data) {
    39. output(sprintf('response-data: %s', json_encode($data)));
    40. }
    41. }
    42. //父类,抽象类
    43. abstract class Controller{
    44. //封装了输入输出
    45. protected$request;
    46. protected$response;
    47. //返回数据
    48. protected$data = 'data';
    49. publicfunction __construct($request, $response){
    50. $this->request = $request;
    51. $this->response = $response;
    52. }
    53. //执行请求函数,定义总体算法(template method),final防止被复写(不允许子类改变总体算法)
    54. publicfinal function execute(){
    55. $this->before();
    56. if($this->valid()){
    57. $this->handleRequest();
    58. }
    59. $this->after();
    60. }
    61. //定义hook methodbefore,做一些具体请求的前置处理
    62. //非abstract方法,子类可以选择覆盖或不覆盖,默认什么都不做
    63. protectedfunction before(){
    64. }
    65. //定义hook methodvalid,做请求的数据验证
    66. //非abstract方法,子类可以选择覆盖或不覆盖,默认返回验证通过
    67. protectedfunction valid(){
    68. returntrue;
    69. }
    70. //定义hook methodhandleRequest,处理请求
    71. //定义为abstract方法,子类必须实现或也声明为抽象方法(由子类的子类负责实现)
    72. abstractfunction handleRequest();
    73. //定义hook methodafter,做一些请求的后置处理
    74. //非abstract方法,子类可以选择覆盖或不覆盖,默认直接输出数据
    75. protectedfunction after(){
    76. $this->response->render($this->data);
    77. }
    78. }
    79. //子类1,实现父类开放的具体算法
    80. class User extends Controller{
    81. //覆盖before方法,实现具体算法,这是一个处理用户数据操作的控制器
    82. //因此,我们选择在before里面判断用户是否已经登录了,这里简单判断下session数据
    83. functionbefore(){
    84. if(empty($_SESSION['auth'])){
    85. //没登录就直接跳转了,不再执行后续的操作
    86. $this->response->redirect("user/login.php");
    87. }
    88. }
    89. //覆盖valid方法,这里我们验证用户提交数据中有没有带验证token
    90. functionvalid(){
    91. if(isset($this->request->token)){
    92. return true;
    93. }
    94. returnfalse;
    95. }
    96. //覆盖handleRequest方法,必选,以为父类中声明了abstract了
    97. functionhandleRequest(){
    98. //做具体处理,一般根据参数执行不同的业务逻辑
    99. }
    100. //这个类我们选择不覆盖after方法,使用默认处理方式
    101. }
    102. //子类2,实现父类开放的具体算法
    103. class Post extends Controller{
    104. //这个类我们选择不覆盖before方法,使用默认处理方式
    105. //这个类我们选择不覆盖valid方法,使用默认处理方式
    106. //覆盖handleRequest方法,必选,以为父类中声明了abstract了
    107. functionhandleRequest(){
    108. //做具体处理,一般根据参数执行不同的业务逻辑
    109. $this->data = array('title' => 'ucai');
    110. }
    111. //覆盖after方法,使用json格式输出数据
    112. functionafter(){
    113. $this->response->json($this->data);
    114. }
    115. }
    116. class Client {
    117. publicstatic function test(){
    118. $request = new Request();
    119. $response = new Response();
    120. //最终调用
    121. $user= new User($request, $response);
    122. $user->execute();
    123. //最终调用
    124. $post= new Post($request, $response);
    125. $post->execute();
    126. }
    127. }
    128. Client::test();
    129. 2、命令模式(Command)
    130.   行为请求者与行为实现者解耦。就像军队里的“敬礼”,不管是谁听到这个命令都会做出标准的敬礼动作。
    131.   好处:便于添加和修改行为,便于聚合多个命令
    132.   弊端:造成过多具体的命令类
    133.   应用场景:对要操作的对象,进行的相同操作
    134. 代码实现
    135. <?php
    136. /**
    137. * 优才网公开课示例代码
    138. *
    139. * 命令模式 Command
    140. *
    141. * @author 优才网全栈工程师教研组
    142. * @seehttp://www.ucai.cn
    143. */
    144. function output($string) {
    145. echo $string . "\n";
    146. }
    147. class Document {
    148. private$name = '';
    149. public function __construct($name) {
    150. $this->name = $name;
    151. }
    152. publicfunction showText() {
    153. output(sprintf("showText: %s", $this->name));
    154. }
    155. publicfunction undo() {
    156. output(sprintf("undo-showText: %s", $this->name));
    157. }
    158. }
    159. class Graphics {
    160. private$name = '';
    161. publicfunction __construct($name) {
    162. $this->name = $name;
    163. }
    164. publicfunction drawCircle() {
    165. output(sprintf("drawCircle: %s", $this->name));
    166. }
    167. publicfunction undo() {
    168. output(sprintf("undo-drawCircle: %s", $this->name));
    169. }
    170. }
    171. class Client {
    172. publicstatic function test() {
    173. $document = newDocument('A');
    174. $graphics = newGraphics('B');
    175. $document->showText();
    176. $graphics->drawCircle();
    177. $document->undo();
    178. }
    179. }
    180. Client::test();
    181. <?php
    182. /**
    183. * 优才网公开课示例代码
    184. *
    185. * 命令模式 Command
    186. *
    187. * @author 优才网全栈工程师教研组
    188. * @seehttp://www.ucai.cn
    189. */
    190. function output($string) {
    191. echo $string . "\n";
    192. }
    193. interface Command {
    194. publicfunction execute();
    195. publicfunction undo();
    196. }
    197. class Document implements Command {
    198. private$name = '';
    199. publicfunction __construct($name) {
    200. $this->name = $name;
    201. }
    202. publicfunction execute() {
    203. output(sprintf("showText: %s", $this->name));
    204. }
    205. publicfunction undo() {
    206. output(sprintf("undo-showText: %s", $this->name));
    207. }
    208. }
    209. class Graphics implements Command {
    210. private$name = '';
    211. publicfunction __construct($name) {
    212. $this->name = $name;
    213. }
    214. publicfunction execute() {
    215. output(sprintf("drawCircle: %s", $this->name));
    216. }
    217. publicfunction undo() {
    218. output(sprintf("undo-drawCircle: %s", $this->name));
    219. }
    220. }
    221. class Client {
    222. publicstatic function test() {
    223. $array = array();
    224. array_push($array, new Document('A'));
    225. array_push($array, new Document('B'));
    226. array_push($array, new Graphics('C'));
    227. array_push($array, new Graphics('D'));
    228. foreach ($array as $command) {
    229. $command->execute();
    230. }
    231. $top = array_pop($array);
    232. $top->undo();
    233. }
    234. }
    235. Client::test();
    236. <?php
    237. /**
    238. * 优才网公开课示例代码
    239. *
    240. * 命令模式 Command
    241. *
    242. * @author 优才网全栈工程师教研组
    243. * @seehttp://www.ucai.cn
    244. */
    245. function output($string) {
    246. echo $string . "\n";
    247. }
    248. interface Command {
    249. publicfunction execute();
    250. publicfunction undo();
    251. }
    252. class Document {
    253. private$name = '';
    254. publicfunction __construct($name) {
    255. $this->name = $name;
    256. }
    257. publicfunction showText() {
    258. output(sprintf("showText: %s", $this->name));
    259. }
    260. publicfunction undo() {
    261. output(sprintf("undo-showText: %s", $this->name));
    262. }
    263. }
    264. class Graphics {
    265. private$name = '';
    266. publicfunction __construct($name) {
    267. $this->name = $name;
    268. }
    269. publicfunction drawCircle() {
    270. output(sprintf("drawCircle: %s", $this->name));
    271. }
    272. publicfunction undo() {
    273. output(sprintf("undo-drawCircle: %s", $this->name));
    274. }
    275. }
    276. class DocumentCommand implements Command {
    277. private$obj = '';
    278. publicfunction __construct(Document $document) {
    279. $this->obj = $document;
    280. }
    281. publicfunction execute() {
    282. $this->obj->showText();
    283. }
    284. publicfunction undo() {
    285. $this->obj->undo();
    286. }
    287. }
    288. class GraphicsCommand implements Command {
    289. private$obj = '';
    290. publicfunction __construct(Graphics $graphics) {
    291. $this->obj = $graphics;
    292. }
    293. publicfunction execute() {
    294. $this->obj->drawCircle();
    295. }
    296. publicfunction undo() {
    297. $this->obj->undo();
    298. }
    299. }
    300. class Client {
    301. publicstatic function test() {
    302. $array = array();
    303. array_push($array, new DocumentCommand(new Document('A')));
    304. array_push($array, new DocumentCommand(new Document('B')));
    305. array_push($array, new GraphicsCommand(newGraphics('C')));
    306. array_push($array, new GraphicsCommand(new Graphics('D')));
    307. foreach ($array as $command) {
    308. $command->execute();
    309. }
    310. $top = array_pop($array);
    311. $top->undo();
    312. }
    313. }
    314. Client::test();
    315. 3、迭代器模式(Iterator):
    316.   访问聚合对象内容而不暴露内部结构。就像一个双色球彩票开奖一样,每次都是摇出七个球,不能能摇不是七个球的中奖号码组合。
    317.   好处:以不同方式遍历一个集合
    318.   弊端:每次遍历都是整个集合,不能单独取出元素
    319.   应用场景:需要操作集合里的全部元素。
    320. 代码实现:
    321. <?php
    322. /**
    323. * 优才网公开课示例代码
    324. *
    325. * 迭代器模式 Iterator
    326. *
    327. * @author 优才网全栈工程师教研组
    328. * @seehttp://www.ucai.cn
    329. */
    330. function output($string) {
    331. echo $string . "\n";
    332. }
    333. class RecordIterator implements Iterator{
    334. private$position = 0;
    335. //注意:被迭代对象属性是私有的
    336. private$records = array();
    337. publicfunction __construct(Array $records) {
    338. $this->position = 0;
    339. $this->records = $records;
    340. }
    341. functionrewind() {
    342. $this->position = 0;
    343. }
    344. functioncurrent() {
    345. return$this->records[$this->position];
    346. }
    347. functionkey() {
    348. return$this->position;
    349. }
    350. functionnext() {
    351. ++$this->position;
    352. }
    353. functionvalid() {
    354. returnisset($this->records[$this->position]);
    355. }
    356. }
    357. class PostListPager {
    358. protected$record = array();
    359. protected$total = 0;
    360. protected$page = 0;
    361. protected$size = 0;
    362. publicfunction __construct($category, $page, $size) {
    363. $this->page = $page;
    364. $this->size = $size;
    365. //query db
    366. $total = 28;
    367. $this->total = $total;
    368. $record = array(
    369. 0 => array('id'=> '1'),
    370. 1 => array('id'=> '2'),
    371. 2 => array('id'=> '3'),
    372. 3 => array('id'=> '4'),
    373. );
    374. //
    375. $this->record = $record;
    376. }
    377. publicfunction getIterator() {
    378. return newRecordIterator($this->record);
    379. }
    380. publicfunction getMaxPage() {
    381. $max = intval($this->total /$this->size);
    382. return $max;
    383. }
    384. publicfunction getPrevPage() {
    385. return max($this->page - 1,1);
    386. }
    387. publicfunction getNextPage() {
    388. return min($this->page + 1,$this->getMaxPage());
    389. }
    390. }
    391. class Client {
    392. publicstatic function test(){
    393. $pager = new PostListPager(1,2, 4);
    394. foreach ($pager->getIterator() as $key => $val) {
    395. output(sprintf('Key[%d],Val[%s]', $key, json_encode($val)));
    396. }
    397. output(sprintf('MaxPage[%d]', $pager->getMaxPage()));
    398. output(sprintf('Prev[%d]', $pager->getPrevPage()));
    399. output(sprintf('Next[%d]', $pager->getNextPage()));
    400. $iterator =$pager->getIterator();
    401. while($iterator->valid()){
    402. print_r($iterator->current());
    403. $iterator->next();
    404. }
    405. $iterator->rewind();
    406. }
    407. }
    408. Client::test();
    409. 4、观察者模式(Observer):
    410.   又叫发布订阅模式,当一个主体对象发生改变时,依赖它的多个观察者对象都得到通知并自动更新响应。就像报社一样,今天发布的消息只要是看这份报纸的人看到的都是同样的内容。如果发布另一份报纸,也是一样的。
    411.   好处:广播式通信,范围大,一呼百应,便于操作一个组团,“公有制”
    412.   弊端:不能单独操作组团里的个体,不能实行按需分配
    413.   应用场景:操作多个对象,并操作相同。
    414. 代码实现:
    415. <?php
    416. /**
    417. * 优才网公开课示例代码
    418. *
    419. * 观察者模式 Observer
    420. *
    421. * @author 优才网全栈工程师教研组
    422. * @seehttp://www.ucai.cn
    423. */
    424. function output($string) {
    425. echo $string . "\n";
    426. }
    427. //订单数据对象简单模拟,这个是实际需要被观察的对象(Subject),但是我们将其独立,然后
    428. //通过构造方法传入到我们模式中的Subject中,这样使具体业务更加独立
    429. class Order{
    430. //订单号
    431. private$id = '';
    432. //用户ID
    433. private$userId = '';
    434. //用户名
    435. private$userName = '';
    436. //价格
    437. private$price = '';
    438. //下单时间
    439. private$orderTime = '';
    440. //订单数据填充简单模拟,实际应用中可能会读取用户表单输入并处理
    441. publicfunction __set($name, $value){
    442. if(isset($this->$name)){
    443. $this->$name = $value;
    444. }
    445. }
    446. //获取订单属性
    447. public function__get($name){
    448. if(isset($this->$name)){
    449. return $this->$name;
    450. }
    451. return"";
    452. }
    453. }
    454. //假设的DB类,便于测试,实际会存入真实数据库
    455. class FakeDB{
    456. publicfunction save($data){
    457. return true;
    458. }
    459. }
    460. class Client {
    461. publicstatic function test() {
    462. //初始化一个订单数据
    463. $order= new Order();
    464. $order->id = 1001;
    465. $order->userId = 9527;
    466. $order->userName = "God";
    467. $order->price = 20.0;
    468. $order->orderTime = time();
    469. //向数据库保存订单
    470. $db =new FakeDB();
    471. $result = $db->save($order);
    472. if($result){
    473. //实际应用可能会写到日志文件中,这里直接输出
    474. output( "[OrderId:{$order->id}] [UseId:{$order->userId}][Price:{$order->price}]" );
    475. //实际应用会调用邮件发送服务如sendmail,这里直接输出
    476. output( "Dear {$order->userName}: Your order {$order->id} wasconfirmed!" );
    477. //实际应用会调用邮件发送服务如sendmail,这里直接输出
    478. output( "Dear Manager: User{$order->userName}(ID:{$order->userId}) submitted a new order {$order->id},please handle it ASAP!" );
    479. }
    480. }
    481. }
    482. Client::test();
    483. <?php
    484. /**
    485. * 优才网公开课示例代码
    486. *
    487. * 观察者模式 Observer
    488. *
    489. * @author 优才网全栈工程师教研组
    490. * @seehttp://www.ucai.cn
    491. */
    492. function output($string) {
    493. echo $string . "\n";
    494. }
    495. //订单数据对象简单模拟,这个是实际需要被观察的对象(Subject),但是我们将其独立,然后
    496. //通过构造方法传入到我们模式中的Subject中,这样使具体业务更加独立
    497. class Order{
    498. //订单号
    499. private$id = '';
    500. //用户ID
    501. private$userId = '';
    502. //用户名
    503. private$userName = '';
    504. //价格
    505. private$price = '';
    506. //下单时间
    507. private$orderTime = '';
    508. //订单数据填充简单模拟,实际应用中可能会读取用户表单输入并处理
    509. publicfunction __set($name, $value){
    510. if(isset($this->$name)){
    511. $this->$name = $value;
    512. }
    513. }
    514. //获取订单属性
    515. publicfunction __get($name){
    516. if(isset($this->$name)){
    517. return $this->$name;
    518. }
    519. return "";
    520. }
    521. }
    522. //被观察者, 负责维护观察者并在变化发生是通知观察者
    523. class OrderSubject implements SplSubject {
    524. private$observers;
    525. private$order;
    526. publicfunction __construct(Order $order) {
    527. $this->observers = new SplObjectStorage();
    528. $this->order = $order;
    529. }
    530. //增加一个观察者
    531. publicfunction attach(SplObserver $observer) {
    532. $this->observers->attach($observer);
    533. }
    534. //移除一个观察者
    535. publicfunction detach(SplObserver $observer) {
    536. $this->observers->detach($observer);
    537. }
    538. //通知所有观察者
    539. publicfunction notify() {
    540. foreach ($this->observers as $observer) {
    541. $observer->update($this);
    542. }
    543. }
    544. //返回主体对象的具体实现,供观察者调用
    545. publicfunction getOrder() {
    546. return$this->order;
    547. }
    548. }
    549. //记录业务数据日志 (ActionLogObserver),实际可能还要抽象一层以处理不同的Action(业务操作),这里省略
    550. class ActionLogObserver implements SplObserver{
    551. publicfunction update(SplSubject $subject) {
    552. $order = $subject->getOrder();
    553. //实际应用可能会写到日志文件中,这里直接输出
    554. output( "[OrderId:{$order->id}] [UseId:{$order->userId}][Price:{$order->price}]" );
    555. }
    556. }
    557. //给用户发送订单确认邮件 (UserMailObserver)
    558. class UserMailObserver implements SplObserver{
    559. publicfunction update(SplSubject $subject) {
    560. $order = $subject->getOrder();
    561. //实际应用会调用邮件发送服务如sendmail,这里直接输出
    562. output( "Dear {$order->userName}: Your order {$order->id} wasconfirmed!" );
    563. }
    564. }
    565. //给管理人员发订单处理通知邮件 (AdminMailObserver)
    566. class AdminMailObserver implements SplObserver{
    567. publicfunction update(SplSubject $subject) {
    568. $order = $subject->getOrder();
    569. //实际应用会调用邮件发送服务如sendmail,这里直接输出
    570. output( "Dear Manager: User{$order->userName}(ID:{$order->userId}) submitted a new order{$order->id}, please handle it ASAP!" );
    571. }
    572. }
    573. //假设的DB类,便于测试,实际会存入真实数据库
    574. class FakeDB{
    575. publicfunction save($data){
    576. returntrue;
    577. }
    578. }
    579. class Client {
    580. publicstatic function test() {
    581. //初始化一个订单数据
    582. $order= new Order();
    583. $order->id = 1001;
    584. $order->userId = 9527;
    585. $order->userName = "God";
    586. $order->price = 20.0;
    587. $order->orderTime = time();
    588. //绑定观察者
    589. $subject = new OrderSubject($order);
    590. $actionLogObserver = new ActionLogObserver();
    591. $userMailObserver = newUserMailObserver();
    592. $adminMailObserver = new AdminMailObserver();
    593. $subject->attach($actionLogObserver);
    594. $subject->attach($userMailObserver);
    595. $subject->attach($adminMailObserver);
    596. //向数据库保存订单
    597. $db =new FakeDB();
    598. $result = $db->save($order);
    599. if($result){
    600. //通知观察者
    601. $subject->notify();
    602. }
    603. }
    604. }
    605. Client::test();
    606. 5、中介者模式(Mediator):
    607.   用中介对象封装一系列的对象交互,中介使各对象不需要显式地相互引用。类似于邮局,邮寄者和收件者不用自己跑很远路,通过邮局就可以。
    608.   好处:简化了对象之间的关系,减少子类的生成
    609.   弊端:中介对象可能变得非常复杂,系统难以维护
    610.   应用场景:不需要显示地建立交互
    611. 代码实现:
    612. <?php
    613. /**
    614. * 优才网公开课示例代码
    615. *
    616. * 中介者模式 Mediator
    617. *
    618. * @author 优才网全栈工程师教研组
    619. * @seehttp://www.ucai.cn
    620. */
    621. function output($string) {
    622. echo $string . "\n";
    623. }
    624. abstract class Mediator { // 中介者角色
    625. abstractpublic function send($message,$colleague);
    626. }
    627. abstract class Colleague { // 抽象对象
    628. private$_mediator = null;
    629. publicfunction __construct($mediator) {
    630. $this->_mediator = $mediator;
    631. }
    632. publicfunction send($message) {
    633. $this->_mediator->send($message,$this);
    634. }
    635. abstractpublic function notify($message);
    636. }
    637. class ConcreteMediator extends Mediator { // 具体中介者角色
    638. private$_colleague1 = null;
    639. private$_colleague2 = null;
    640. publicfunction send($message,$colleague) {
    641. if($colleague == $this->_colleague1) {
    642. $this->_colleague1->notify($message);
    643. } else{
    644. $this->_colleague2->notify($message);
    645. }
    646. }
    647. publicfunction set($colleague1,$colleague2) {
    648. $this->_colleague1 = $colleague1;
    649. $this->_colleague2 = $colleague2;
    650. }
    651. }
    652. class Colleague1 extends Colleague { // 具体对象角色
    653. publicfunction notify($message) {
    654. output(sprintf('Colleague-1: %s', $message));
    655. }
    656. }
    657. class Colleague2 extends Colleague { // 具体对象角色
    658. publicfunction notify($message) {
    659. output(sprintf('Colleague-2: %s', $message));
    660. }
    661. }
    662. class Client {
    663. publicstatic function test(){
    664. //client
    665. $objMediator = new ConcreteMediator();
    666. $objC1= new Colleague1($objMediator);
    667. $objC2= new Colleague2($objMediator);
    668. $objMediator->set($objC1,$objC2);
    669. $objC1->send("to c2 from c1");
    670. $objC2->send("to c1 from c2");
    671. }
    672. }
    673. Client::test();
    674. 转自:https://mp.weixin.qq.com/s/doz_UlXuXWmSk5Q-rVWmdg