将 对象 组合成 树形结构,表示 “部分-整体” 层次结构,组合模式 使 客户端 对 单个对象 和 组合对象 保持一致的 方式处理;
适用场景:
- 忽略差异:希望 客户端 可以忽略 组合对象 与 单个对象 的差异;
- 处理树形结构;
优点:
- 定义层次:清楚地 定义 分层次 的 复杂对象,表示 对象 的 全部 或 部分 层次;
- 忽略层次:让 客户端 忽略 层次之间的差异,方便对 整个层次结构 进行控制;
- 简化客户端代码;
- 符合开闭原则;
缺点:
- 限制类型复杂:限制类型时,比较复杂,如:某个目录中只能包含文本文件,使用组合模式时,不能依赖类型系统,施加约束,它们都来自于节点的抽象层; 在这种情况下,必须通过在运行时进行类型检查,这样就变得比较复杂;
- 使设计变得更加抽象;
类图
代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title></title>
<style>
.red {
color: red;
}
</style>
</head>
<body>
<div id="root"></div>
<script>
function ReactElement(type, props) {
this.type = type;
this.props = props;
}
let React = {
createElement(type, props = {}, ...childrens) {
childrens.length === 1 ? childrens = childrens[0]: void 0
return new ReactElement(type, { ...props, children: childrens })
}
};
let render = (eleObj, container) => {
// 先取出第一层 进行创建真实 dom
let { type, props } = eleObj;
let elementNode = document.createElement(type); // 创建第一个元素
for (let attr in props) { // 循环所有属性
if (attr === 'children') { // 如果是 children 表示有嵌套关系
if (typeof props[attr] == 'object') { // 看是否是只有一个文本节点
props[attr].forEach(item => { // 多个的话循环判断 如果是对象再次调用 render 方法
if (typeof item === 'object') {
render(item, elementNode);
} else { //是文本节点 直接创建即可
elementNode.appendChild(document.createTextNode(item));
}
})
} else { // 只有一个文本节点直接创建即可
elementNode.appendChild(document.createTextNode(props[attr]));
}
} else if (attr === 'className') { // 是不是 class 属性,class 属性特殊处理
elementNode.setAttribute('class', props[attr]);
} else {
elementNode.setAttribute(attr, props[attr]);
}
}
container.appendChild(elementNode)
};
//ReactDOM.render(<div>hello<span>world</span></div>);
//ReactDOM.render(React.createElement("div",null,"hello,",React.createElement("span",null,"world")));
render(React.createElement("div", null, "hello,", React.createElement("span", { class: "red" }, "world")), document.getElementById('root'));
</script>
</body>
</html>
应用场景
文件夹和文件
function Folder(name) {
this.name = name;
this.children = [];
this.parent = null;
this.level = 0;
}
Folder.prototype.add = function (child) {
child.level = this.level + 1;
child.parent = this;
this.children.push(child);
}
Folder.prototype.show = function () {
console.log(' '.repeat(this.level) + '文件夹' + this.name);
for (let i = 0; i < this.children.length; i++) {
this.children[i].show();
}
}
Folder.prototype.remove = function () {
if (!this.parent) return;
for (let i = 0; i < this.parent.children.length; i++) {
let current = this.parent.children[i];
if (current === this) {
return this.parent.children.splice(i, 1);
}
}
}
function File(name) {
this.name = name;
}
File.prototype.add = function () {
throw new Error(`文件下面不能再添加文件`);
}
File.prototype.show = function () {
console.log(' '.repeat(this.level) + '文件' + this.name);
}
let folder = new Folder('视频');
let vueFolder = new Folder('Vue视频');
let reactFolder = new Folder('React视频');
let vueFile = new File('Vue从入门到精通');
let reactFile = new File('React从入门到精通');
folder.add(vueFolder);
folder.add(reactFolder);
vueFolder.add(vueFile);
reactFolder.add(reactFile);
folder.show();
vueFolder.remove();
folder.show();
绘制表单
// 容器
class FormContainer {
constructor(type) {
this.element = document.createElement(type);
this.children = [];
}
add(child) {
this.children.push(child);
this.element.appendChild(child.element);
return this;
}
}
// 分组
class FieldContainer {
constructor(type) {
this.element = document.createElement(type);
this.children = [];
}
add(child) {
this.children.push(child);
this.element.appendChild(child.element);
return this;
}
}
class LabelItem {
constructor(title) {
this.element = document.createElement('label');
this.element.innerHTML = title;
}
}
class InputItem {
constructor(title) {
this.element = document.createElement('input');
this.element.name = title;
}
}
class TipItem {
constructor(title) {
this.element = document.createElement('span');
this.element.innerHTML = title;
}
}
let userform = new FormContainer('form')
.add(
new FieldContainer('p')
.add(new LabelItem('用户名'))
.add(new InputItem('username'))
.add(new TipItem('用户名长度为6-8位'))
)
.add(
new FieldContainer('p')
.add(new LabelItem('密码'))
.add(new InputItem('password'))
.add(new TipItem('确认密码'))
);
document.body.appendChild(userform.element);
桥接模式
上一篇