C++实现设计模式——Builder模式
- 建造者模式定义
建造者(Builder)模式的定义:指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。
- 举例为什么用Builder模式
举一个通俗的例子:
我们大家都玩游戏吧,每个角色会有很多属性,即构造参数,有一些是构造必需参数,如id,name,有一些是可以用默认的值,比如hp,level等等,游戏刚开始我们可以用默认值构造,但是当游戏过了一段时间这些属性都变得各不一样了,此时我们再次构造玩家角色的时候,只能是先构造一个初始化的,然后在后期修改赋值,如下:
Player *p1 = new Player ();
p1->setName("milo");
p1->setHp(1000);
p1->setleve(6);
```
但是这样有个不好的地方,那就是对象的构造过程是非连续的,也就是说对象可处于一个构造不完全的状态,我们很容易写出将对象传入各个方法,每个方法去赋值对象的某一部分这样的代码,这其实引入了一个状态空间,如果状态空间是强可控的,那还好(但依然提高了维护成本,你需要牢牢掌握住对象的构造过程,什么字段在何处被赋值);如果不可控,那么就很难保证这个对象是否被正确的构造,可能在某个方法中覆盖了某字段,也可能遗漏了某字段导致 错误。
那为了解决这个问题,建造者模式就出现了,就可以写成下面的这种方式
Player p1= PlayerBuilder.player()
.name("milo")
.hp(1000)
.leve(6)
.build();
需要构造什么参数,就加上哪个接口,其它的用默认值,这就可以绝对赋值哪个,这是第一个优点,还有一个优点,建造者模式可以随机组装构造参数的顺序,个数,这个在复杂构造的情况下很有用。
- 结构图
- 代码举例
#include <iostream>
#include <string>
using namespace std;
class Player;
class playBuilder
{
public:
string Name;
int ID;
int HP;
int Level;
public:
playBuilder() { //参数默认值
Name = "xiaoming";
ID = 1001;
HP = 100;
Level = 1;
cout<<"默认构造函数"<<endl;
}
playBuilder* SetName(string Name) {this->Name = Name;return this;}
playBuilder* SetID(int ID) {this->ID = ID;return this;}
playBuilder* SetHP(int HP) {this->HP = HP;return this;}
playBuilder* SetLevel(int Level) {this->Level = Level;return this;}
Player *build();
};
class Player {
public:
string Name;
int ID;
int HP;
int Level;
public:
string GetName() {return Name;}
int GetID() {return ID;}
int GetHP() {return HP;}
int GetLevel() {return Level;}
Player() {cout<<"默认构造函数"<<endl;}
Player (playBuilder const &temp) {
this->Name = temp.Name;
this->ID = temp.ID;
this->HP = temp.HP;
this->Level = temp.Level;
}
~Player() {}
void show() {
cout<<Name<<"-"<<ID<<"-"<<HP<<"-"<<Level<<endl;
}
};
Player *playBuilder::build(){
return new Player(*this);
}
int main() {
Player * p1 = playBuilder().SetID(90)->build();
Player * p2 = playBuilder().SetID(91)->SetHP(200)->build();
Player * p3 = playBuilder().SetName("zhangsan")->SetID(92)->build();
p1->show();
p2->show();
p3->show();
return 0;
}
上面这种情况就是我们要设置哪个参数就添加哪个方法set就行,对象是一个完整的创建过程,这也是我们很多地方比较常见的一种用法,它比正常的builder少了director角色,算是个简化版。
加入现在我们要求按照一定的规则进行构造,下面我们加上director,如果说builder是构造器,可以给我们随心构造的条件,那么director就好比一个指挥者,它规定着builder该怎么去创建一个目标对象。
#include <iostream>
#include <string>
using namespace std;
class Player;
class playBuilder
{
public:
string Name;
int ID;
int HP;
int Level;
public:
playBuilder() { //参数默认值
Name = "xiaoming";
ID = 1001;
HP = 100;
Level = 1;
cout<<"默认构造函数"<<endl;
}
playBuilder* SetName(string Name) {this->Name = Name;return this;}
playBuilder* SetID(int ID) {this->ID = ID;return this;}
playBuilder* SetHP(int HP) {this->HP = HP;return this;}
playBuilder* SetLevel(int Level) {this->Level = Level;return this;}
Player *build();
};
class Player {
public:
string Name;
int ID;
int HP;
int Level;
public:
string GetName() {return Name;}
int GetID() {return ID;}
int GetHP() {return HP;}
int GetLevel() {return Level;}
Player() {cout<<"默认构造函数"<<endl;}
Player (playBuilder const &temp) {
this->Name = temp.Name;
this->ID = temp.ID;
this->HP = temp.HP;
this->Level = temp.Level;
}
~Player() {}
void show() {
cout<<Name<<"-"<<ID<<"-"<<HP<<"-"<<Level<<endl;
}
};
Player *playBuilder::build(){
return new Player(*this);
}
class Director {
public:
playBuilder *builder;
Director() {
builder = new playBuilder();
}
Player * construct(string Name,int ID,int HP,int Level) { //构造规则
builder = builder->SetName(Name);
builder = builder->SetID(ID);
builder = builder->SetHP(HP);
builder = builder->SetLevel(Level);
return builder->build();
}
~ Director() {
delete builder;
}
};
int main() {
Director d1;
Player * p1 = d1.construct("xiaowang",999,9999,1); //输入构建规则需要的参数
Player * p2 = Director().construct("xiaosong",888,8888,8);
Player * p3 = playBuilder().SetName("zhangsan")->SetID(92)->build();
p1->show();
p2->show();
p3->show();
return 0;
}
如图中所示,参数的构造顺序和需要构造哪些参数都在Director中决定了,如果需要多种规则方案,就往里面添加construct方法。
以上就是builder的设计方式相关了,只是简单的介绍了为什么要用,可以怎么用,具体的线程安全等等细节,在我们实际使用的时候还是要多考虑考虑的。