木灵鱼儿
阅读:140
创建型模式:建造者模式
简介
建造者模式指将一个复杂对象的构造过程与表示分离,使同样的构建过程可以创建不同的表示。
它将变与不变相分离,即产品的整体组成不变,但是每一个部分是可以灵活变动的。
“表示”指的就是实际生成的实例对象,相同的构造过程可以创建不同的表示,这里我们需要先了解一下构造者模式中的3个角色。
角色:
- 指挥者:调用具体建造者来创建复杂对象的各个部分,它只处理创建的过程
- 建造者:提供各个部件的具体创建方法,在完成后提供产品的实例。
- 产品:要创建的复杂对象
指挥者指挥建造者去建造产品,同一个指挥者指挥不同的建造者,可以产生不同的产品,而指挥者也是可以替换的,不同的指挥者指挥同一个建造者,也可以产生不同的产品。
但是最终这个产品从大类的角度去看,都是属于同一种。
举个实物例子:
C组装电脑,A客户要求用三星的内存条,B客户要求镁光内存条,最终组装出来的都是电脑,但是却又有不同。
客户的要求可以抽象成建造者的具体实现,C则是指挥者同时兼建造者。
而且建造者模式它可以不存在单独的指挥者(A它自己组装,它自己不就是指挥者和建造者了),具体在后面有一个例子。
面向接口思维
为了能让指挥者与建造者之间能够灵活替换而不会产生使用上的问题,那必然会需要面向接口编程,两方都依赖于抽象接口,不依赖具体实现。
代码实现
我们搞一个通过建造者模式生成dom元素的例子!
我们的产品:自定义的dom元素,它只是自定class,tag,id,text文本内容,通过调用show方法,他将会插入到body元素中。
产品的这些set方法都是提供的接口,用于给建造者建造不同的东西,这个就好比电脑的插槽,有内存插槽,有cpu插槽,建造者通过插槽传入不同的东西进来。
class MyDom {
class: Array<string> = []; //class类名
id: string = ""; //id
tag: string = ""; //标签名
text: string = ""; //文本
constructor() {}
setClass(classArr: string[]): void {
this.class = classArr;
}
setId(id: string): void {
this.id = id;
}
setTag(tag: string) {
this.tag = tag;
}
setText(text: string) {
this.text = text;
}
show() {
//创建标签
const dom = document.createElement(this.tag);
//传入id
dom.setAttribute("id", this.id);
//设置class
dom.classList.add(...this.class);
//传入文本
dom.innerText = this.text;
//传入body
document.body.appendChild(dom);
}
}
产品有了,我们开始准备建造者:
先声明一个接口,接口有设置产品的方法,因为最终要提供一个获取产品的方法,这个方法没必要让子类去实现,所以直接写在父类了。
为了构建的时候省事,所以我要求每个方法最终返回this,已达到链式调用的效果。
abstract class Builders {
protected dom: MyDom = new MyDom();
public abstract setTag(): this;
public abstract setText(): this;
public abstract setClass(): this;
public abstract setId(): this;
public getDom(): MyDom {
return this.dom;
}
}
具体实现:
//创建一个div,并且设置id和class,已经内容
class CreateDiv extends Builders {
setTag() {
this.dom.setTag("div");
return this;
}
setText() {
this.dom.setText("我是一个通过建造者模式生成的div");
return this;
}
setId() {
this.dom.setId("box");
return this;
}
setClass() {
this.dom.setClass(["container", "font-weight-bold"]);
return this;
}
}
建造者有了,我们现在搞定指挥者:
先声明一个指挥者的抽象,用于给客户端去依赖。
abstract class Commander {
abstract builder: Builders;
public setBuilder(builder: Builders) {
this.builder = builder;
}
public abstract create(): MyDom;
}
指挥者抽象存在一个builder
的依赖,用于存放建造者对象,通过setBuilder
方法。
create
交给具体子类去实现。
class MyDivCommander extends Commander {
builder!: Builders;
create() {
//构建过程
const builder = this.builder.setTag().setId().setClass().setText();
return builder.getDom();
}
}
客户端去实际使用:
function create() {
const createDiv = new CreateDiv();
const myDivCommander:Commander = new MyDivCommander();
//传入建造者
myDivCommander.setBuilder(createDiv);
//调用指挥者获取dom
const dom = myDivCommander.create();
//使用dom
dom.show();
}
在java中可能会用一个类去实现,而不是函数,那么它对于抽象Commander
的要求可能会更明确一些。
运行create
我们可以创建出一个自定义的div元素。
不需要单独的指挥者
上述代码中,其实指挥者的存在仿佛有点多余,有时候我们没必要一定要去实现一个指挥者,根据业务需求可能直接通过客户去调用建造者,客户自己去控制构建流程并使用。
function create() {
const createDiv = new CreateDiv();
let dom: MyDom;
//自己构建
dom = createDiv.setTag().setId().setClass().setText().getDom();
//使用dom
dom.show();
}
create();
建造者(Builder)模式在应用过程中可以根据需要改变,如果创建的产品种类只有一种,只需要一个具体建造者,这时可以省略掉抽象建造者,甚至可以省略掉指挥者角色。
建造者模式的应用场景
当需要创建的产品具备复杂创建过程时,可以抽取出共性创建过程,然后交由具体实现类自定义创建流程,使得同样的创建行为可以生产出不同的产品,分离了创建与表示,使创建产品的灵活性大大增加。
建造者模式主要适用于以下场景:
- 相同的方法,不同的执行顺序,产生不同的结果。
- 多个部件或零件,都可以装配到一个对象中,但是产生的结果又不同。
- 产品类非常复杂,或者产品类中不同的调用顺序产生不同的作用。
- 初始化一个对象比较复杂,参数多,而且很多参数都具有默认值。
总结
我们可以灵活的切换指挥者和建造者,他们都是基于抽象,而具体的实现会由子类去完成,能保证调用上的一致性。
以上述代码来说,如果我想要创建一个不同的元素,我只需要生成不同的建造者子类就可以了,使用同一个指挥者生成不同的dom元素,使用不同的指挥者,也能生成不同的dom元素。
建造者模式用来创建复杂对象,可以通过设置不同的可选参数,“定制化”地创建不同的对象。
优点
- 产品的建造与表示分离,实现了解耦。使用建造者不需要知道具体产品实现细节。
- 将复杂的产品创建步骤分解在不同的方法中,使得创建过程更加清晰。
- 具体的建造者之间是独立的,这有利于系统的扩展,增加新的具体建造者无需修改原有的类库代码,符合开闭原则。
缺点
- 建造者模式所创建的产品-般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
版权申明
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿 - 有梦就能远航站点。未经许可,禁止转载。
相关推荐
行为模式:观察者模式
简介观察者模式:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式。而我们前端vue的响应式原理其实就是观察者模式的实现,只不过他是完全解耦的发布订阅者模式;和基础的观察者模式实现上会有不同,原理都是一样的。观察者模式是为了将重要的部分与辅助部分进行解耦,我们举个例子:在前端中,我们的滚动条距离就可以理解为一个重要部分,我们监听了scroll事件,并在回调函数中处理了一条特殊操作,比如根据滚动的距离操作header元素显隐。此时这个代码量很少,我们知道scroll事件是不建议监听太多的,...
行为模式:迭代器模式
简介迭代器模式:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。简单来说就是再程序设计中,对于一些自定义的数据结构对象,比如链表啊,或者系统提供的数据结构对象,他们的遍历方法通常会写在数据结构的类中,也就是说数据和遍历的方法是写在同一个类里面。这种方式不利于程序的扩展,因为如果要改动遍历的方法,就必须改动源数据,这违背了开闭原则。既然遍历的方法不能写在数据类中,那么将遍历的方法让客户实现不就好了,但是同样也是不可取的,如果交由客户去写,第一是会暴露很多不必要的属性给客户,第二是增加了客户端的使用负担。而迭代器模式很好的解决了这个问题,在迭代器模式中有以下几个角色...
行为模式:访问者模式
简介访问者模式:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。有一点点抽象,前一句话的意思是指在写好一个类之后,这个类就基本上不需要改动了(只要需求不改),这其实就是为了解决类结构不变但操作处理逻辑易变的问题,把对数据的操作都封装到访问者类中,我们只需要调用不同的访问者,而无需改变改变结构类,实现了。简单来说就是将一些操作作为扩展分离出去, 这些扩展的功能可能现在没有,以后会有,我们无法知道以后的情况,于是...
行为模式:命令模式
简介命令模式是将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。举个简单的例子:我有一个饭店,共有五个厨师:A,B,C,D,E;每个厨师都会做不同的菜,但是我没有菜单,于是客人就必须知道我有几个厨师,他们分别会做什么菜,于是就会产生下面这些情况:客人1想吃青椒炒蛋,于是问了好几个厨师才知道C厨师会做,于是它通知C厨师做青椒炒蛋客人2想吃冒菜,于是他也问了好几个厨师,找到E厨师制作...更多你会发现客人与厨师耦合度非常之高,这显然不利于饭店的发展,然后我们想一下生活中的真实情况:每个饭店都...
行为模式:模板方法模式
简介模板方法模式:定义一个操作中的算法骨骼,而将算法的一些步骤延迟到子类中去,使得子类可以不改变整体算法结构的情况下重新定义该算法的某些特定步骤。简单点来说就是将重要的步骤顺序的处理交由父类来做,然后父类是一个抽象的类,它申明了每个步骤的抽象,由子类去进行每个步骤的具体实现。这样父类定好了一个整体的骨骼,具体步骤实现交给子类,每当需要改动时只需要更换不同的子类就可以了,符合开闭原则。代码实现//抽象父类 abstract class A { //抽象方法 public abstract step1(): void; //抽象方法 public abstract step...
结构型模式:代理模式
简介代理模式指:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。更直白的说,代理模式更注重的是对源对象的控制访问(中介和保护),虽然我们可以在代理对象上扩展源对象没有的属性,但是如果要增强原有的功能其实还需要和其他模式进行组合使用,比如装饰器模式。角色:抽象角色:用于源对象与代理对象的依赖抽象源对象:真实的业务对象,是代理类所要代表的真实对象代理类:通过抽象角色规范,提供与源对象相同的接口,内部含有对源对象的引用,通过它可以进行扩展和控制代码实现//抽象 interface AInterfa...
结构型模式:享元模式
简介享元模式是运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。举个例子:我们有一个服装工厂,它有男装和女装衣服各100套,为了销售,需要模特来帮忙拍照做成广告照片,正常情况下我们可能会这么写。class Model { private gender: string; //性别 private clothes: number; //衣服 constructor(gender: string, clothes: number) { this.gender = gende...
结构型模式:外观模式
简介外观模式:是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。其实就是一种封装,把一系列的操作封装成简单的接口供客户端去调用。但是不能滥用,因为本身就是为了减少客户端与各个依赖的耦合性,将一堆依赖改为依赖一个外观类。外观模式遵循的是迪米特法则,也就是最少知道原则,但是如果新增新的功能,可能会改动到外观类,所以它违背了开闭原则。它常常应用于SKD,开源类库这些,用于给外部提供精简的接口。而我们js,可能并不需要去创建一个类,直接整个函...

结构型模式:组合模式
简介组合模式它本身是一种树形结构模式,可以将其理解为一棵树,其中树为根节点,然后有树枝和树叶,树枝不断的分叉,树枝上也会有树叶。我们看个图:它是一种将对象组合成树状的层次结构的模式,用来表示“整体-部分”的关系,使用户对单个对象和组合对象具有一致的访问性。其实说人话就是,我定义了一个接口,它有一个show方法,然后所有的对象都得实现,然后对象之间可以嵌套,因为嵌套了,所以当我调用show方法的时候,这个对象必须去遍历自己的子级嵌套对象的show方法,不断的重复,最终得到结果之和。类似于一个递归函数,只不过具体的处理都在每个对象自身show方法中去实现。组合模式常常用在:购物车、节点统计购...

结构型模式:装饰器模式
简介在软件开发过程中,有时想用一些现存的组件。这些组件可能只是完成了一些核心功能。但在不改变其结构的情况下,可以动态地扩展其功能。所有这些都可以釆用装饰器模式来实现。装饰器模式指:在不改变现有对象结构的情况下,动态的给对象增加一些职责(功能)的一种模式。这种模式也是防止滥用继承,相对于桥接模式只能桥接两个维度,装饰器模式是可以对接多个维度的,因为他可以嵌套使用。我们举个例子:我有一个食物类:class 煎饼我单点煎饼这个食物是可以的,我也可以加不同的配料:鸡蛋、火腿、豆皮于是乎我们通过继承得到:class 鸡蛋煎饼 extends 煎饼class 火腿煎饼 extends 煎饼class...