详解C++设计模式编程中建造者模式的实现
作者:曾经的你| 发布时间:2022-10-25 01:33:20
建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。这是建造者模式的标准表达,不过看着让人迷惑,什么叫构建和表示的分离?一个对象使用构造函数构造之后不就固定了,只有通过它方法来改变它的属性吗?而且还要同样的构建过程搞出不同的表示,怎么可能呢?多写几个构造函数?
其实多写几个构造函数,根据不同参数设置对象不同的属性,也可以达到这样的效果,只是这样就非常麻烦了,每次要增加一种表示就要添加一个构造函数,将来构造函数会多得连自己都不记得了,这违背了开放-封闭的原则。
要不就只能设计几个set函数,每次属性不一样了,我就构造一个对象,然后用set函数改变对象的属性。这样也可以达到效果。只是代码就会非常冗余了,每个要用到这个对象的地方,都要写上好几句语句,一旦对象有点什么变化,还得到处都改一遍,这样就很容易出错,以后别人看着这种神逻辑和神代码估计也会崩溃了。而且这也违背了依赖倒转的原则。
于是大神们就开始想了,不能加很多构造函数,也不能直接用一堆set函数,然后发现,有些对象的构建是固定的几个步骤的,就像一条流水线一样,任何的产品都是通过每一个固定的步骤拼凑出来的。例如说一部手机,先放主板,再放屏幕,再放电池,再放外壳,贴个膜就能卖几千了,每次推出新产品,就换个更好的主板,换个大点的屏幕,再整个大容量电池,贴个超牛B的高透膜,又能卖出个新价钱。就是说,这些步骤都没有变,变的只是每个部分的东西。
这就是大神的厉害之处了,透过现象看本质,基本有变的,有不变的,那敢情好,面向对象的一个重要指导思想就是,封装隔离变化的,留出不变的。于是他们就用一个Builder类把步骤中的每个部分封装起来,这个类的主要作用就是生产每个部件,再抽象一下提升高度,这样就依赖倒转了,这样每次只需要添加一个类,这个类还是这几个部分,只是内部的实现已经不一样了,这样就满足了开放-封闭的原则了。但还是有一个问题,光有Builder类还不行,虽然产品的每个部分都有对应的函数,但是用起来的话,还是跟前面说的set函数一样,一用就要使用一大堆函数,也就是这变的东西是封装起来了,但这不变的东西还没留出来。这时,就添加一个Director类,这个类就是专门规定组装产品的步骤的,这样只要告诉Director使用哪个Builder,就能生产出不同的产品,对于客户端来说,只看到用了Director的一个construct函数,甚是方便。
再反过来看建造者模式的定义,构建指的就是生产一个产品的步骤,表示就是每个产品部分的具体实现,通过Director封装步骤,通过Builder封装产品部分的实现,再把他两隔离开,就能隔离变的,留出不变的供客户端使用。
图中可以看到,Product是必须要知道,没有抽象,但是这个产品却可以由不同的部分组合而成。Director里的construct也是固定,没有抽象出来,如果要更改步骤,也要添加一个函数,或者再添一个Diector,所以建造者模式一般应用于步骤不会发生大的变化,而产品会发生大变化的情况。
常用的场景
C#中的StringBuilder就是一个建造者的例子,但只是一个建造者,还缺一个Director,不能算一个完整的建造者模式。建造者模式一般应用于构建产品的步骤(也可以称为算法)不变,而每个步骤的具体实现又剧烈变化的情况。
优点
1.隔离了构建的步骤和具体的实现,为产品的具体实现提供了灵活度。
2.封装和抽象了每个步骤的实现,实现了依赖倒转原则。
3.封装了具体的步骤,减少了代码的冗余。
缺点
1.要求构建产品的步骤(算法)是不能剧烈变化的,最好是不变的,这样就影响了灵活度。
实例
#include "stdafx.h"
#include <stdlib.h>
#include <iostream>
using namespace std;
//抽象类,用来安排创建人的具体流程,其他类必须遵循这个流程,但是可以自己具体实现
class CPersonBuilder
{
public:
virtual void BuildHead()=0;
virtual void BuildBody()=0;
virtual void BuildArmLeft()=0;
virtual void BuildArmRight()=0;
virtual void BuildLegLeft()=0;
virtual void BuildLegRight()=0;
};
//创建瘦子的类
class CThinPersonBuilder:public CPersonBuilder
{
public:
CThinPersonBuilder()
{
cout<<"is creating thin person "<<endl<<endl;
}
~CThinPersonBuilder()
{
cout<<"is finished for thin person"<<endl<<endl;
}
public:
void BuildHead()
{
cout<<"BuildHead"<<endl;
}
void BuildBody()
{
cout<<"BuildBody(thin)"<<endl;
}
void BuildArmLeft()
{
cout<<"BuildArmLeft"<<endl;
}
void BuildArmRight()
{
cout<<"BuildArmRight"<<endl;
}
void BuildLegLeft()
{
cout<<"BuildLegLeft"<<endl;
}
void BuildLegRight()
{
cout<<"BuildLegRight"<<endl;
}
};
//创建胖子的类
class CFatPersonBuilder:public CPersonBuilder
{
public:
CFatPersonBuilder()
{
cout<<"is creating fat person"<<endl;
}
~CFatPersonBuilder()
{
cout<<"is finished for fat person"<<endl;
}
public:
void BuildHead()
{
cout<<"BuildHead"<<endl;
}
void BuildBody()
{
cout<<"BuildBody(Fat)"<<endl;
}
void BuildArmLeft()
{
cout<<"BuildArmLeft"<<endl;
}
void BuildArmRight()
{
cout<<"BuildArmRight"<<endl;
}
void BuildLegLeft()
{
cout<<"BuildLegLeft"<<endl;
}
void BuildLegRight()
{
cout<<"BuildLegRight"<<endl;
}
};
//指挥者类,用来指挥创建的人是瘦子还是胖子
class CPersonDirector
{
public:
CPersonDirector(CPersonBuilder *p)
{
this->m_p=p;
}
const void CreatePerson(void) const
{
m_p->BuildHead();
m_p->BuildBody();
m_p->BuildArmLeft();
m_p->BuildArmRight();
m_p->BuildLegLeft();
m_p->BuildLegRight();
}
private:
CPersonBuilder *m_p;
};
int _tmain(int argc, _TCHAR* argv[])
{
cout<<"---------建造者模式测试案例------------------------"<<endl<<endl;
CThinPersonBuilder *p_tp=new CThinPersonBuilder();
CPersonDirector *p_dtp=new CPersonDirector(p_tp);
p_dtp->CreatePerson();
delete p_tp;
delete p_dtp;
p_tp=NULL;
p_dtp=NULL;
cout<<endl<<endl;
CFatPersonBuilder *p_fp=new CFatPersonBuilder();
CPersonDirector *p_dfp=new CPersonDirector(p_fp);
p_dfp->CreatePerson();
delete p_fp;
delete p_dfp;
p_fp=NULL;
p_dfp=NULL;
system("pause");
return 0;
}


猜你喜欢
- 由于maven 使用上手很容易所以很多时候可以囫囵吞枣能够使用就可以了,由于作者最近在做的持续集成的代码扫描的时候,发现私有云里面大型工程m
- 本文为大家分享了C#基于Socket套接字的网络通信封装代码,供大家参考,具体内容如下摘要之所以要进行Socket套接字通信库封装,主要是直
- 首先,说说final。final关键字可以修饰变量,方法,类。final变量:需求:1 需要一个永不改变的编译时常量2 一个运行时被初始化的
- QTableView是Qt中用来把数据集以表格形式提供给用户的一个控件QTableView类实现表格视图,QTableView的数据由继承Q
- 在开发过程中,碰到生成一个List对象,需要对其里面的每个对象都进行校验。但是,这个Lis
- 引言依照领导要求区分了两种压测模式:固定次数压测和固定时间压测。此前一直沿用的都是固定次数,所以本次第二版剥离了固定次数的模式增加了固定时间
- Android launcher中模拟按home键的实现Intent mHomeIntent = new Intent(Intent.ACT
- 现阶段,我们创建了最简单的Android项目,现在在此公布github链接https://github.com/neuyu/android-
- 在类中自定义的“函数”称为“方法”,由于C#是完全面向对象的
- android Notification实例详解1.使用Builder模式来创建2.必须要设置一个smallIcon,还可以设置setTic
- 实例如下:private void Form1_Load(object sender, EventArgs e)
- 一、使用@Profile1.1、@Profile修饰类开发环境package com.example.demo.config;import
- 创建SpringBoot项目可以通过两种方式1、通过访问:https://start.spring.io/,SpringBoot的官方网站进
- 如下所示:if(File.Exists(path)){// 是文件}else if(Directory.Exists(path)){// 是
- 一、什么是CharacterEncodingFilter官方解释如下是spring内置过滤器的一种,用来指定请求或者响应的编码格式。在web
- 一.hutool工具摘抄一段hutool工具的简介:Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,
- 目录一.什么是负载均衡二.负载均衡的简单分类三.为什么需要做负载均衡四.springCloud如何开启负载均衡五.IRule1.Random
- 国际化(internationalization)是设计和制造容易适应不同区域要求的产品的一种方式。它要求从产品中抽离所有地域语言,国家/地
- 概念是利用锁的机制来实现同步的。互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程中的协调机制,这样在同一时间只有一
- 写在前面:上周末抽点时间把自己写的一个简单Socket聊天程序的初始设计和服务端细化设计记录了一下,周二终于等来毕业 * 的软考证书,然后接下