构造器模式简介
对于大多数的类来说,直接使用构造函数便可得到想要的对像,对于复杂属性的类,可能多设几个构造函数,或构造函数多设几个参数也可能达到想要的结果。但对于复杂的对象有时还真是需要使用专门的构造器来获取适当对象。再说,对于C++开发来说,你搞个构造函数有七八个参数甚至更多,看你领导想不想批你!
对的,构造器模式是按需生成别的类对像的方法,但构造器本身肯定也是类了,要在这类里设置如何生成指定对像呢。
相关知识
1、如果获取某类型对象都考虑使用构造器了,那该类型本身的构造函数是否要私有呢?我想绝大多数情况都会吧。
2、、初始化成员变量并不一定必须能过构造函数传参,也可以用{}初始化。
class Dog
{
std::string name, weight, color;
//...
}
那么使用 Dog boMei{“bingo”, “15KG”, “withe”};也是可以的。
3、链式设置设计。像上面Dog 类,可能会有 setName(…), setWeight(…),setColor(…)等接口,那么如果每个函数返回自身引用能实现什么效果呢?
class Dog
{
std::string name, weight, color;
public:
Dog& setName(std::string nam){ name = nam; return *this;}
Dog& setWeight(std::string weigh){ weight = weigh; return *this;}
Dog& setColor(std::string clr) { color = clr; return *this;}
//...
}
那么在连续设置属性时,可Dog dog; dog.setName(“bingo”).setWeight(“15KG”).setColor(“withe”); 。怎么样,方便很多吧?
4、假设有个构造器,class DogBuilder 是用来构造 Dog对象的,那么可以在构造器内部重载 Dog() ,这样就可以直接返回Dog 对象了
class DogBuilder
{
Dog dog;
public:
operator Dog() { return std::move(dog);}
DogBuilder &setName(string nam) { dog.setName(nam); }
DogBuilder &setWeight(string weigh) { dog.setWeight(weigh);}
DogBuilder &setColor(string color) {dog.setColor(color);}
}
那么就可以通过构造器生产简单的获取一个Dog对象了。
Dog myDog = DogBuilder().setName("bingo").setWeight("15KG").setColor("withe");
不过,这方法给人莫名其妙的感觉,不如直接设个接口 Dog getDog(){ return dog;} 给人明确。注意了,如果用了move那么这个构造器就不能再用着构造下一个Dog了。
5、有时候一个对象很复杂,必须分几个构造器才能将不同属性构造成功,这时候可能构造器里对象设为引用,然后从构造函数传参进去了。另一方面几个构造器也可以放在同一个基类里,这样每部分构造完,在调下一个部分时,都会有接口直接调用。
代码实践
下面就当Dog 类有三方面属性(name, weight, color),使用三个构造器来分别构造。
DogBuilder.h
#pragma once
#include <string>
#include <iostream>
class Dog
{
std::string name, weight, color;
public:
Dog() = default;
Dog& setName(std::string nam) { name = nam; return *this; }
Dog& setWeight(std::string weigh) { weight = weigh; return *this; }
Dog& setColor(std::string clr) { color = clr; return *this; }
void printInfo() {
std::cout << "Dog Information, Name: " << name << " Color:" << color << " Weight:" << weight << std::endl;
}
};
class DogWeightBuilder;
class DogColorBuilder;
class DogBaseBuilder
{
protected:
Dog& dog;
explicit DogBaseBuilder(Dog& dog):dog(dog){}
public:
operator Dog() { return std::move(dog); }
DogWeightBuilder weightAttri() const;
DogColorBuilder colorAttri() const;
};
class DogBuilder : public DogBaseBuilder
{
using Self = DogBuilder;
//dog is construct at this
Dog dog;
public:
explicit DogBuilder() :DogBaseBuilder(dog) {}
Self& setName(std::string name) { dog.setName(name); return *this; }
//this Builder is main builder.
};
class DogWeightBuilder : public DogBaseBuilder
{
using Self = DogWeightBuilder;
public:
explicit DogWeightBuilder(Dog& dog) :DogBaseBuilder(dog) {}
Self& setWeight(std::string weigh) {
dog.setWeight(weigh);
return *this;
}
//other attribute set interface
};
class DogColorBuilder : public DogBaseBuilder
{
using Self = DogColorBuilder;
public:
explicit DogColorBuilder(Dog& dog) :DogBaseBuilder(dog) {}
Self& setColor(std::string clr) {
dog.setColor(clr);
return *this;
}
//other attribute set interface
};
DogBuilder.cpp
#include "DogBuilder.h"
DogWeightBuilder DogBaseBuilder::weightAttri() const
{
return DogWeightBuilder(dog);
};
DogColorBuilder DogBaseBuilder::colorAttri() const
{
return DogColorBuilder(dog);
}
main.cpp
#include "DogBuilder.h"
int main()
{
Dog dog = DogBuilder().setName("BingGo").
colorAttri().setColor("Withe").
weightAttri().setWeight("15 KG");
dog.printInfo();
system("pause");
}
结果输出:
符合预期。
总结拓展
通过上面的知识点整合,让你写一个PetStoreService类,Dog 对象只能从这个类获取,并且各种属性能一次性设置完毕,这不会很难了吧?或者更进一步获取到一个Dog要做什么工作是不是也完全可以在PetStoreService里直接实现了,就是外面根本不需要暴露Dog 类的具体对象,构造器就可设好身高、体重、战斗力后,用Dog看门这事就直接在PetStoreService里被执行了。