个人理解
定义:组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构(Copy的)。
理解:存在前提条件,只有满足以下两个条件,才建议使用组合模式(Composite Pattern)
-
一组相似的对象,说明对象的相似度需要在较高的情况下才可以使用
-
具有树形结构的特性,比如公司部门的管理,系统多级权限的管理等等。
主要解决:主要解决的是复杂元素内部处理,客户程序与元素的内部程序解耦的问题。
组成部分:组合模式一般由一个统一的类进行实现,外部进行调用。
案例分析
以公司部门为原型,设计一个统一管理的类。
由于公司内部存在不同的部门,各部门直接会存在子级的部门及成员,所以可做一个类,每个部门、成员都属于该类的一个对象,具体代码如下。
场景代码
为了保证例子更加的真实,这里使用mysql进行数据的存储,所以需要建立两张表,branch(部门表)、member(成员表),并增加若干条数据,表格式如下代码所示:
CREATE TABLE `branch` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL DEFAULT '' COMMENT '部门名称',
`parent_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '上级部门ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
INSERT INTO `branch` VALUES ('1', '总公司', '0');
INSERT INTO `branch` VALUES ('2', '华北区分公司', '1');
INSERT INTO `branch` VALUES ('3', '东北区分公司', '1');
INSERT INTO `branch` VALUES ('4', '西北区分公司', '1');
INSERT INTO `branch` VALUES ('5', '西南区分公司', '1');
INSERT INTO `branch` VALUES ('6', '北京分公司', '1');
INSERT INTO `branch` VALUES ('7', '华北区总裁办', '2');
INSERT INTO `branch` VALUES ('8', '华北区人事部', '2');
INSERT INTO `branch` VALUES ('9', '华北区财务部', '2');
CREATE TABLE `member` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`name` varchar(255) NOT NULL DEFAULT '' COMMENT '成员名称',
`sex` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '性别',
`age` tinyint(3) unsigned NOT NULL DEFAULT '25' COMMENT '年龄',
`type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '是否为领导',
`branch_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '所属部门',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
INSERT INTO `member` VALUES ('1', '刘一二(CEO)', '0', '25', '0', '1');
INSERT INTO `member` VALUES ('2', '赵三四(COO)', '0', '25', '0', '1');
INSERT INTO `member` VALUES ('3', '华北区负责人', '0', '25', '0', '2');
INSERT INTO `member` VALUES ('4', '东北区负责人', '0', '25', '0', '3');
组合模式代码(为了链接数据库方便,这里直接使用的是thinkphp中的DB方法):
<?php
namespace app\design\fade\bombin;
//成员、部门的统一对象
use think\Db;
use think\Exception;
class Branch
{
protected $id; //当前对象ID
protected $data; //当前对象的数据
protected $type; //当前对象的类型(0-部门 1-成员)
protected $branch_id; //当前部门ID
/**
* 初始化对象
* @author Renling
* @datetime 10:18 2019/2/19
*
* @access public
* @param $id int 对象ID
**/
public function __construct($id = null, $type = 0)
{
$this->type = $type;
if ($type==0) {
//如果ID为null的时候,获取最顶端的部门ID
$this->id = $this->branch_id = $id?:$this->getHighestBranchId();
} else {
if (empty($id)) {
throw new Exception('member id is not null');
}
$this->id = $id;
$this->branch_id = $this->getBranchId();
}
$this->data = $this->getData();
}
/**
* 获取最高级的部门ID
* @author Renling
* @datetime 10:22 2019/2/19
*
* @access protected
*
* @return integer
**/
protected function getHighestBranchId()
{
$data = Db::table('branch')->where([
'parent_id' => 0
])->find();
if (!empty($data)) {
return $data['id'];
}
return null;
}
/**
* 获取当前成员对象的所属部门ID
* @author Renling
* @datetime 11:43 2019/2/19
*
* @access protected
*
* @return string
**/
protected function getBranchId()
{
$id = $this->id;
$data = Db::table('member')->find($id);
return $data['branch_id'];
}
/**
* 获取当前对象的数据
* @author Renling
* @datetime 11:48 2019/2/19
*
* @access protected
*
* @return array
**/
protected function getData()
{
$id = $this->id;
if ($this->type==0) {
$data = $this->getBranchData(compact('id'));
} else {
$data = $this->getMemberData(compact('id'));
}
if (isset($data[0])) {
return $data[0];
}
return null;
}
/**
* 获取当前部门的子级部门的ID值
* @author Renling
* @datetime 12:42 2019/2/19
*
* @access protected
*
* @return array
**/
protected function getChildrenBranchIds()
{
$parent_id = $this->branch_id;
$branchIds = Db::table('branch')->field('id')->where(compact('parent_id'))->select();
return array_column($branchIds, 'id');
}
/**
* 获取成员的数据
* @author Renling
* @datetime 11:54 2019/2/19
*
* @access public
* @param $where array 条件
*
* @return array
**/
public function getMemberData($where)
{
return Db::table('member')->where($where)->select();
}
/**
* 获取部门的数据
* @author Renling
* @datetime 11:55 2019/2/19
*
* @access public
* @param $where array 条件
*
* @return array
**/
public function getBranchData($where)
{
return Db::table('branch')->where($where)->select();
}
/**
* 获取当前部门下的所有子级部门及部门成员
* @author Renling
* @datetime 12:38 2019/2/19
*
* @access public
*
* @return array
**/
public function getTree()
{
$branch_id = $id = $this->branch_id;
$data = $this->getBranchData(compact('id'));
$data = $data[0];
$data['member'] = $this->getMemberData(compact('branch_id'));
$children = $this->getChildrenBranchIds();
foreach ($children as $branch_id) {
$object = new Branch($branch_id);
$data['children'][] = $object->getTree();
}
return $data;
}
}
场景1,当某一个成员登陆时,可通过初始化函数进行加载,并通过getTree方法获取该成员所在的部门的子级部门树。
<?php
namespace app\design\controller;
use app\design\fade\bombin\Branch;
use think\Controller;
class Index extends Controller
{
public function index()
{
$branch = new Branch(2, 1);
halt($branch->getTree());
}
}
场景2,当需要调用某一个部门的数据及其子级部门树的时候,也同样可以通过初始化函数进行实例化一个对象。
<?php
namespace app\design\controller;
use app\design\fade\bombin\Branch;
use think\Controller;
class Index extends Controller
{
public function index()
{
$branch = new Branch(2);
halt($branch->getTree());
}
}
这里,把一组相似的对象(每一个部门为一个对象,每一个成员为一个对象)当作一个单一的对象,然后,把每个部门、成员通过树的形式组合在一起,这就是组合模式。
参考资料
《菜鸟教程——组合模式》http://www.runoob.com/design-pattern/composite-pattern.html
《大话设计模式》
下一篇
初始设计模式——迭代器模式