设计模式/软件设计模式Oesign pattern)是软件设计过程中的经验总结。可 以让程序代码易重用、易理解、更稳定。目前总结出来的模式有23种之多,文中主要涉及 “工厂模式”在多串口通信软件开发中的应用。作者针对衡器称重系统中常用的仪表、一机一
秤、一机多秤等应用场景,提出相应的解决方案。
1.引言
在软件开发过程中,常常会碰到相似问题, 做重复的工作。如何提高效率,保证代码的可靠 性,可重用性昵?前人总结出了很多软件开发模 式,来解决相应的问题。文中将针对“软件设计 模式”一“工厂模式”在多串口通信软件开发中 的应用展开描述。
2.设计模式简介
设计模式(Oesign patten)是一套被反复使用 的、多数人知晓的、经过分类编目的、代码设计 经验的总结。使用设计模式是为了重用代码、让 代码更容易被他人理解、保证代码可靠性。最早 提出设计模式思想是在1994年,由Erich Gamma 等人,在其所著的《面象对象的可复用元素》一 书中提出的。总结出三大类别创建型模式、结 构型模式、行为型模式,六大原则(开闭原则、 里氏原则、依赖倒置原则、接口隔离原则、迪米 特法则、合成复用原则,23种设计模式俱体内 容请参看相关书籍。本文将重点介绍:工厂设计 模式。它属于“创建型模式”,即在创建对象的过 程中,隐藏创建逻辑,这使得程序在创建给定实 例对象时,可以提供灵活的判断。
3.衡器软件中的问题
作者长期从事衡器称重软件的设计。在长期 的工作经历中,参与了很多项目,其中多数都会 用到串口通信。会遇到以下几种典型的应用问题:
(1)在使用衡器仪表时,串口是常见的对外通 信方式。称重仪表厂家多,型号不一,通信格式 各不相同。能不能使用统一的开发模式,进行管 理,减少重复性的工作昵?
(2)地磅称重管理中,有“一机一秤”,也有 机多秤”的模式。在多秤同时使用时,仪表型 号可能相同,也可能不同,有没有简洁、高效的 扩展软件代码的方法昵?
4.工厂设计模式
针对上述问题,作者通过实践和总结,觉得软件开发模式的中的“工厂模式”比较适合解决 这类问题。
什么是“工厂模式”?顾名思义,就是客户提 交“订单”,交给“工厂”生产,最后得到“产 品”。“工厂模式”模拟了这个过程。“工厂模 式”分为三种子类别:简单工厂、工厂方法、抽 象工厂。这三种方法,本质相同,但各有其应用 场景,简单来说,如下:
(1)简单工厂:一个工厂生产多种不同产品。
(2)工厂方法:多个工厂,每个工厂生产一种 产品。是对“简单工厂”的改进,因为“简单工 厂”不符合“开闭原则”。
(3)抽象工厂:多个工厂,每个工厂生产一种 或多种产品。可以根据“订单”需求,生产“产 品”。“订单”中可能存在多种不同的“产品”需 求,可以交给不同的“工厂”来“生产”。
这三种模式都能解决衡器软件中的仪表串口 通信问题。文中重点讲解“简单工厂”和“抽象 工厂”这两种模式,这两种模式比较容易理解, 其它的模式也可依此类推。
5.面向对象的实现过程
设计模式的运用与编程语言无关。作者以常 用的C++语言为例,说明面向对象的封装过程。
首先,作者封装了一组串口通信类,将串口 通信过程中,常用的的方法,封装在一个类族里, 为后期模式设计提供基础如图1所示。
串口类库可根据需要不断扩展,封装好后, 与下文的设计模式类隔离,减少代码耦合。这个 类族中的底层子类,相当于“工厂”里生产的产品
(1)简单工厂的实现过程
简单工厂模式如图2所示,将“产品”实例的创建放到工厂方法中完成,从而为“产品”的选择提供灵活性。这里的“产品”就是仪表型号,工厂就是 TMeterFactory 类,其方法 CreateMeter 可以创建各种仪表的实例。
主要部分的伪代码如下:
#include
classTM eterfactory//仪表工厂类
{
Private:
m ap
TM eterfactory0;
ICommO bject*C reateM eter (char*strM eter- Name);//创建仪表实例方法
~TM eterFactoryO;//销毁仪表类实例
};
ICommObject*TM eterFactory::CreateM eter (char*strM eterN am e)
{ //检查仪表实例是否存在 ifm _m eter.end )!=m _m eter.find (strM eterN am e)) return m_m eter.find strM eterN am e)->second; //创建新的仪表实例 ICommO bject*pM eter=N ULL; if(!strcm p "X K 3190A 9",strM eterN am e)) pM eter=new TXK3190A9 0; else return NULL;
...//继续添加代码,创建其他仪表型号 m_meter.insert std ^:pa ir
}
客户端调用代码如下: charweight[10]={0};
TMeterFactory*pFactory=new TMeterFactory;
ICommObject*pMeter=pFactory->CreateMe-
ter(“XK3190A9”);
strcpy(weight,pMeter->read());// 读取重量,中间省略 open,write,close 等方法代码
上述过程中,可以看到:在使用简单工厂模式 (或其它模式) 时,并不一定能减少代码量,但是易于后期扩展和理解。从客户端的调用代码可以看出:模式提供了统一的接口,调用者只要给出仪表型号,就能得到相应的类实例。实例创建过程封装到其他代码块中,只要那部分代码没有问题,调用者就能得到正确的结果,相当于黑盒操作。这种方式,适合多人分工合作,共同开发软件。
(2) 抽象工厂的实现过程
“简单工厂”模式适合“一机一秤”的称重软件开发。“抽象工厂”模式适合解决“一机多秤”的称重模式。他将仪表实例创建过程推迟到子类中完成,从而为“订单”中的多种“产品”组合提供机会。以下是“一机一秤”和“一机两秤”类图如图 3 所示。
由上图可以看出,每一个“工厂”代表一种称重模式,可以从“一机一秤”,扩展到“一机 n 秤”。而每一种称重模式中,可以用同一种型号的仪表,也可以用不同种型号的仪表组成系统。具
体实施时,可根据需求选择。C++ 伪代码如下: class ICommObject;
class IFactory // 抽象工厂
{
public:
virtual ICommObject * CreateMeter1()=0; // 创建仪表 1,此处为纯虚方法
virtual ICommObject * CreateMeter2 ()=0; // 创建仪表 2
// 可以继续添加 protected:
IFactroy();
};
Class TOne_pc_and_one_scale:public IFactory// 具体工厂:实现一机一秤功能
{
public:
ICommObject * CreateMeter1 () {return new TXK3190A9;}
};
Class TOne_pc_and_two_scale:public IFactroy// 具体工厂:实现一机两秤功能
{
public:
ICommObject * CreateMeter1(){return new
T8142pro;}
ICommObject * CreateMeter2(){return new
XK3180;}
};
客户端调用代码如下: // 一机一秤调用方法
char weight_A9[10]={0};// 重量字符串
IFactory * pScale=new TOne_pc_and_one_scale;
ICommObject * pA9=pScale->CreateMeter1();
strcpy(weight,pA9->read());// 读取重量到 weight
中,中间省略 open,write,close 等方法代码
// 一机两秤调用方法 char weight_8142[10]={0},weight_3180[10]={0};//
重量字符串
IFactory * pScale=new TOne_pc_and_two_scale; // 创建工厂实例
ICommObject * p3180=pScale->CreateMeter1(); ICommObject * p8142=pScale->CreateMeter2(); strcpy (weight_3180,p3180->read ());// 读取重量
到 weight_A9 中,中间省略 open,write,close 等方法代码
strcpy (weight_8142,p8142->read ()); // 读取重量到 weight_814 中
使用“抽象工厂”模式,在添加新的“工厂”时,并不影响已有的“工厂”代码,已有的代码基本不需改动,提高了程序的稳定性,符合“开
闭原则”,因此成为 23 种软件设计模式之一。
6.工厂模式在称重传感器温度测试软件中的应用
作者在编写 《传感器温度测试软件》 的过程中,使用了数据采集板,每个板上有一个串口,多块板同时向上位机发送传感器的实时温度测量数据。利用“抽象工厂设计”模式,很好的完成了软件编写。原来在其他软件中的通信代码 (即图 1),移植过来使用,进行部分扩展,实现了代码重用。软件可以对每一个串口的通信数据和通信参数,进行单独的管理。如下图:
7.相关问题讨论
在 Windows 系统中,某个“串口”设备,在某一时刻,只能被一个软件使用 (虚拟串口设备除外)。多个进程或线程同时使用,会产生冲突。冲突的情况分为三种:
(1) 两个 (或两个以上) 不同的软件,同时打开同一个串口
(2) 同一个软件,运行两次实例 (或两次以上) 在内存中,同时打开同一个串口
(3) 同一个软件,运行一次,但在软件内部两次(或两次以上) 打开同一个串口
①、②两种情况涉及不同进程之间的资源抢占的冲突,可以考虑用进程互斥的方法解决,不属于软件模式的解决范畴。
对于第③种情况,可用“单例模式”解决软件内部多次、同时读、写同一个串口冲突的问题。
“单例模式”的目标就是:每个类只能创建一个实例。类的“构造函数”和“拷贝构造函数”设为“私有”或“保护”权限,类实例由其静态成员函数创建。
文中的“工厂模式”也涉及到串口的调用,因此可能有人会问:“工厂模式”能否结合“单例模式”同时使用呢?答案是否定的。“单例模式”封装的对象是“串口”硬件实体。本文中的“工厂模式”是以“称重仪表”为对象,进行封装。两者的出发点和角度不同,虽然都用到了串口通信,但是功能需求不同。物理串口,同一时刻,只能被一个实例使用,因此用“单例模式”比较合理。而“称重仪表”类,则存在多台相同型号的仪表同时使用的情况,所以需要创建同一个类的多个实例。这些属于面对的编程思想,需要结合实践去体会他们的异同。
8.总结
在软件设计过程中,不仅衡器称重软件“一机多秤”(即一台上位机,多台称重仪表同时工作),可以使用“工厂模式”来设计,其它类似的—涉及多台仪表,多个串口同时工作的软件,都可参考这种模式。软件模式的使用,使代码易于阅读,管理,能提高代码的重用效率。实际软件开发中,具体使用哪种模式,是一种模式,还是多种模式混合使用?对于开发者来说,需要有一定行业经验,总结出用户的需求,整理出规律性的内容,再选择合适开发模式,才能找到一条正确的道路。