Part2设计模式和结构化有较强的联系,分成两个part来解释,这篇主要包括桥接,适配器,装饰器这三种
Bridge
优点:
- 通过这种模式可以将大量的没用到的成员与方法隐藏起来,只暴露出公用接口,降低复杂性
- 可以通过前向声明 impl,把大量的include放到 impl.cpp里面,降低编译成本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
struct Person{
std::string name;
class PersonImpl;
PersonImpl *impl; // bridge - not necessarily inner class, can vary
Person();
~Person();
void greet();
};
struct Person::PersonImpl
{
void greet(Person* p);
};
void Person::PersonImpl::greet(Person* p){
printf("hello %s", p->name.c_str());
}
Person::Person(): impl(new PersonImpl){}
Person::~Person(){ delete impl;}
void Person::greet(){ impl->greet(this);}
|
这个例子就是通过PersonImpl把大量的实现细节隐藏到了这个类里面,brpc中的server搭建就有很经典的使用. ==tips==: Plmpl 编译防火墙 解除了接口与实现之间的耦合关系,从而降低文件间的编译依赖关系
Composite
这个模式比较简单,直接看代码,这是神经网络的neuron类例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
template <typename Self>
struct SomeNeurons { //主要是为了封装connect_to
template <typename T> void connect_to(T& other){
for (Neuron& from : *static_cast<Self*>(this)){
for (Neuron& to : other){
from.out.push_back(&to);
to.in.push_back(&from);
}
}
}
};
struct Neuron : SomeNeurons<Neuron>{
vector<Neuron*> in, out;
unsigned int id;
Neuron(){
static int id = 1; //static标记id,很方便
this->id = id++;
}
Neuron* begin() { return this; }
Neuron* end() { return this + 1; }
};
struct NeuronLayer : vector<Neuron>, SomeNeurons<NeuronLayer>{
NeuronLayer(int count){
while (count-- > 0)
emplace_back(Neuron{});
} //继承vector用来组合neuron向量,继承SomeNeurons用来解决连接问题
};
|
Decorator
C++11给这个模式带了了很多新东西,一起来看看吧
Dynamic Decorator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
struct Shape{
virtual string str() const = 0;
};
struct ColoredShape : Shape{ //装饰类
Shape& shape;
string color;
ColoredShape(Shape& shape, const string& color): shape{shape}, color(color){}
string str() const override{...}
};
struct Circle: Shape {
float radius;
CirCle(float radius): radius(radius){}
resize(float factor) {radius *= factor};
string str() const override{...}
}
Circle circle(0.6);
ColoredShape redCircle(circle, "red"); //shape引用
|
Static Decorator
前面动态类型的一个缺点是说被修饰的redCircle无法访问circle的方法, 比如 redCircle.resize(2) 编译不通过,下面这个实现方法就是为了解决这个问题的。这个方法的缺点是再编译期进行的装饰,没法重组合。
1
2
3
4
5
6
7
8
9
10
11
12
|
template <typename T>
struct ColoredShape: T{
static_assert(is_base_of<Shape,T>::value, "template arg must be a Shape");
string color;
ColoredShape(Shape& shape, string color): shape(shape), color(color){}
string str(){...}
};
ColoredShape<TransparentShape<Square>> square{"blue"}; //可以访问所有被修饰的层以及原本的square的所有成员
square.size = 2;
square.transparency = 0.5;
square.resize(3);
|
这里还有个缺点,通过这种方法,我们没法调用一次构造函数实现所有成员+修饰成员的初始化,解决方法为可变参数模板+类型推导
1
2
3
4
5
6
7
8
9
10
|
template <typename T>
struct TransparentShape: T{
int trans;
template<typename ...Args>
TransparentShape(const int trans, Args ...args):
T(std::forward<Args>(args)...), trans(trans){}
...
}
ColoredShape2<TransparentShape2<Square>> sq = { "red", 51, 5 }; //这样初始化就没问题了
|
Functional Decorator
针对函数的装饰器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
//1.不需要返回值
template <typename Func>
struct Logger2{
Func func;
string name;
Logger2(const Func& func, const string& name)
: func{func},
name{name}{}
void operator()() const{
cout << "Entering " << name << endl;
func();
cout << "Exiting " << name << endl;
}
};
template <typename Func>
auto make_logger2(Func func,
const string& name){
return Logger2<Func>{ func, name };
}
//call
auto call = make_logger2([](){cout<<"count"<<endl;}, "HelloFunc");
call();
|
还有一种是当有入参和返回值的需求时, 可变参数模板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
template <typename> struct Logger3; //为啥这里需要先部分特化的声明?
template <typename R, typename... Args>
struct Logger3<R(Args...)>
{
Logger3(function<R(Args...)> func, const string& name)
: func{func},
name{name}{}
R operator() (Args ...args){
cout << "Entering " << name << endl;
R result = func(args...);
cout << "Exiting " << name << endl;
return result;
}
function<R(Args ...)> func;
string name;
};
template <typename R, typename... Args>
auto make_logger3(R (*func)(Args...), const string& name){
return Logger3<R(Args...)>(
std::function<R(Args...)>(func),
name);
}
double add(double a, double b){
cout << a << "+" << b << "=" << (a + b) << endl;
return a + b;
}
//call
auto logged_add = make_logger3(add, "Add");
auto result = logged_add(2, 3);
|
文章作者
Sun.StriKE
上次更新
2019-04-10
(a5d7191)