刘*连怎么样 http://m.39.net/pf/a_9360001.html全文共字,预计学习时长33分钟
图源:Pexels
在用JavaScript开发APP时,你可能会觉得创建一个复杂对象很困难。一旦这关系到代码中的某个细节,创建复杂对象就会变得更加复杂,因为它会使APP很占内存。
这样的困难一般有很多形式。一种是,在试图创建不同种类的复杂对象时,代码会变得很冗长;另一种,创建不同对象的过程会被拉长,因为同一等级模块中的逻辑需要梳理清晰。
这个时候,我们就需要运用到建造者模式(builderpattern)。一些问题运用建造者模式可以得到轻松改善。
首先,什么是建造者模式(builderpattern)?
建造者模式可以将一个复杂的对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。也就是说如果我们用了建造者模式,那么用户就需要指定需要建造的类型就可以得到它们,而具体建造的过程和细节就不需要知道了。建造者模式实际就是一个指挥者,一个建造者,一个使用指挥者调用具体建造者工作得出结果的客户。
建造者模式主要用于“分步骤构建一个复杂的对象”,在这其中“分步骤”是一个稳定的算法,而复杂对象的各个部分则经常变化。
通俗的说:就是一个白富美需要建一个别墅,然后直接找包工头,包工头再找工人把别墅建好。这其中白富美不用直接一个一个工人的去找。而且包工头知道白富美的需求,知道哪里可以找到工人,工人可以干活,中间节省了白富美的和工人之间沟通的成本,白富美也不需要知道房子具体怎么建,最后能拿到房就可以了。
图源:Pexels
今天的文章里,小芯就将和大家一起讨论文章开头提及的问题,以及如何在使用JavaScript的设计过程中解决这些问题。哪些问题可以通过建造者模式得到轻松改善?
首先来看一个不使用建造者模式的例子,再和使用建造者模式的例子进行对比,我们可以看到代码上的区别。
在下面的代码示例中,我们试图定义“frog(青蛙)”这一类。假设,为了让青蛙完全有能力在野外生存,它们需要两只眼睛、四条腿、嗅觉、味觉和心跳。
现在,很明显,在现实世界中,有更多事情牵涉其中,需要某一种气味才能生存听起来很荒谬,但我们不需要对每件事都完全实事求是,只要让它既简单又有趣就行了。
不用建造者模式
classFrog{
constructor(name,gender,eyes,legs,scent,tongue,heart,weight,height){
this.name=name
this.gender=gender
this.eyes=eyes
this.legs=legs
this.scent=scent
this.tongue=tongue
this.heart=heart
if(weight){
this.weight=weight
}
if(height){
this.height=height
}
}
}
Frog.jshostedwithbyGitHub
使用建造者模式
classFrogBuilder{
constructor(name,gender){
this.name=name
this.gender=gender
}
setEyes(eyes){
this.eyes=eyes
returnthis
}
setLegs(legs){
this.legs=legs
returnthis
}
setScent(scent){
this.scent=scent
returnthis
}
setTongue(tongue){
this.tongue=tongue
returnthis
}
setHeart(heart){
this.heart=heart
returnthis
}
setWeight(weight){
this.weight=weight
returnthis
}
setHeight(height){
this.height=height
returnthis
}
}
FrogBuilder.jshostedwithbyGitHub
现在这看起来好像有点矫枉过正了,因为建造者模式下的代码量更大。
但是如果深入挖掘在开发过程可能发生的所有情况,您将发现,对比这两个示例,建造者模式下的代码示例将在促进简单性、可维护性和提供更多机会,从而在实现强大功能的方面更占优势。
下面四个是在JavaScript中利用建造者模式设计就可以轻松解决的大问题。
一、可读性
图源:Pexels
最近的代码示例已经变得有点难以阅读,因为我们必须同时处理多种变化。
如果想创造“青蛙”的实例,就没有办法对其置之不理,只能去理解整个过程。
此外,提供一些文档,否则不能理解为什么tongueWidth被重命名为width。这太荒谬了!
classFrogBuilder{
constructor(name,gender){
//Ensurethatthefirstcharacterisalwayscapitalized
this.name=name.charAt(0).toUpperCase()+name.slice(1)
this.gender=gender
}
formatEyesCorrectly(eyes){
returnArray.isArray(eyes)?{left:eye[0],right:eye[1]}:eyes
}
setEyes(eyes){
this.eyes=this.formatEyes(eyes)
returnthis
}
setLegs(legs){
if(!Array.isArray(legs)){
thrownewError(legsisnotanarray)
}
this.legs=legs
returnthis
}
setScent(scent){
this.scent=scent
returnthis
}
updateTongueWidthFieldName(tongue){
constnewTongue={...tongue}
deletenewTongue[tongueWidth]
newTongue.width=tongue.width
returnnewTongue
}
setTongue(tongue){
constisOld=tongueWidthintongue
this.tongue=isOld
?this.updateTongueWidthFieldName(tongue,tongue.tongueWidth)
:tongue
returnthis
}
setHeart(heart){
this.heart=heart
returnthis
}
setWeight(weight){
if(typeofweight!==undefined){
this.weight=weight
}
returnthis
}
setHeight(height){
if(typeofheight!==undefined){
this.height=height
}
returnthis
}
build(){
returnnewFrog(
this.name,
this.gender,
this.eyes,
this.legs,
this.scent,
this.tongue,
this.heart,
this.weight,
this.height,
)
}
}
constlarry=newFrogBuilder(larry,male)
.setEyes([{volume:1.1},{volume:1.12}])
.setScent(sweatysocks)
.setHeart({rate:22})
.setWeight(6)
.setHeight(3.5)
.setLegs([
{size:small},
{size:small},
{size:small},
{size:small},
])
.setTongue({tongueWidth:18,color
arkred,type:round})
.build()
FrogBuilder.jshostedwithbyGitHub
从以下四个方面来获得提升代码可读性的能力:
1.使方法的名称具有充分的自记录性
对我们来说,updateTongueWidthFieldName的用处和使用原因很容易被定义。
我们知道这是正在更新字段名。我们也知道其中的原因,因为update这个词已经意味着更新!这个自记录的代码帮助我们假设一个旧的字段名,需要更改以使用新的。
2.简短的构造器
完全可以以后再设置别的属性!
3.当启动一个新“frog”时,要清楚了解每个参数
就像读英语一样,你需要清楚地设置出“眼睛”“腿”,然后使用建造方法去构建“青蛙”。
4.将每个逻辑隔离在单独的容易执行的代码块中
当你修改一些东西时,只需要专注于一件事,那就是在代码块中被隔离出来的东西。
二、样板文件(通过模板化解决)
图源:Unsplash
我们将来可能会遇到的一个问题是,最后会得到一些重复的代码。
例如,回顾“frog”实例时,你认为当我们想要创造某些特殊类型的青蛙时,它们会具有完全相同的特性吗?
在现实世界中,青蛙有不同的变种。例如,蟾蜍是青蛙的一种,但并非所有的青蛙都是蟾蜍。所以,这告诉我们蟾蜍有一些与普通青蛙不同的特性。
蟾蜍和青蛙的一个区别是,蟾蜍的大部分时间是在陆地上度过的,而普通青蛙是在水里。此外,蟾蜍的皮肤有干疙瘩,而正常青蛙的皮肤有点黏。
这意味着我们必须以某种方式确保每次青蛙被实例化时,只有一些值可以通过,也有一些值必须通过。
让我们回到Frog构造器,添加两个新参数:栖息地和皮肤:
将来可能会遇到的一个问题是,最终会得到一些重复的代码。
classFrog{
constructor(
name,
gender,
eyes,
legs,
scent,
tongue,
heart,
habitat,
skin,
weight,
height,
){
this.name=name
this.gender=gender
this.eyes=eyes
this.legs=legs
this.scent=scent
this.tongue=tongue
this.heart=heart
this.habitat=habitat
this.skin=skin
if(weight){
this.weight=weight
}
if(height){
this.height=height
}
}
}
Frog.jshostedwithbyGitHub
在两次简单的更改后,这个构造器已经有点混乱了!这就是为什么推荐使用建造者模式。
如果把栖息地和皮肤参数放在最后,可能会出现错误,因为体重和身高可能很难确定,而这些又都是可变的!
又由于这种可选性,如果用户不传递这些信息,就会出现错误的栖息地和皮肤信息。
编辑FrogBuilder来支持栖息地和皮肤:
setHabitat(habitat){
this.habitat=habitat
}
setSkin(skin){
this.skin=skin
}
FrogBuilder.jshostedwithbyGitHub
现在假设需要两只分开的蟾蜍和一只正常的青蛙:
//frog
constsally=newFrogBuilder(sally,female)
.setEyes([{volume:1.1},{volume:1.12}])
.setScent(blueberry)
.setHeart({rate:12})
.setWeight(5)
.setHeight(3.1)
.setLegs([
{size:small},
{size:small},
{size:small},
{size:small},
])
.setTongue({width:12,color:navyblue,type:round})
.setHabitat(water)
.setSkin(oily)
.build()
//toad
constkelly=newFrogBuilder(kelly,female)
.setEyes([{volume:1.1},{volume:1.12}])
.setScent(blackice)
.setHeart({rate:11})
.setWeight(5)
.setHeight(3.1)
.setLegs([
{size:small},
{size:small},
{size:small},
{size:small},
])
.setTongue({width:12.5,color
live,type:round})
.setHabitat(land)
.setSkin(dry)
.build()
//toad
constmike=newFrogBuilder(mike,male)
.setEyes([{volume:1.1},{volume:1.12}])
.setScent(smellysocks)
.setHeart({rate:15})
.setWeight(12)
.setHeight(5.2)
.setLegs([
{size:medium},
{size:medium},
{size:medium},
{size:medium},
])
.setTongue({width:12.5,color
live,type:round})
.setHabitat(land)
.setSkin(dry)
.build()
FrogBuilder.jshostedwithbyGitHub
那么,这里的代码哪里重复了呢?
如果仔细观察,就会注意到我们必须重复蟾蜍的栖息地和皮肤设置。如果再有五个只属于蟾蜍的设置呢?那么每次输出蟾蜍或者是普通青蛙的时候,都要手动操作这个模板。
创建一个模板,按照惯例,通常称之为指导器(director)。
指导器负责执行创建对象的步骤——通常是在构建最终对象时可以预先定义一些公共结构,比如本例中的蟾蜍。
因此,不必手动设置蟾蜍之间的不同属性,可以让指导器直接生成:
classToadBuilder{
constructor(frogBuilder){
this.builder=frogBuilder
}
createToad(){
returnthis.builder.setHabitat(land).setSkin(dry)
}
}
letmike=newFrogBuilder(mike,male)
mike=newToadBuilder(mike)
.setEyes([{volume:1.1},{volume:1.12}])
.setScent(smellysocks)
.setHeart({rate:15})
.setWeight(12)
.setHeight(5.2)
.setLegs([
{size:medium},
{size:medium},
{size:medium},
{size:medium},
])
.setTongue({width:12.5,color
live,type:round})
.build()
ToadBuilder.jshostedwithbyGitHub
这样,就可以避免将蟾蜍的共享样板文件应用到所有,而只