Algorithm Library Design 演算法庫設計in c++ III(設計策略)

來源:互聯網
上載者:User
1.基於策略的類設計(Policy-Based Class Design)

基於策略的設計是一種面向組件的結構設計技巧。它將一個類的功能分解為不同的策略,每個策略可以有不同的實現,被稱為策略類。主類或者稱為host class,通過將policy class作為它的模版參數來擷取函數功能。

作為樣本,給出一個策略,它的目的是產生對象。下面有兩種不同的實現。

template <class>struct OpNewCreator {    static T* create() { return new T; }};template <class T>struct MallocCreator {    static T* create() {        void* buf = std::malloc(sizeof(T));        if (!buf) return 0;        return new(buf) T;    }};

 

我們同時有一個host class來使用policy.(個人覺得這樣不是常規意義上public 繼承的is-a意義了,類似private繼承的implemented using)

template <class CreationPolicy = OpNewCreator<Widget> >class WidgetManager : public CreationPolicy {     // ...};

 

現在使用者可以選擇,用什麼樣的方式來產生對象。hwo to create

typedef WidgetManager<> DefaultWidgetManager;typedef WidgetManager< MallocCreator<Widget> > MallocWidgetManager;

 

考慮到WidgetManager總是操縱Widget類型資料,所以不應該由使用者來顯示聲明Widget,更好的

方式是用模版模版參數template template parameters.這樣甚至可以在host class如下面的WidgetManager內部用同樣的策略產生其它類型的對象。

// library codetemplate <template <class> class CreationPolicy = OpNewCreator>class WidgetManager : public CreationPolicy<Widget> {     // ...    void doSomething() {        Gadget* q = CreationPolicy<Gadget>().Create();        // ...    }    // ...};

 

//application codetypedef WidgetManager<MallocCreator> MallocWidgetManager;

 

當前關於模版的模版的實參,有一個極其容易出差錯的地方,參見

<<c++ templates>> p107模版的模版實參。 因為該實參本身具有參數,所以該參數必須應該精確匹配它所替換的模版的模版的模版參數本身的參數。

例如

template<typename T, template<typename> class Container>

class Relation;

如果我們使用Relation<int,std::list> relation;就是錯誤的。

因為雖然我們平常都是用list<int> list<double>之類的,但是list模版類是有兩個參數的而不是一個,儘管後一個是有預設值因此我們一般都忽略,但是在這裡是認為不匹配的。

template<typename T, typename Allocator = allocator<T>>

class list;  //list 的聲明。

要想使得使用者能夠像上面那樣使用Relation,要像下面這樣聲明Relation。

#include<memory>

template<typename T, typename = std::allocator<T> > class Container>

class Relation;

2.擴充的策略(Enriched Policies)

我們可以再寫一個creation policy class,它不僅僅有create函數,同時還提供其它的函數(介面)

template <class T>struct PrototypeCreator {    PrototypeCreator(T* p = 0) : prototype(p) {}    T* create() {        return prototype ? prototype->clone() : 0;    }    T* get_prototype() { return prototype; }    void set_prototype(T* p) { prototype = p; }private:    T* prototype;};

這個與前面的policy的實現相比與眾不同的policy class實現可以和前面的policy實現一起工作為host class提供服務。

typedef WidgetManager<PrototypeCreator> MyWidgetMgr;// ...Widget* p = ...;MyWidgetMgr mgr;mgr.set_prototype(p);
 
我們可以在host class 內部針對PrototypeCreator提供一個特別的函數
template <template <class> class CreationPolicy = OpNewCreator>class WidgetManager : public CreationPolicy<Widget> {     // ...    void switch_prototype(Widget* p) {        CreationPolicy& myPolicy = *this;        delete myPolicy.get_prototype();        myPolicy.set_prototype(p);    }    // ...};

由於模版的"lazy" implicit instantiation機制的存在,我們仍然可以用前面的policy如OpNewCreator,儘管OpNewCreator不支援get_prototype,以及set_prototype.

但是沒有關係,編譯錯誤僅僅在你試圖調用switch_pototype的時候才會發生。

3.組合策略(Combining Policies)

基於策略的設計模式的威力在有多種策略組合的時候顯的尤為明顯,給程式設計帶來極大的靈活性。Loki庫裡有一個smart pointer類作為host class 使用4種policies.

template <    class T,    template <class> class OwnershipPolicy  = RefCounted,                     class ConversionPolicy = DisallowConversions,    template <class> class CheckingPolicy   = AssertCheck,    template <class> class StoragePolicy    = DefaultSPStorage>class SmartPtr;
這裡每一種策略都對應有多種可能的實現:
  • Ownership policy: DeepCopy, RefCounted, RefCountedMT, COMRefCounted, RefLinked, DestructiveCopy, and NoCopy.
  • Conversion policy: AllowConversion and DisallowConversion.
  • Checking policy: AssertCheck, AssertCheckStrict, RejectNullStatic, RejectNull, RejectNullStrict, and NoCheck.
  • Storage policy: DefaultSPStorage, ArrayStorage, LockedStorage, and HeapStorage.

這裡我們有1 + 7 + 2 + 6 + 4 = 20個組件,在一起它們提供了7 * 2 * 6 * 4 = 336種不同的組合。因此有336種不同的smart pointer 類。

而且以後我們可能還會添加新的policy classes使用者也可以定製他們自己的policy classes.

4.Named Template Arguments

在SmartPtr,所有的策略都要預設值,因此寫SmartPtr<int>就會得到一個預設的配置。如果要改寫ownership policy的實現也很容易,SmarPtr<int, NoCopy>

但是如果我們想要對storage polciy採用非預設的實現,那麼所有的模版實參都要顯式的給出。類似與Python中的函數

def foo(name = ‘abc,’ age = 18, city = ‘LaSa’):

使用者可以方便的寫出foo(‘def’, 32),也可以寫 foo(age = 32) 從而參數為 name = ‘abc’ age = 32  city = ‘LaSa’

我們在C++這裡希望使用者可以簡單的寫成下面的形式

SmartPtr<int, StoragePolicy_is<LockedStorage> > sp; //從而表明除了storage policy 採用LockedSotrage實現,其餘polcies均採用預設實現配置。

一種實現這種功能的技巧叫做Named Template Arguments。我們以一個簡單的BreadSlicer class作為例子,它有3個policy參數。

template < class PolicySetter1 = DefaultSetter,           class PolicySetter2 = DefaultSetter,           class PolicySetter2 = DefaultSetter >class BreadSlicer {    typedef PolicySelector < PolicySetter1, PolicySetter2,                             PolicySetter3 >            Policies;    // use policies:    Policies::P1 // ...    Policies::P2 // ...    Policies::P3 // ...};

下面給出具體測試程式吧,原網頁上的樣本有些問題,這種方法雖然好用,不過實現細節太繁瑣不直觀了,多層次的繼承,多繼承,模版參數作為父類,虛擬繼承,非型別參數的模版參數,

通通出現了,想出這個方法的人真是太牛了。

#include <iostream>
using namespace std;

struct DefaultPolicy1{
    static void print(){
        cout << "Default Policy1 print" << endl;
    }
};

struct DefaultPolicy2{
    static void print(){
        cout << "Default Policy2 print" << endl;
    }
};

struct DefaultPolicy3{
    static void print(){
        cout << "Default Policy3 print" << endl;
    }
};

struct MyPolicy1{  //for Policy1 we can choose from defualt DefaultPolicy1 and custom MyPolicy1
    static void print() {
        cout << "My Policy1 print" << endl;
    }
};

struct MyPolicy2{
    static void print() {
        cout << "My Policy2 print" << endl;
    }
};

struct MyPolicy3{
    static void print() {
        cout << "My Policy3 print" << endl;
    }
};

struct DefaultPolicies {
    typedef DefaultPolicy1 P1;
    typedef DefaultPolicy2 P2;
    typedef DefaultPolicy3 P3;
};

class DefaultSetter : virtual public DefaultPolicies {};

template <typename Policy>
struct Policy1_is : virtual public DefaultPolicies {
    typedef Policy P1;
};

template <typename Policy>
struct Policy2_is : virtual public DefaultPolicies {
    typedef Policy P2;
};

template <typename Policy>
struct Policy3_is : virtual public DefaultPolicies {
    typedef Policy P3;
};

template <class Base, int D>
struct Discriminator : public Base {};

template < class Setter1, class Setter2, class Setter3 >
class PolicySelector : public Discriminator<Setter1,1>,
                       public Discriminator<Setter2,2>,
                       public Discriminator<Setter3,3>
{};

template < class PolicySetter1 = DefaultSetter,
           class PolicySetter2 = DefaultSetter,
           class PolicySetter3 = DefaultSetter >
class BreadSlicer {
    typedef PolicySelector < PolicySetter1, PolicySetter2,
                             PolicySetter3 >
            Policies;
    public:
    static void print() {
        Policies::P1::print();  //Policy1 print
        Policies::P2::print();  //Policy2 print
        Policies::P3::print();  //Polciy3 print
    }
};

int main()
{
    cout <<  "using default" << endl;
    BreadSlicer<> bs1;
    bs1.print();
    cout << "using custom policy fo Policy2" << endl;
    BreadSlicer<Policy2_is<MyPolicy2> > bs2;
    bs2.print();
    cout << "using custom policy for Policy2 and Policy3" << endl;
    BreadSlicer<Policy3_is<MyPolicy3>, Policy2_is<MyPolicy2> > bs3;
    bs3.print();
    return 1;
}

//result

using default
Default Policy1 print
Default Policy2 print
Default Policy3 print
using custom policy fo Policy2
Default Policy1 print
My Policy2 print
Default Policy3 print
using custom policy for Policy2 and Policy3
Default Policy1 print
My Policy2 print
My Policy3 print

下面解釋一下,

template <class Base, int D>
struct Discriminator : public Base {};

template < class Setter1, class Setter2, class Setter3 >
class PolicySelector : public Discriminator<Setter1,1>,
                       public Discriminator<Setter2,2>,
                       public Discriminator<Setter3,3>
{};

 

Discriminator類模版之所以需要int D這個非型別參數,是為了避免PolicySelector重複的繼承同一個父類

如public Discriminator<DefualtSetter>。

struct DefaultPolicies {
    typedef DefaultPolicy1 P1;
    typedef DefaultPolicy2 P2;
    typedef DefaultPolicy3 P3;
};

class DefaultSetter : virtual public DefaultPolicies {};

template <typename Policy>
struct Policy1_is : virtual public DefaultPolicies {
    typedef Policy P1;
};

而只所以用virtual public DefaultPoicies是因為虛擬繼承避免重複的繼承DefaultPoicies中的成員,只有一個DefaultPoicies作為一個虛基類,避免了多繼承造成P1,P2的重定義。

Policy2_is<MyPolicy2>會覆蓋掉父類對P2的定義,這正是我們想要的。

原文用的是Policy2_is<CustomPolicy>樣本,並給出一個。

個人感覺這麼實現太複雜了,還不如給一個default 的config類,如上面的struct DefaultPolicies ,然後host class用這個做模版參數就好了,使用者自己改的話,定義一個自己的config繼承default config,

然後修改需要改動的policy 實現,如 typedef MyPolicy2 P2;

 

 
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.