Generic design | Policies 和Policy Classes

    xiaoxiao2022-06-27  40

    注意:本系列博文需要对templates和STL有一定掌握。

    Policies 和Policy Classes

          承接上一篇博文所述,这回将介绍Policies 和Policy Classes。Policies 和Policy Classes有助于我们设计出安全且有效率,具有高度弹性的【设计元素】。所谓Policy,即用来定义一个class 或class template的介面,该介面由下列项目之一或全部组成:内隐型别定义,成员函式和成员变量。

    Policies 也被其他人用于traits,不同的是后者比较重视行为而非型别,policies也让人联想到设计范式strategy,只不过policies吃紧于编译期(compile—time bound)。

           举个例子,定义一个policy用来生成物件:creator policy是个带有型别T的class template,它必须提供一个名为create的函数给外界使用。这个函数不接受参数,传回一个 T的指针。就语义而言,每当create()被call就必须传回一个指标,指向新生的T物件,至于物件的精确生成模式,留给policy实作品来做订制因子。

           现在定义一个可实作出creator policy的class。产生物件的可行办法总体有三种,一即为运算式new,另一种即为malloc加上placement new运算子。此外还可以采用复制的方式。以下为实现代码:

    template<class T> struct OpNewCreator { static T* create() { return new T; } }; template<class T> struct MallocCreator { static T* create() { void *buf=malloc(sizeof(T)); return buf?new(buf) T:0; } }; template<class T> struct PrototypeCreator { PrototypeCreator(T * pObj=0) :pPrototype_(pObj) { } T* create() { return pPrototype_?pPrototype_->Clone():0; } T *GetPrototype() { return pPrototype_; } void SetPrototype(T *pObj) { pPrototype_=pObj; } private: T *pPrototype_; };

           任何一个policy 都可以有无限多的实作品。实作出的policy 即为policy classes ,或许这个名字不够精确,因为如你所见,policy 的实作品也可以是class template。然而,这东西并不是期望被单独使用,它们主要被用于继承或被内包含其他的classes。

           这里有一个重要的概念:policies介面个一般的传统的classes 介面(纯虚函数 classes)不同,它结构比较松散,一位内polices是语法导向而非标记导向。换句话来说creator明确定义的是“怎样的语法构造符合其所规范的class” ,而非“必须实作出哪些函数”。例如creator policy 并没有规范create()必须是static还是virtual,它只是要是class 必须定义出create()。此外creator也只规定create()应该传回一个指向新物件的指针但不是必须的。因此create()也许会传回nullprt 或丢出异常,这些都极为可能。

           面对一个policy ,你可以实作出数个policy classes 。它们全都必须遵守policy 所定义的介面。往后你会看到一个例子,使用者选择了一个policy 并应用到较大的结构当中。

           先前定义的三个policy classes ,各有不同的实作方式,甚至连介面都不尽相同(第三个policy classes 多了两个函数)。尽管如此,它们全都定义了create(),并传回必要的返回型别。因此它们都符合creator policy。

          以下展现的是如何设计一个class 得以利用creator policy,它以复合或继承的方式使用先前所定义的三个classes 之一:

    template<class T> class WidgetManaget:public T { ... };

           如果class 采用一个或多个policies,我们称其为hosts 或host classes,虽然host classes从技术上来说是host class template,但是不论host classes或host calss template,都意味着相同的概念。上例的widgetmanager便是采用了一个policy 的host class。Hosts负责把policies提供的结构和行为组成一个更加复杂的结构和行为。

          当客户端讲widgetmanager template具现化的时候,必须传进一个他所期望的policy:

    typedef WidgetManaget<OpNewCreator<Widget>> MyWidgetMgr;

          让我们分析整个过程的来龙去脉,无论何时,当一个MyWidgetMgr物件需要产生一个widget物件,它便call 它的policy子物件opnewcreator<widget>所提供的create()。选择【生成策略】是widgetmanager使用者的权利。藉由这样的设计,可以让widgetmanager使用者自动装配他所需要的机能。这便是整个policy based class 的设计主旨。

         如同先前的例子所示,policy 的template参数往往都是累赘。使用者每每需要传入template参数给opnewcreator,这很笨拙。一般来说host class 已经知道policy class 所需参数,或者能轻易地推到出来。上述例子中widgetmanager 总是操作widget物件。这样的情况下还要求使用者把widget型别传入opnewcreator,这就显得多余且沉坠。

        这时候程式库可以使用 template template参数来描述policies,如下所示:

    template <template<class created> class T> class WidgetManaget:public T<Widget> { ... };

           尽管定义了created参数,但其并未对widgetmanager 有任何贡献。你不能在widgetmanager仲使用created,它只是 T的形式参数,因此可以省略。

    应用端现在只需要使用widgetmanager时提供template名称即可:

    typedef WidgetManaget<OpNewCreator> MyWidgetMgr;

           搭配policy class 使用 template template参数,并不单纯为了方便简约。有时候这种用法不可或缺。以便host class 可藉由templates 产生不同型别的物件。举个例子,假设widgetmanager 想要以相同的生成策略产生一个Gadget物件,代码如下所示:

    template<template <class> class T> class WidgetManaget :public T<Widget> { ... void dosomething() { Gadget *pG_=T<Gadget>().Create(); ... } };

           使用policies 是否会为我们带来一些优势呢?咋一看起来貌似并不多。首先,creator policy 的实作本来就十分简短。当然,widgetmanager 的撰写者应该会把生成物件的那份程式码写成内联函数,并避开【将widgetmanager 建立为一个template】时可能遭遇的问题。然而,policy 的确能够带给widgetmanager 非常大的弹性。第一,当你准备具象化widgetmanager时,你可以从外部变更policies,就跟改变template参数一样地简单。第二,你可以根据程序的特殊要求,提供自己的policies。你可以采用new 或者malloc()或者prototype或一种你自己专用于你系统上的罕见记忆体配置器。widgetmanager就像一个小型的程式码生成引擎,你可以自行设定程式码的产生方式。

         为了让应用程序开发人员日子更加轻松,widgetmanager 的作者应该定义一些常用的policies,并且以 template 预设参数 的形式提供最常用的policy:

    template <template<class> class T=OpNewCreator> class WidgetManaget ...

           需要注意,policies和虚拟函数有很大的不同,虽然虚拟函数也提供类似的效果:class 作者以基本的虚拟函数建立高端功能,并允许使用者撰写这些基本虚拟函数的行为。然而之前所示,policies因为有丰富的型别资讯及静态链接等特性,所以建立起【设计元素】时的本质性东西,不正是【设计】指定了“执行前型别如何相互作用,你能够做什么,不能够做什么”的完整规则吗?policies可以让你在型别安全的前提下藉由组合各个简单的需求来产出你的设计。此外,由于编译期才将host class与其 policies结合在一起,所以跟手工打造的程序比较起来,当然显得更假牢固并且更有效率。

          当然,也由于policies的特质,他们不适合用于动态链接和二进位介面,所以本质上policies 和传统介面并不互相竞争。

    另外一种使用template template参数 的情况是把template成员函数 用来连接所需的简单型别。也就是说,将policy实作为一般的class,但有一个或数个的template members。

          例如,我们可以重新定义先前的creator policy 成为一个 没有template参数的class ,其内提供一个名为create<T>的template函数。如此一来,policy class 看起来如下:

    struct OpNewCreator { template<class T> static T *Create() { return new T; } };

    这种方式所定义并实作出来的policy,对于旧式编译器有较好的相容性。但从另一方面来讲,这样的policy 难以讨论,定义,实作与运用。

    转载请注明原文地址: https://ju.6miu.com/read-1124074.html

    最新回复(0)