APP下载

模板方法设计模式的泛型编程技术实现

2019-07-08余建华赵晓莲

计算机时代 2019年5期
关键词:设计模式

余建华 赵晓莲

摘  要: 模板方法是设计模式中一种典型的设计范式,传统上是利用面向对象编程技术的多态属性实现的,文章应用泛型编程技术来实现模板方法设计模式。这种实现方式舍弃了面向对象编程技术中基于继承的基类,派生类的虚函数机制,免去了在程序运行过程中虚函数二次寻址的代价。在保留设计模式中程序修改扩展弹性的同时,降低了程序运行负载,提高运行速度,展现出一种泛型编程技术不同于传统的应用场景。

关键词: 面向对象编程; 泛型编程; 模板函数; 模板方法; 设计模式

中图分类号:TP311.1          文献标志码:A  文章编号:1006-8228(2019)05-41-05

Abstract: The template method is a representative pattern in design patterns. Traditionally, it is implemented by polymorphic attributes of object oriented programming technology. This paper introduces a method that implements the template method pattern by generic programming. This method of implementation abnegates the inheritance based virtual function mechanism which is the pivotal conception in object oriented programming, and avoids the expense for the second function addressing of virtual function in program running. The template method pattern implemented by generic programming not only reserves the flexibility for program extending and amending, but also improves the speed of program executing, demonstrating another scene of application of generic programming which is different from the traditional.

Key words: object oriented; generic programming; template function; template method pattern; design patterns

0 引言

泛型編程技术及在其上发展的模板元编程是C++编程领域近年来最为关注的课题。模板是实现泛型编程技术的关键性C++构件[1]。模板第一次具有里程碑意义的应用是标准模板程序库STL,其通过泛型容器、迭代器、泛型算法实现,富有弹性,包含数据结构和算法的可复用组件库可扩展,淋漓尽致地向我们展现了模板的使用艺术,以至于我们一提到模板,首先想到的就是实现泛型容器的技术。实际上模板技术的魅力远不止于此,利用其具有的编译期计算能力而发展的模板元编程技术,以及其与设计模式的结合,为我们打开了泛型编程领域的一片新天地。 设计模式是早先C++编程领域的专家为帮助应用开发者针对一些常见的,特定的应用问题而归纳总结的一组可复用的设计范式[3]。模板方法是设计模式中一种典型的行为型模式。本文首先介绍传统利用面向对象编程技术的多态属性实现的方式,然后应用泛型编程技术实现模板方法设计模式,最后阐述两种实现的差异。

1 面向对象技术实现模板方法

模板方法是一种行为型的设计模式,主要思想是在基类中定义功能单元或算法的骨架,而这个骨架使用的细节操作延迟到派生类实现。在不改变骨架的条件下,通过某些细节操作步骤的变更,我们可以方便地维护,扩展功能单元或算法。

例如我们想计算封闭几何图形的面积与周长比率,以此来发现何种图形的单位周长围成的面积最大。

首先定义一个几何图形基类:

class Geometrics

{ public:

float CalculateRatio0Area2Circularity(void);

private:

virtual float CalculatePerimeter(void)=0;

virtual float CalculateArea(void)=0;

};

基类的计算面积与周长比率的公有成员函数用来实现算法的骨架,其主要是调用计算周长和面积的纯虚函数来算出几何图形的周长和面积,然后算出比率。

float Geometrics::CalculateRatio0Area2Circularity(void)

{ float fPerimeter=1.0,fArea=1.0;

fPerimeter=CalculatePerimeter();

fArea=CalculateArea();

return(fArea/fPerimeter);

}

定义一个圆的子类,用以实际计算圆的面积与周长比率。

class Circularity: public Geometrics

{ public:

Circularity(int iRadius):m_iRadius(iRadius){};

private:

float CalculatePerimeter(void);

float CalculateArea(void);

private:

int m_iRadius;

};

圓形子类实现基类的计算周长和面积的纯虚函数,用以具体计算圆形的周长和面积。

float Circularity::CalculatePerimeter()

{ return 2*PI*m_iRadius; }

float Circularity::CalculateArea()

{ return PI*m_iRadius*m_iRadius; }

同样定义一个矩形的子类,用以实际计算矩形的面积与周长比率

class Rectangle:public Geometrics

{ public:

Rectangle(int iLength, int iWidth):m_length(iLength),

m_width(iWidth) {};

private:

float CalculatePerimeter(void);

float CalculateArea(void);

private:

int m_length;

int m_width;

};

矩形子类实现基类的计算周长和面积的纯虚函数,用以具体计算矩形的周长和面积

float Rectangle::CalculatePerimeter()

{ return 2*(m_length+m_width); }

float Rectangle::CalculateArea()

{ return m_length * m_width; }

主函数调用基类对象使用计算圆和矩形面积与周长比率的功能。注意生成的是派生类对象,指针却是基类对象类型。

int main()

{ class Geometrics *pGeometrics01=new Circularity(3);

class Geometrics *pGeometrics02=new Rectangle(3,3);

std::cout<CalculateRatio0Area2Circularity() << std::endl;

std::cout<CalculateRatio0Area2Circularity()<

return 0;

}

使用模板方法,我们可以方便地扩展或修改。如通过增加其他图形的子类,计算其他图形的面积与周长比率,并不需要再定义面积与周长比率的计算方法;或者修改某一图形面积与周长的计算方法,也不影响其他已有图形面积与周长的计算。

2 泛型编程技术实现模板方法

通过泛型编程技术也能实现模板方法设计模式的功能。

定义一个函数模板,用以实现计算面积与周长比率的算法骨架。

template

float CalculateRatio0Area2Circularity(const T& tGeometrics)

{ float fPerimeter=1.0,fArea=1.0;

fPerimeter=tGeometrics.CalculatePerimeter();

fArea=tGeometrics.CalculateArea();

return(fArea/fPerimeter);

}

定义一个圆形类,用以实际计算圆的面积与周长比率,注意这里不再有继承关系。

class Circularity

{ public:

Circularity(int iRadius):m_iRadius(iRadius){};

float CalculatePerimeter(void) const;

float CalculateArea(void) const;

private:

int m_iRadius;

};

圆形类实现计算周长和面积的成员函数,用以具体计算圆形的周长和面积

float Circularity::CalculatePerimeter() const

{ return 2*PI*m_iRadius; }

float Circularity::CalculateArea() const

{ return PI*m_iRadius*m_iRadius; }

定义一个矩形类,用以实际计算矩形的面积与周长比率。

class Rectangle

{ public:

Rectangle(int iLength, int iWidth):m_length(iLength),

m_width(iWidth) {};

float CalculatePerimeter(void) const;

float CalculateArea(void) const;

private:

int m_length;

int m_width;

};

矩形类实现计算周长和面积的成员函数,用以具体计算矩形的周长和面积

float Rectangle::CalculatePerimeter() const

{ return 2*(m_length + m_width); }

float Rectangle::CalculateArea() const

{ return m_length * m_width; }

主函数调用函数模板使用计算圆和矩形面积与周长比率的功能。

int main()

{ class Circularity stCircularity01(3);

class Rectangle stRectangle01(3,3);

std::cout<<"Circularity:"

<(stCircularity01) << std::endl;

std::cout<<"Rectangle:"

<(stRectangle01) << std::endl;

return 0;

}

3 面向对象技术与泛型编程技术的复合应用

我们举例的这个计算几何图形的面积与周长比率的问题,在实现算法上分成了两个层次。第一层是利用计算出来的面積与周长相除,计算比率。第二层是计算具体几何图形的面积与周长。第一层是共性操作,第二层是特性操作。

实现这个算法的第一个方法是设计模式中的模板方法。将第一层共性操作放到基类成员函数中作为实现方法的骨架,将第二层特性操作放到派生类成员函数,实现这个骨架使用的细节操作。这种基于C++继承和虚拟函数机制的变化由用户使用基类类型的指针来调用虚拟函数来支持。正是因为不管实际上生成哪一个派生类对象,在使用形式上都是基类指针,编译期无法决定实际调用对象的成员函数,此任务由执行期获得对象的实际派生类型后决定,因此这种机制的弹性变化称之为动态多态[2]。

实现这个算法的第二个方法是泛型编程的模板技术。通过模板函数的表达式及具体操作,我们在模板和将要用以具现模板的参数间约定了一组接口,所有具现模板的参数类型都要实现这些接口。这些接口就是抽象出来的所有参数类型的共性操作。把这些共性操作放置于模板中,把细节实现的特性操作放置于具体类型的成员函数中,我们同样实现了解决方法模型的两层结构,由此获得同样的设计弹性。由于具体使用的对象类型在编译期具现化模板函数时就已确定,因此这种基于模板技术实现的弹性变化称之为静态多态[5]。

现在我们来个“动”,“静”结合,把设计模式中的模板方法和泛型编程中的模板技术复合起来产生一个解决问题的方法模型用以解决更复杂的问题,同时拥有更高的弹性。为此我们把举的例子修改一下,有计算基础几何图形的面积与周长比率改为组合几何图形的面积与周长比率。这种组合有两种方式,凸型和凹型,如图1所示。

我们解决问题的算法模型结构分为三个层次。如图2所示。

第一层是计算几何图形的面积与周长比率,如果比率的算法有修改需求,将在这里修改。注意这里的修改将影响所有图形比率的计算,这是共性层。我们用函数模板实现这一层。

template

float CalculateRatio0Area2Circularity(T& tGeometrics)

{ float fPerimeter=1.0,fArea=1.0;

fPerimeter=tGeometrics.CalculatePerimeter();

fArea=tGeometrics.CalculateArea();

return(fArea/fPerimeter);

}

第二层是计算“凸” 或 “凹” 组合图形的面积与周长。我们分别定义两个基类管理计算。如果想要扩展计算其他组合方式的组合图形,可以在这一层扩展新基类,这是偏特性层(借用泛型编程术语)。

凸组合图形基类的定义:

class Convexity

{ public:

float CalculatePerimeter(void);

float CalculateArea(void);

private:

virtual float CalculatePerimeterPolygonA(void)=0;

//const;

virtual float CalculatePerimeterPolygonB(void)=0;

virtual float CalculateAreaPolygonA(void)=0;

virtual float CalculateAreaPolygonB(void)=0;

};

凸組合图形计算面积与周长:

float Convexity::CalculatePerimeter(void)

{ float fPerimeterPolygonA, fPerimeterPolygonB;

fPerimeterPolygonA=CalculatePerimeterPolygonA();

fPerimeterPolygonB=CalculatePerimeterPolygonB();

return fPerimeterPolygonA + fPerimeterPolygonB;

}

float Convexity::CalculateArea(void)

{ float fAreaPolygonA, fAreaPolygonB;

fAreaPolygonA=CalculateAreaPolygonA();

fAreaPolygonB=CalculateAreaPolygonB();

return fAreaPolygonA+fAreaPolygonB;

}

⑴ 凹组合图形基类的定义与凸组合图形基类类似;

⑵ 凹组合图形计算周长的方法与凸组合图形基类相同,凹组合图形计算面积的方法与凸组合图形基类类似,前者组成组合图形的子图形相减,后者是相加,此处不再列出;

⑶ 第三层是计算具体组合图形的面积与周长,也是面积与周长的实际计算点。如果“凸” 或 “凹” 组合图形由其他基础图形构成,可在这一层扩展,这是特性层。我们用派生类来管理计算。

由圆与矩形组成的凸组合图形类定义:

class ConvexityConcreteA: public Convexity

{ public:

ConvexityConcreteA(int iRadius, int iLength):

m_iRadius(iRadius), m_iLength(iLength) {};

private:

virtual float CalculatePerimeterPolygonA(void);

virtual float CalculatePerimeterPolygonB(void);

virtual float CalculateAreaPolygonA(void);

virtual float CalculateAreaPolygonB(void);

private:

int m_iRadius;

int m_iLength;

};

由圆与矩形组成的凸组合图形面积与周长的计算:

float ConvexityConcreteA::CalculatePerimeterPolygonA(void)

{ return 2*(m_iLength + m_iRadius); }

float ConvexityConcreteA::CalculatePerimeterPolygonB(void)

{ return PI*m_iRadius; }

float ConvexityConcreteA::CalculateAreaPolygonA(void)

{ return 2*m_iRadius * m_iLength; }

float ConvexityConcreteA::CalculateAreaPolygonB(void)

{ return 0.5*PI*pow(m_iRadius, 2); }

由圆与三角形组成的凸组合图形类定义:

class ConvexityConcreteB: public Convexity

{ public:

ConvexityConcreteB(int iHigh, int iLength, int

iWidth):m_iHigh(iHigh), m_iLength(iLength),

m_iWidth(iWidth){};

private:

virtual float CalculatePerimeterPolygonA(void);

virtual float CalculatePerimeterPolygonB(void);

virtual float CalculateAreaPolygonA(void);

virtual float CalculateAreaPolygonB(void);

private:

int m_iHigh;

int m_iLength;

int m_iWidth;

};

由圆与三角形组成的凸组合图形面积与周长的计算:

float ConvexityConcreteB::CalculatePerimeterPolygonA(void)

{ return 2*m_iLength + m_iWidth; }

float ConvexityConcreteB::CalculatePerimeterPolygonB(void)

{ return 2*sqrt(pow(0.5*m_iWidth, 2)+pow(m_iHigh, 2)); }

float ConvexityConcreteB::CalculateAreaPolygonA(void)

{ return m_iLength * m_iWidth; }

float ConvexityConcreteB::CalculateAreaPolygonB(void)

{ return 0.5*m_iWidth*m_iHigh; }

由圆与矩形组成的凹组合图形类定义与相应的凸组合图形定义类似,其组成的凹组合图形面積与周长的计算与相应的凸组合图形面积与周长计算类似;

由圆与三角形组成的凹组合图形类定义与相应的凸组合图形定义类似,其组成的凹组合图形面积与周长的计算与相应的凸组合图形面积与周长计算类似,此处不再列出。

在主函数中,我们用模板函数来获取各个组合图形的面积与周长比率。

int main()

{ class ConvexityConcreteA stConvexityConcreteA(2,4);

class ConvexityConcreteB stConvexityConcreteB(2,4,4);

class ConcavityConcreteA stConcavityConcreteA(2,4);

class ConcavityConcreteB stConcavityConcreteB(2,4,4);

std::cout<<"ConvexityConcreteA:"

<(stConvexityConcreteA) << std::endl;

std::cout<<"ConvexityConcreteB:"

<(stConvexityConcreteB) << std::endl;

std::cout<<"ConcavityConcreteA:"

<(stConcavityConcreteA) << std::endl;

std::cout<<"ConcavityConcreteB:"

<(stConcavityConcreteB) << std::endl;

return 0;

}

4 总结

模板方法是设计模式中一种典型的行为型设计范式[6],在设计模式中是利用面向对象编程技术中基于继承的基类,派生类的虚函数机制来实现的。本文应用泛型编程技术来实现模板方法设计模式。这种实现方式是利用的是模版函数与实现参数间的约定接口[4],在接口的定义域实现过程中获得与设计模式同样的设计弹性,同时省去了虚函数运行期多态的动态开销,降低了运行负载,展现出一种泛型编程技术不同于传统的应用场景。

参考文献(References):

[1] Stanley B·Lippman,Josée Lajoie . C++ Primer 中文版(第三版)[M].电子工业出版社,2013.

[2] Bruce Eckel.C++编程思想(第二版)[M].机械工业出版社,2002.

[3] Erich Gamma,Richard Helm,Ralph Johnson,John Vlissides.设计模式 可复用面向对象软件的基础[M].机械工业出版社,2000.

[4] Andrei Alexandrescu. Modern C++ Design[M].Addison-Wesley Professional,2003.

[5] David Vandevoorde,Nicolai M.Josuttis. C++Templates中文版[M].人民邮电出版社,2008.

[6] 结城浩.图解设计模式[M].人民邮电出版社,2016.

猜你喜欢

设计模式
设计模式识别的特征信息分类研究
“1+1”作业设计模式的实践探索
基于能力目标培养的药学专业课程整体教学设计模式研究
引入线索约束的设计模式变体挖掘研究*
设计模式挖掘的有效性评估策略
智慧图书馆环境下的融贯式服务设计模式研究
三维协同设计模式下的航天项目管理实践与展望
交通机电工程设计模式创新探讨
应用型高校学生程序设计能力培养研究
社会化媒体的共生交互社交设计模式与策略研究