Builder模式实例分析(C语言版)
Builder模式
设计模式之Builder
Builder模式定义:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示.
Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们.用户不知道内部的具体构建细节.Builder模式是非常类似抽象工厂模式,细微的区别大概只有在反复使用中才能体会到.
为何使用?
是为了将构建复杂对象的过程和它的部件解耦.注意: 是解耦过程和部件.
因为一个复杂的对象,不但有很多大量组成部分,如汽车,有很多部件:车轮方向盘发动机还有各种小零件等等,部件很多,但远不止这些,如何将这些部件装配成一辆汽车,这个装配过程也很复杂(需要很好的组装技术),Builder模式就是为了将部件和组装过程分开.
如何使用?
首先假设一个复杂对象是由多个部件组成的,Builder模式是把复杂对象的创建和部件的创建分别开来,分别用Builder类和Director类来表示.
首先,需要一个接口,它定义如何创建复杂对象的各个部件:
public interface Builder {
//创建部件A比如创建汽车车轮
void buildPartA();
//创建部件B 比如创建汽车方向盘
void buildPartB();
//创建部件C 比如创建汽车发动机
void buildPartC();
//返回最后组装成品结果(返回最后装配好的汽车)
//成品的组装过程不在这里进行,而是转移到下面的Director类中进
行.
//从而实现了解耦过程和部件
Product getResult();
理解设计模式3—生成器(Builder)
,
概 述
视 觉 系统 是 ^ 类从 周围 世界 获 取 信 息 的最 l 要 的通 道 , 冈 丰
身的 特性 等 .部是 影响 图像像 素 点值的 因 素。因此 .要想 区分 出每 个 因素 对 图像 中 每点 的影 响是 非常 困难 的。 3 理 解一 幅 图 像往 往 需要 有 这 个领 域 中 的 些先 验 知识
象 一般提 供装配接 口供具体 生成器用来 装配产 品。
生活 中的倒 予
现实 中 最 明星 的 例 子就 是 工 厂的 流 水 线 。一 个 产 品的 备 个 部 分 被 舟 别 建 造 ,然 后 在梳 水 线 上 装 配形 成 完 整 的产 品。 根据 装 配的 腼 序或 这 十 组 成 部分 的 不 同 ,会 创建 不 同 的产 品 。 创建 产 品的 过 程 由流 水 线 和 创建 备 个 部 舟 的机 器 协 同完 成 ,提 水线
维普资讯 http://www.cqvip.com
理 设计 式豢 解 模
生成器 (u d r B U e)
o 撰文/ 非壹
生 成 器是 面向 创建 过 程 的 一 种模 式 , 它 专 注 于 如何 重 用产 品 的 创 建 过 程 . 通 过 砷 创建 过 程 的 重 用产 生 不 同 的产 品 关曩 诃 :设 计 曩式 创 t型 囊 式 蔓用 生成器实现了生成器 ( u l e ) 口.定义了产品备个组成 B id r 接 部舟 的具 体创建 方法 ,并提 供 了获取 对 象的接 口。 ・ 产品 (r d c Pout A.P o u t )——被创建的产品对象。产品对 rd cB 生成器 f u l e )是面向创建过程的一种模式 .它专注干如何 B id r 重用 产品 的 创建 过程 ,通过 对创建 过 程的重 用产生 不 同的产 品 。 生成 器模 式和其 他 几种 创建 型模式 关 系不大 .一 般独 立使用 。
基于HBuilder快速开发移动端APP的设计与实现
基于HBuilder快速开发移动端APP的设计与实现
1. 引言
1.1 背景介绍
随着移动应用市场的不断扩大和升级,基于HBuilder快速开发移动端APP的设计与实现备受关注。本文旨在探讨HBuilder开发工具的特点和优势,介绍移动端APP设计流程,详细阐述HBuilder快速开发移动端APP的步骤,并通过实例分析和优缺点比较,总结出基于HBuilder开发移动端APP的一些经验和规律,以期为开发者提供参考和借鉴。通过本文的研究,希望能够为移动应用开发领域的进一步发展提供一些启示和建议。
1.2 研究目的
本研究旨在探讨基于HBuilder快速开发移动端APP的设计与实现方法,通过深入研究HBuilder开发工具和移动端APP设计流程,以及探讨HBuilder快速开发移动端APP的步骤,从而为开发人员提供有效的参考和指导。具体目的包括:
1. 探究HBuilder开发工具的特点和优势,从而更好地了解如何利用该工具进行移动应用开发;
2. 分析移动端APP设计流程,深入了解移动应用开发的整体流程和关键要素;
3. 探讨HBuilder快速开发移动端APP的具体步骤和方法,为开发人员提供实用的开发经验和技巧;
4. 通过实例分析,展示HBuilder快速开发移动端APP的实际应用效果和优势;
5. 比较HBuilder快速开发移动端APP的优缺点,为开发人员提供更全面的了解和选择参考。
通过以上研究目的,本研究旨在为开发人员提供实用的指导和建议,促进移动应用开发的效率和质量提升。
1.3 研究意义
移动应用已经成为人们日常生活中不可或缺的一部分,而基于HBuilder快速开发移动端APP的设计与实现具有重要的研究意义。随着移动互联网的快速发展,移动应用市场愈发竞争激烈,开发者需要快速、高效地发布新应用来抢占市场份额。基于HBuilder的快速开发工具可以大大缩短开发周期,提高开发效率,有助于开发者更快速地响应市场需求。
23种设计模式简单实例
23种设计模式简单实例
设计模式是解决软件设计中常见问题的经典解决方案,它们被广泛应
用于软件工程中。本文将介绍23种设计模式,并提供每种模式的简单实
例来帮助读者更好地理解和应用这些模式。
1. 创建型模式(Creational Patterns)
1.1 简单工厂模式(Simple Factory Pattern):一个工厂类根据传
入的参数决定创建哪一种产品类的实例。例如,一个汽车工厂根据参数创
建不同型号的汽车。
1.2 工厂方法模式(Factory Method Pattern):定义一个创建对象
的接口,但由子类决定要实例化的类是哪一个。例如,一个汽车品牌父类
提供创建具体汽车型号的方法,由子类决定具体型号。
1.3 抽象工厂模式(Abstract Factory Pattern):提供一个创建一
系列相关或相互依赖对象的接口,而无需指定它们的具体类。例如,一个
汽车制造工厂提供创建汽车引擎和轮胎的方法。
1.4 建造者模式(Builder Pattern):将一个复杂对象的构建与它
的表示分离,使得同样的构建过程可以创建不同的表示。例如,一个套餐
包括饮料、主食和甜品等多个组件,可以通过建造者模式逐步构建套餐。
1.5 单例模式(Singleton Pattern):保证一个类仅有一个实例,
并提供一个访问它的全局访问点。例如,一个日志管理器只能有一个实例,可以通过单例模式实现。
2. 结构型模式(Structural Patterns)
2.1 适配器模式(Adapter Pattern):将一个类的接口转换成客户希望的另一个接口。例如,将一个新的USB设备适配到旧的电脑接口上。
01 C#设计模式-设计模式概述-1
GoF设计模式简介
创建型模式
抽象工厂模式(Abstract Factory) ★★★★★ 建造者模式(Builder) ★★☆☆☆ 工厂方法模式(Factory Method) ★★★★★ 原型模式(Prototype) ★★★☆☆
设计模式的定义与分类
设计模式的分类(P)
根据范围,即模式主要是处理类之间的关系还是处理对 象之间的关系,可分为类模式和对象模式两种: • 类模式处理类和子类之间的关系,这些关系通过继承 建立,在编译时刻就被确定下来,是一种静态关系 • 对象模式处理对象间的关系,这些关系在运行时变化, 更具动态性
设计模式的诞生与发展
Gang of Four
Erich Gamma
苏黎世大学计算机科学博士,是 Eclipse、 JUnit 等项目的负责人 墨尔本大学计算机科学博士,原IBM 研究员,现供职于波士顿顾问集团 康奈尔大学计算机科学博士,伊利诺伊 大学教授 斯坦福大学计算机科学博士,原IBM研究 员,于2005年11月24日因脑瘤去世,享 年44岁
实例说明 • 某软件公司要开发一个跨平台图像浏览系统,要求该 系统能够显示BMP、JPG、GIF、PNG等多种格式的文 件,并且能够在Windows、Linux、Unix等多个操作 系统上运行。系统首先将各种格式的文件解析为像素 矩阵(Matrix),然后将像素矩阵显示在屏幕上,在不 同的操作系统中可以调用不同的绘制函数来绘制像素 矩阵。
简单工厂模式
简单工厂模式的应用实例
Chart
实例类图
HistogramChart
+ Display () : void ...
ChartFactory + GetChart (string type) : Chart ...
<<create>>
LineChart + LineChart () + Display () : void ...
简单工厂模式的应用实例
实例说明
某软件公司要基于 C#语言开发一套图表库,该图表库可以为应用系统 提 供 多 种 不 同 外 观 的 图 表 , 例 如 柱 状 图 (HistogramChart) 、 饼 状 图 (PieChart) 、折线图 (LineChart) 等。该软件公司图表库设计人员希望为应 用系统开发人员提供一套灵活易用的图表库,通过设置不同的参数即可 得到不同类型的图表,而且可以较为方便地对图表库进行扩展,以便能 够在将来增加一些新类型的图表。 现使用简单工厂模式来设计该图表库。
简单工厂模式的结构与实现
简单工厂模式的实现
典型的抽象产品类代码:
abstract class Product { //所有产品类的公共业务方法 public void MethodSame() { //公共方法的实现 } //声明抽象业务方法 public abstract void MethodDiff(); }
PowerBuilder9.0教程分析
软件开发工具_pb_自学指导孙勇
第一章 PowerBuilder简介
一、目的要求
1.本课程在软件课程中的作用;
2.信息系统的开发模式;
3.掌握PowerBuilder的主要特性;
4.初步掌握PowerBuilder开发环境;
5.了解应用程序开发步骤;
6.熟悉PowerBuilder的安装方法。
二、重点难点
重点是PowerBuilder开发环境。
第一章PowerBuilder简介
本章主要内容:本章中,首先扼要地介绍本课程在软件课程中的作用和信息系统的开发模式 PowerBuilder的主要特性,然后阐述集成开发环境的组成、各画板的功能、作用、定制开发环境的方法、对象的基本概念等,并在章尾介绍本课程的教学内容以及开发PowerBuilder应用程序的基本步骤。
自学内容:
1.1本课程在软件课程中的作用
在计算机专业中,软件教学占有非常重要的位置。为了满足计算机职业岗位群的需要,近年来,我们对计算机网络专业和计算机程序员专业中的软件课程进行了相应的改革。通过学习Word、Excel、FrontPage等常用软件使学生学会计算机的使用方法;通过学习C语言程序设计、数据结构等课程使学生掌握基本的程序设计方法;通过学习SQL Server使学生掌握网络数据库的设计方法和使用方法;通过学习网页制作和Java语言使学生能够编写网站应用程序;通过学习PB使学生能够编写客户端应用程序;通过学习软件工程使学生能够懂得软件开发的全过程,掌握编写软件的规范,为学生在“软件工厂”从事软件工作打下基础。从以上分析可知,PB课程属于与职业岗位群直接有关的课程,在整个软件课程体系中占有比较重要的位置。
常见设计模式的解析和实现(C++)整理版
public: AbstractProductB(){} virtual ~AbstractProductB(){}
}; //派生类ConcreateProductB1,继承自AbstractProductB,代表产品B的第一种实现 class ConcreateProductB1:public AbstractProductB { public:
//生产产品B的第二种实现 AbstractFactory *cf2 = new ConcreteFactory2(); cf2->CreateProductB();
delete cf1; delete cf2; return 0; }
常见设计模式的解析和实现(C++)之三-Builder 模式
作用: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
virtual AbstractProductA* CreateProductA()=0; virtual AbstractProductB* CreateProductB()=0; }; // 派生类 ConcreateFactoryl,继承自 AbstractFactory,生产产品A和产品B的第一种实现class ConcreteFactory1:public AbstractFactory { public: ConcreteFactory1() { cout<<"ConcreteFactory1"<<endl; } virtual ~ConcreteFactory1(){}
Builder模式在Java中的应用
Builder模式在Java中的应用在Java编程中,Builder模式是一种常见的设计模式。它的目
的是将一个复杂的对象的构造与其表示分离开来,使得同样的构
造过程可以创建不同的表示。在本文中,我们将讨论Builder模式
在Java中的应用,以及它的优点和使用注意事项。
一、Builder模式的概念
Builder模式属于创建型模式,它的作用是将一个类的构造方法
与其创建对象的过程分离开来,从而使得一个类可以有多种表示
方法。在Builder模式中,对象的构造器被视为一系列的步骤,每
个步骤可以用一个Builder对象来代表。每个Builder对象都依次
执行构造过程的一个步骤,例如设置对象的属性和调用其他对象
的构造函数。最终,Builder对象将产生一个完整的、可用的对象。
二、Builder模式的应用
在Java编程中,Builder模式可以用于创建复杂的对象,例如
一个复杂的数据库连接(connection)、一个完整的电子邮件(email)等等。Builder模式可以将创建这样的对象的过程分成多
个步骤,而且每个步骤的顺序和内容都可以根据实际需求来设计。这样可以大大提高代码的可读性和可维护性。
以一个简单的StringBuilder为例,来说明Builder模式的应用。StringBuilder是一个字符串缓冲区,它可以动态地添加、删除、修改和查询其内容。StringBuilder对象的创建可以分为三个步骤:
首先,创建一个StringBuilder对象。
其次,向缓冲区中添加需要的内容。
最后,获取缓冲区中的内容。
c++builder触发事件介绍
C++builder出发事件介绍
C++ Builder 初学问与答(二十)收藏
(版权要求:请务必尊重知识产权,未经授权不要转载、摘录)
17 事件及事件响应
16)问:从前面的编程过程可以看出,用C++Builder开发应用程序的一个特点是,大部分的编程工作是在响应事件和处理事件上。事件的产生可能来自于系统,也可能来自于用户。您能给我详细介绍一下事件吗?
答:好的,这节课我们将对事件进行深入地探讨,并重点讲述常用的鼠标和键盘事件,以及这些事件的事件处理过程。
那么什么是事件呢?
事件是一种把发生的动作与代码相连的机制。更精确地讲,事件是一个方法指针,它指向某个类实例的方法。
对于应用程序开发人员来说,一个事件只是一个与系统动作相关的名字,例如鼠标单击事件OnClick,这个名字与一段代码相连。
17)问:这个问题您能说具体一点吗?
答:一个名为Button1的按钮有一个OnClick方法。在缺省情况下,C++Builder会在包含这个按钮的窗体中产生一个名为Button1Click的事件处理过程,并把这个过程赋给OnClick。当按钮发生OnClick事件时,按钮就会调用这个赋给OnClick事件的方法,也就是Button1Click。
18)问:我编写了一个程序,菜单File中有一个打开文件菜单命令,而工具条上经常也有一个打开文件的加速按钮。我是不是要将这个程序编写两次呢?
答:不需要,在某些情况下,可能需要用一个事件处理过程响应几个不同的事件。这时只需要将几个事件共用一个事件处理过程就可以了。
19)问:怎样实现把一个已有的事件处理过程同另外一个事件相连呢?
《PowerBuilder 程序设计基础》第6章:PowerScript编程
数组及使用-2
一维数组: 一维数组 string ls_array_1 [] //不定长数组 string ls_array_2 [10] // 从 ls_array[1] 到 ls_array[10] 共10个单元 string ls_array_3 [ 5 to 24] // 从 ls_array[5] 到 ls_array [24] 共10个单元 多维数组: 多维数组 string ls_array1[100,200] //定义一个 100 * 200 的数组,共20000个单元 string ls_array2[1-100 , -2 to 97] // 定义一个 100 * 100 的数组。 数组及数组间的操作: 数组及数组间的操作 上界及下界: UpperBound(ls_my_array_1) ,LowerBound(ls_my_array_3)) 数组是否一致 =,<> 数组变量类型可以是任意类型,包括窗口,菜单,自定义对象,控件等等
常用函数和语句-文件操作
文件操作在程序设计中是不可缺少的。可以用文件函数来打开一个文本 或Blob文件并对它进行读写操作。可以用流模式(Stream!) 或行(Line!) 模式 打开一个文件。在行模式中,文件每次被一行行的读入,每行之间被回车符 和换行符隔开,最后以EOF(文件结束)为结束标志。在流模式中,文件 被整个读入,并且换行符和回车符不被写入文件。用户一般用流模式将整个 文件读入到MultilineEditor中,再整个写到文件中去。 当打开文件时,PowerBuilder分给打开的文件一个独特的整数并且将 指针放在文件中你所规定的地方。你可以用这个整数来标识文件以便读写或 关闭文件。文件指针规定了下一次文件读写的起始位置并且在每次读写完成 后由PowerBuilder自动更改。文件函数如下:
powerbuilder 正则表达式
正则表达式在PowerBuilder中的应用
随着信息技术的发展,软件开发技术也在不断更新换代。PowerBuilder作为一种老牌的可视化开发工具,自问世以来,一直备受开发者的青睐。在PowerBuilder中,正则表达式作为一种强大的文本处理工具,也被广泛应用于字符串匹配、替换和提取等功能。本文将重点介绍正则表达式在PowerBuilder中的应用,并且探讨在实际开发中如何更好地利用正则表达式来提高开发效率。
一、正则表达式的基本概念
正则表达式是一种用来描述、匹配字符串的方法。它是由普通字符以及特殊字符(元字符)组成的字符串。通过使用正则表达式,我们可以轻松地检测一个字符串是否符合某种模式、获取匹配的字符串等操作。
在PowerBuilder中,我们经常会用到一些常用的正则表达式元字符,比如:
1. ^:匹配输入字符串的开始位置。
2. $:匹配输入字符串的结束位置。
3. .:匹配任意单个字符。
4. *:匹配前面的子表达式零次或多次。
5. +:匹配前面的子表达式一次或多次。
6. ?:匹配前面的子表达式零次或一次。
7. []:匹配中括号内的任意一个字符。
8. ():标记一个子表达式的开始和结束位置。
二、PowerBuilder中的正则表达式函数
PowerBuilder提供了一系列用于处理正则表达式的函数,开发者可以通过这些函数实现对字符串的匹配、替换和提取等操作。其中,最常用的函数包括:
1. Match():用于判断字符串是否与正则表达式匹配。
2. Replace():用于对字符串中与正则表达式匹配的部分进行替换。
java的Builder原理和实现详解
java的Builder原理和实现详解
⾸先给⼀个简单的Builder设计模式的例⼦:
主实现类代码如下:
/**
* 实体类包含⼀个静态内部类 Builder
*/
public class CompanyClient {
public String companyName;
// ⽤final修饰的成员变量表⽰常量,只能被赋值⼀次,赋值后值⽆法改变! final修饰的变量有三种:静态变量、实例变量和局部变量。 public String companyAddress;
public double companyRegfunds;
public String mPerson;
public String mType;
//构造⽅法
public CompanyClient() {
this(new Builder());
}
//构造⽅法
public CompanyClient(Builder builder){
panyName = panyName;
panyAddress = panyAddress;
panyRegfunds = panyRegfunds;
this.mPerson = builder.person;
this.mType = builder.type;
}
public String getCompanyName() {
return companyName;
}
public String getCompanyAddress() {
return companyAddress;
}
public double getCompanyRegfunds() {
java 对象 builder实例化方式
在Java中,对象可以通过多种方式进行实例化,包括使用传统的构造函数、使用静态工厂方法、使用实例工厂方法,以及使用Builder 模式。
Builder模式是一种常用的设计模式,它提供了一种构建对象的最佳方式。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
以下是一个简单的Java Builder模式的示例:
```java
public class Person {
private String name;
private int age;
private String address;
private Person(PersonBuilder builder) {
= ;
this.age = builder.age;
this.address = builder.address;
}
public static class PersonBuilder {
private String name;
private int age;
private String address;
public PersonBuilder withName(String name) {
= name;
return this;
}
public PersonBuilder withAge(int age) {
this.age = age;
return this;
}
public PersonBuilder withAddress(String address) {
this.address = address;
return this;
C10-DSP Builder设计初步
6
DSP微处理器
DSP(digital signal processor)是一种独特的微处
理器,是以数字信号来处理大量信息的器件。
DSP优点
对元件值的容限不敏感,受温度、环境等外部因素影响小;容易 实现集成;可以分时复用,共享处理器;方便调整处理器的系数 实现自适应滤波;可实现模拟处理不能实现的功能:线性相位、 多抽样率处理、级联、易于存储等;可用于频率非常低的信号。
供了MATLAB和DSP目标板的接口,利用此工具可以MATLAB 环 境下完成对DSP的操作,从而极大地提高DSP 应用系统的开发进 程。
本章介绍的是Altera公司于2002年推出的DSP Builder,主要完成
基于FPGA的应用系统设计。
以图形化的系统建模,可以自动化的完成大部分的设计和仿真,
图9-1 基于Matlab、DSP Builder、 12 QuartusII等工具
9.1 Matlab/DSP BuБайду номын сангаасlder及其设计流程
依据应用目标的不同要求,开发人员的熟练程度差异, DSP Builder 提供两套设计流程,即: 自动流程:
1、MATLAB/Simulink建模; 2、系统仿真; 3、DSP Builder完成VHDL转换、 综合、适配、下载;
像识别、机器人视觉、多媒体、电子地图、图像增强等。 军事:保密通信、声纳处理、导航、全球定位等。 仪器仪表:频谱分析、函数发生、数据采集、地震处理等 自动控制:深空作业、自动驾驶、机器人控制等。 医疗:助听、超声设备、诊断工具、病人监护、心电图等。 家用电器:数字电视、可视电话、音乐合成等。 生物医学信号处理:CT图像重建、心电信号分析等。
C#的23种设计模式简要介绍
1、FACTORY—工厂模式:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改。如:如何创建及如何向客户端提供。2、BUILDER—建造模式:将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的变化,客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。3、FACTORY METHOD—工厂方法模式:核心工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。4、PROTOTYPE—原始模型模式:通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或减少产品类,产品类不需要非得有任何事先确定的等级结构,原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法。5、SINGLETON—单例模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可使用。结构型模式6、ADAPTER—适配器(变压器)模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端。7、BRIDGE—桥梁模式:将抽象化与实现化脱耦,使得二者可以独立的变化,也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。8、COMPOSITE—合成模式:合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式就是一个处理对象的树结构的模式。合成模式把部分与整体的关系用树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。9、DECORATOR—装饰模式:装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增加功能,这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。10、FACADE—门面模式:外部与一个子系统
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Builder模式实例分析(C语言版)Builder模式实例分析(C语言版)
Builder模式实例分析(C语言版)
转载时请注明出处:/absurd
设计模式、设计模式还是设计模式,设计模式已经被许多高手讲过了无数遍了。本来我无意再去重复被人重复过无数遍的工作,但按照我们的培训计划,现在该讲设计模式了,作为培训计划的制定者,我不能不贡献一点力量,所以最终决定写几篇关于设计模式的BLOG。本文的主题是Builder模式。
Builder模式是一个很常用模式。按照《设计模式》一书所说,它的意图是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。这句话有点深奥,构建、创建和表示,这些术语往往让新手摸不着头脑。要解释这些术语恐怕有困难,算了,还是让我们来打个比方吧:建筑物是一个复杂对象,砖头、钢筋和水泥是建筑的基本材料,生产建筑材料的过程就是所谓的构建,修建建筑物的过程就是所谓的创建,建筑物的风格和种类就是所谓的表示。在这里,说白了,builder模式就是让制砖厂和建筑公司分开,由建筑公司决定建筑物的风格和种类,让同样的砖头可以修建不同的建筑物。
我们再来看一个实际一点例子:XML是一种广泛使用的文件格式,和其它任何文件格式一样,它本身只是一个容器。你可以用它来存放配置文件(如gconf),可以用它存放多媒体文件(如SMIL),也可以用它存放Office文档(如OpenOffice)。XML只表示语法上的内容,而不表示语义上的内容,所以你可以用它存放任何数据。操作XML最正规的方式是文档对象模型(DOM),不过这个东西太复杂了,除非为了做XSLT转换,否则很少有人用它。
操作XML文件最简单的方式是SAX(Simple API for XML)。SAX方式采用了典型的Builder模式,它把XML的解析和创建表示两个过程分开了,并且只负责解析过程,由用户自己负责创建表示的过程。如果用SAX方式解析XML文件,并生成一棵DOM树,那么解析的过程就是前面所说的构建过程,而生成DOM树的过程就是创建表示的过程。为了避免在术语上绕来绕去,在后面的例子中,我干脆用解析和创建两个词来代替它们。
XML文件的结构是递归的,它由tag和文本等基本元素组成。tag有起始tag(如),结束tag(如)和起始/结束tag(如
出某个tag的属性,而有的Builder可能会把OpenOffice文档转换成PDF文档。总之,Builder可以实现不同的功能,但解析器始终是被重用的。
从SAX例子中,我们可以看出:采用Builder模式,可以重用解析器部分的代码,把变化的部分隔离到Builder里,这是从代码重用和隔离变化的角度考虑的。另外一方面,分离解析和创建两个过程,实际上也简化了问题的复杂度,解析器部分只负责解析,实现起来更加容易。而且创建者Builder更了解自己的目标,会做得更专业。
Builder的模式的结构:
在上面的SAX例子中,Director就是解析器(Parser),而Builder是SAX要求提供的一个抽象接口,ConcreteBuilder是Builder接口的具体实现。比如某个ConcreteBuilder的功能可能是创建一棵DOM树,另外一个ConcreteBuilder的功能可能是把OpenOffice文档转换为PDF文档,总之,Director不关心Builder的具体实现。
Builder的模式的交互过程:
在上面的SAX示例中,expat实现了一个XML解析器,当它解析到起始TAG时就调用Builder的XML_StartElementHandler函数,解析到一个结束TAG时就调用Builder的XML_EndElementHandler函数,解析到其它元素时调用Builder其它相应的函数,直到解析完成为止。注意这些函数是由ConcreteBuilder实现的。
下面我们以一个歌词解析器为例进行更详细的讲解:
1. 歌词(lrc)文件介绍:
歌词(lrc)文件是一种文本文件,它用来描述歌曲的歌词。在歌词(lrc)文件的帮助下,播放器可以同步显示歌词。歌词(lrc)文件的格式很简单,它由时间标签、ID标签和歌词组成。
时间标签,如:[02:42.91][00:58.78]
ID标签,如:[ar:Whitney Houston]
歌词,如:I don’t really need to look very much further
下面是一个完整的歌词文件:
[al:]
[ar:Whitney Houston]
[ti:i have nothing(一无所有)]
I have nothing
演唱:Whitney Houston
专辑:
[00:20.95]Share my life, take me for what I am
[00:30.47]Cause I’ll never change all my colours for you
[00:40.16]Take my love, I’ll never ask for too much
[00:49.09]Just all that you are and everything that you do
[02:42.91][00:58.78]I don’t really need to look very much further
[02:47.74][01:03.69]I don’t want to have to go where you don’t follow
[02:52.37][01:08.25]I won’t hold it back again, this passion inside
[02:56.62][01:12.40]Can’t run from myself
[02:59.05][01:15.18]There’s nowhere to hide
[03:03.02](Your love I’ll remember forever)
[03:39.61][03:09.02][01:19.98]Don’t make me close one more door
[03:46.85][03:13.73][01:24.92]I don’t wanna hurt anymore
[03:51.05][03:17.90][01:29.04]Stay in my arms if you dare
[03:56.01][03:22.98][01:34.29]Or must I imagine you there
[04:09.89][04:06.04][04:00.35][03:27.61][01:38.77]Don’t walk away from me...
[04:15.73][03:32.56][01:43.95]I have nothing, nothing, nothing
[04:21.27][01:49.16]If I don’t have you, you
[01:59.65]you ,you, you
[04:30.64]If I don’t have you
[02:05.71]You see through, right to the heart of me
[02:14.49]You break down my walls with the strength of you love
[02:24.11]I never knew love like I’ve known it with you
[02:32.69]Will a memory survive, one I can hold on to
[04:36.33]Oh~~
2. Builder的接口定义:
struct _LrcBuilder;
typedef struct _LrcBuilder LrcBuilder;
typedef LRC_RESULT (*LrcBuilderBegin)(LrcBuilder* thiz, const char* buffer);
typedef LRC_RESULT (*LrcBuilderOnIDTag)(LrcBuilder* thiz, const char* key, size_t key_length,
const char* value, size_t value_length);
typedef LRC_RESULT (*LrcBuilderOnTimeTag)(LrcBuilder* thiz, size_t start_time);
typedef LRC_RESULT (*LrcBuilderOnLrc)(LrcBuilder* thiz, const char* lrc, size_t lrc_length);
typedef LRC_RESULT (*LrcBuilderEnd)(LrcBuilder* thiz);
typedef LRC_RESULT (*LrcBuilderDestroy)(LrcBuilder* thiz);
struct _LrcBuilder
{
LrcBuilderBegin on_begin;
LrcBuilderOnIDTag on_id_tag;
LrcBuilderOnTimeTag on_time_tag;
LrcBuilderOnLrc on_lrc;
LrcBuilderEnd on_end;
LrcBuilderDestroy destroy;
char priv[1];
};
l on_begin: 在解析之前调用,以便builder做些初始化工作。
l on_id_tag:解析到ID标签时调用。
l on_time_tag:解析到时间标签时调用。
l on_lrc:解析到歌词时调用。
l on_end:解析完成时调用。
l destroy:销毁builder时调用。
3. 两个builder的实现。
我们实现了两个builder:
l dumpbuilder: 直接把解析的内容打印到屏幕上。它的功能有两个:一是用于调试目的,可以查看解析器工作是否正常。二是美化lrc文件,把排版比较乱的lrc文件,以正规的格式输出。它的主要代码如下:
static LRC_RESULT lrc_dump_builder_on_time_tag(LrcBuilder* thiz, size_t start_time)
{
int min = start_time / 6000;
int sec = start_time % 6000;
int per_sec = sec % 100;
sec = sec / 100;
LrcDumpBuilder* data = (LrcDumpBuilder*)thiz->priv;
fprintf(data->fp, "[%d:%02d.%02d]", min, sec, per_sec);
return LRC_RESULT_OK;
}
static LRC_RESULT lrc_dump_builder_on_lrc(LrcBuilder* thiz, const char* lrc, size_t lrc_length)
{
LrcDumpBuilder* data = (LrcDumpBuilder*)thiz->priv;
fwrite(lrc, lrc_length, 1, data->fp);
fprintf(data->fp, "/n");
return LRC_RESULT_OK;
}
LrcBuilder* lrc_dump_builder_new(FILE* fp)
{
LrcDumpBuilder* data = NULL;
LrcBuilder* thiz = (LrcBuilder*)calloc(sizeof(LrcBuilder) + sizeof(LrcDumpBuilder), 1);
if(thiz != NULL)
{
thiz->on_begin = lrc_dump_builder_on_begin;
thiz->on_id_tag = lrc_dump_builder_on_id_tag;
thiz->on_time_tag = lrc_dump_builder_on_time_tag;
thiz->on_lrc = lrc_dump_builder_on_lrc;
thiz->on_end = lrc_dump_builder_on_end;
thiz->destroy = lrc_dump_builder_destroy;
data = (LrcDumpBuilder*)thiz->priv;
data->fp = fp != NULL ? fp : stdout;
}
return thiz;
}
defaultbuilder: 是默认的builder,它负责把lrc文件构建成内存中的结构,以便可以方便查询。它的主要代码如下:
static LRC_RESULT lrc_default_builder_on_time_tag(LrcBuilder* thiz, size_t start_time)
{
LrcDefaultBuilder* data = NULL;
LRC_ASSERT(thiz != NULL);
if(thiz != NULL)
{
LrcTimeTag* time_tag = NULL;
LrcListIter iter = {0};
data = (LrcDefaultBuilder*)thiz->priv;
time_tag = lrc_time_tag_new(data->time_tag_pool, start_time, NULL);
iter = lrc_list_first(data->temp_time_tags);
lrc_list_insert(&iter, time_tag, 0);
}
return LRC_RESULT_OK;
}
static LRC_RESULT lrc_default_builder_on_lrc(LrcBuilder* thiz, const char* lrc, size_t lrc_length)
{
LrcDefaultBuilder* data = NULL;
LRC_ASSERT(thiz != NULL && lrc != NULL);
if(thiz != NULL && lrc != NULL)
{
char* new_lrc = NULL;
LrcListIter iter = {0};
LrcTimeTag* time_tag = NULL;
data = (LrcDefaultBuilder*)thiz->priv;
iter = lrc_list_first(data->temp_time_tags);
new_lrc = lrc_strdup(data, lrc, lrc_length);
while(!lrc_list_iter_is_null(&iter))
{
time_tag = (LrcTimeTag*)lrc_list_iter_data(&iter);
lrc_time_tag_set_lrc(time_tag, new_lrc);
lrc_tree_add_time_tag
(data->tree, time_tag);
iter = lrc_list_iter_next(&iter);
}
}
lrc_list_reset(data->temp_time_tags);
return LRC_RESULT_OK;
}
LrcBuilder* lrc_default_builder_new(void)
{
LrcDefaultBuilder* data = NULL;
LrcBuilder* thiz = (LrcBuilder*)calloc(sizeof(LrcBuilder) + sizeof(LrcDefaultBuilder), 1);
if(thiz != NULL)
{
thiz->on_begin = lrc_default_builder_on_begin;
thiz->on_id_tag = lrc_default_builder_on_id_tag;
thiz->on_time_tag = lrc_default_builder_on_time_tag;
thiz->on_lrc = lrc_default_builder_on_lrc;
thiz->on_end = lrc_default_builder_on_end;
thiz->destroy = lrc_default_builder_destroy;
data = (LrcDefaultBuilder*)thiz->priv;
}
return thiz;
}
4. Product。
所谓Product就是ConcreteBuilder产生的结果,不同的ConcreteBuilder所产生的Product是不相同的。
在我们的例子中,dumpbuilder产生的Product是一段文本。而defaultbuilder产生的Product是一个数据结构。如下:
struct _LrcIdTag
{
const char* key;
const char* value;
};
struct _LrcTimeTag
{
size_t start_time;
size_t pause_time;
size_t repeat_times;
const char* lrc;
};
struct _LrcTree
{
LrcList* id_tags;
LrcList* time_tags;
};
5. 解析器(Director)的工作过程。
解析器的任务就是解析出lrc最基本的元素:时间标签、ID标签和歌词,然后调用builder相应的函数。
static LRC_RESULT lrc_parser_parse_tag(LrcParser* thiz)
{
const char* p = thiz->p;
skip_space(p);
if(*p >= '0' && *p <= '9')
{
lrc_parser_parse_time_tag(thiz);
}
else
{
lrc_parser_parse_id_tag(thiz);
}
return LRC_RESULT_OK;
}
static LRC_RESULT lrc_parser_parse_id_tag(LrcParser* thiz)
{
const char* key = NULL;
const char* value = NULL;
size_t key_length = 0;
size_t value_length = 0;
if(lrc_parser_parse_pair(thiz, &key, &key_length, &value, &value_length) == LRC_RESULT_OK)
{
if(thiz->builder->on_id_tag != NULL)
{
thiz->b
uilder->on_id_tag(thiz->builder, key, key_length, value, value_length);
}
}
return LRC_RESULT_OK;
}
static LRC_RESULT lrc_parser_parse_time_tag(LrcParser* thiz)
{
float seconds = 0;
char number[32] = {0};
size_t start_time = 0;
const char* key = NULL;
const char* value = NULL;
size_t key_length = 0;
size_t value_length = 0;
if(lrc_parser_parse_pair(thiz, &key, &key_length, &value, &value_length) == LRC_RESULT_OK)
{
key_length = key_length < 20 ? key_length : 20;
strncpy(number, key, key_length);
start_time = atoi(number) * 60 * 100;
value_length = value_length < 20 ? value_length : 20;
strncpy(number, value, value_length);
sscanf(number, "%f", &seconds);
start_time += seconds * 100;
if(thiz->builder->on_time_tag != NULL)
{
thiz->builder->on_time_tag(thiz->builder, start_time);
}
}
return LRC_RESULT_OK;
}
static LRC_RESULT lrc_parser_parse(LrcParser* thiz)
{
LRC_RESULT ret = LRC_RESULT_OK;
const char* p = thiz->p;
if(thiz->builder->on_begin != NULL)
{
thiz->builder->on_begin(thiz->builder, p);
}
while(*p != '/0')
{
if(*p == '[')
{
thiz->p = ++p;
skip_space(thiz->p);
lrc_parser_parse_tag(thiz);
}
else if(*p == '/n' || *p == '/r')
{
skip_to_next_line(thiz->p);
}
else if(*p == ' ' || *p == '/t')
{
skip_space(thiz->p);
}
else if(*p != ']')
{
thiz->p = p;
lrc_parser_parse_lrc(thiz);
}
else
{
++thiz->p;
}
p = thiz->p;
}
if(thiz->builder->on_end != NULL)
{
thiz->builder->on_end(thiz->builder);
}
return ret;
}
6. 调用者(client)
有了Parser和相应的Builder,调用者需要把它们组合起来,这个过程很简单。在解析完成时,调用者还希望从Builder取出Product,以便后面使用。如:
LrcParser* lrc_parser_new_from_file(const char* filename)
{
LrcParser* thiz = NULL;
char* buffer = read_file(filename);
if(buffer != NULL)
{
thiz = lrc_parser_new(buffer);
if(thiz != NULL)
{
thiz->owner_buffer = 1;
}
}
return thiz;
}
static Lrc* lrc_parse(LrcParser* parser)
{
Lrc* thiz = NULL;
if(parser != NULL)
{
LrcBuilder* builder = (LrcBuilder*)lrc_default_builder_new();
lrc_parser_run(parser, builder);
thiz = (Lrc*)lrc_default_builder_get_tree(builder);
builder->destroy(builder);
lrc_parser_destroy(parser);
}
return thiz;
}
附:
lrc解析器完整的代码可以到这里下载。
~~end~~