<学习笔记>Algorithm Library Design 算法库设计in c++ IV(GenVoca 架构)

GenVoca 架构

GenVoca是一种软件架构,与policy-based架构类似的是,它的最终成品也是由各个组件组装而成的,但是组织的方式有所不同。

在Policy-based架构中,组件作为具体的policy实现被添加到host class以实现相应的功能,而在GenVoca中组件作为wrappers (layers) on top of a more basic components adding functionality to it.

个人感觉从原文给的list的一个实现来看,与基于policy的架构相比,GenVoca多了不同的层次,上层需要下层的不同实现。如B层需要下面的C来实现,而实现后的B又可作为实现提供给更上层的A。

R : A | B[R]
S : C[R] | D[S] | E[R,S]
R可以由A实现,或者由B和R的一个实现合起来实现。
S可以由C以及R的某个实现,合起来实现。

GenVoca实例:List

image

上图是一个List的特征图。特征是从用户使用角度来看的,用户装配List要提供的参数。

必要的特征

我需要List 操纵什么样的数据?

List如何持有数据?通过引用持有还是深拷贝的方法持有。

对于数据操作,我们采用多态性吗?

可选的特征

需要一个变量记录长度吗->如果需要长度的数据类型是什么int short 还是long

需要Tracing吗,这里意思是说我们要追踪过程打印过程信息吗?

设计一个GenVoca架构需要考虑下面几个步骤:
  • 确定特种图中提到的主要职责,要提供的主要功能。对于List:
    • 数据的存储
    • 数据的拷贝
    • 数据的销毁(注意这个对应到特征图,如果用户选择copy持有数据,或者owned reference那么要自己负责delete,否则不需要,empty delete就好)
    • 动态类型检查(为了monomorphism)
    • 长度计数器
    • tracing
  • 确定组件分组:
    • BasicList: PtrList     //:左边代表策略组件,右边是不同的实现策略categories : components
    • Copier: PolymorphicCopier, MonomorphicCopier, EmptyCopier
    • Desroyer: ElementDestroyer, EmptyDestroyer
    • TypeChecker: DynamicTypeChecker, EmptyTypeChecker
    • Counter: LengthList
    • Tracing: TracedList
  • 确定使用依赖`use'' dependencies between component categories.

   image

  • Sort the categories into a layered architecture.得到具体架构。最底层的copier,destoryer,typechecker,作为config.

The component categories that do not depend on other categories are grouped into the bottom ConfigurationRepository (Config for short) layer category.

Tracing和Counter是可选的分组。

个人注,上图其实很清楚的标明了List的整体架构设计。

BasicList(其实就是用PtrList类实现)有3个策略copier,destoryer,typecheker,通过选择不同的策略组合不同的list。其实就是基于policy的设计。

然后counter用basicList

那么采用模版继承的方法。

<typename Base>

class Derived: public Base

通过把basicList作为模版参数,并继承,使得counter复用basiclist,同时它自己提供basicList没有的长度计数器,修改相应操作维护长度计数。

Tracing 使用counter那它在最上层,同样通过相同方式继承Counter即可,然后修改相应操作添加过程打印的信息。

  • 写下GenVoca grammar
List            : TracedList[OptCounterList] | OptCounterList
OptCounterList  : LengthList[BasicList] | BasicList
BasicList       : PtrList[Config]
Config          :
   Copier       : PolymorphicCopier | MonomorphicCopier | EmptyCopier
   Destroyer    : ElementDestroyer | EmptyDestroyer
   TypeChecker  : DynamicTypeChecker | EmptyTypeChecker
   ElementType  : [ElementType]
   LengthType   : int | short | long | ...
实现细节

用户可以自己定义一个符合自己需求的的config

struct TracedIntListConfig {
  typedef int                             ElementType;
  typedef const ElementType               ElementArgumentType;
  typedef MonomorphicCopier<ElementType>  Copier;
  typedef ElementDestroyer<ElementType>   Destroyer;
  typedef DynamicTypeChecker<ElementType> TypeChecker;

  typedef TracedList<PtrList<TracedIntlListConfig> >
      FinalListType;
};
typedef TracedIntListConfig::FinalListType TracedIntList;
这里要注意的是出现了模版参数循环依赖。这样才能给出具体的最终List类型。
注,其实这个config不应该是最终提供给用户,这些参数是最终决定List类型的,但不应由用户设定这样的参数,这个还是一个比较内部的config,例如具体采用什么Destroyer对于用户应该是透明的。
用户可能会设置出使用MonomorphicCopierEmptyDestroyer这样会带来内存泄漏的错误配置。
这里面应该多一层抽象层,隔离开来。用户只要声明我用copy持有数据,或者说我用ownwed refernce,那么库的内部就会决定使用ElementDestroyer。
而用户声明要使用external refernce持有数据的话,库的内部就会决定使用EmptyDestroyer,不做显示的delete,即我不负责数据的释放。
后面会给出如何加上这一层抽象,所谓的Generator方法,让用户更高层次的设定参数。
 
OK,我们先实现出一个基本的PtrList,这是一个最基本的list,它有很多策略参数,具体策略的实现用户给出在Config中。
template <class Config_>
class PtrList
{
public:
  // export Config
  typedef Config_ Config;
  
private:
  // retrieve the needed types from the repository
  typedef typename Config::ElementType          ElementType;
  typedef typename Config::ElementArgumentType  ElementArgumentType;
  typedef typename Config::Copier               Copier;
  typedef typename Config::Destroyer            Destroyer;
  typedef typename Config::TypeChecker          TypeChecker;
  typedef typename Config::FinalListType        FinalListType;

  // data members
  ElementType*   head_;
  FinalListType* tail_;   // note: not PtrList* but FinalListType*

public:

  PtrList (ElementArgumentType& h, FinalListType *t = 0)
    : head_(0), tail_(t) { set_head(h); }

  ~PtrList() { Destroyer::destroy(head_); }

  void set_head(ElementArgumentType& h) {
    TypeChecker::check(h);
    head_ = Copier::copy(h);
  }

  ElementType& head() { return *head_; }

  void set_tail(FinalListType *t) { tail_ = t; }

  FinalListType* tail() const { return tail_; }

};

 

//对这个最基本的list的使用

template <class List>
void print_list(List* l) {
  std::cout << "[ ";
  for ( ; l; l = l->tail() ) std::cout << l->head() << " ";
  std::cout << "]\n";
}

template <class List>
void push_front(typename List::ElementArgumentType& e, List*& l) {
  l = new List(e, l);
}

int main()
{
  typedef ListConfig::FinalListType List;
  List* ls = 0;
  push_front(1,ls);
  push_front(2,ls);
  push_front(3,ls);
  print_list(ls);   // prints "3 2 1"
}

注意list的实现中将一些操作转交给别的类去做,也即某些策略参数,如Copier,Destoyer

template <class ElementType>
struct MonomorphicCopier {
  static ElementType* copy(const ElementType& e) { 
    return new ElementType(e);
  }
};

template <class ElementType>
struct PolymorphicCopier {
  static ElementType* copy(const ElementType& e) { 
    return e.clone();    // polymorphic copy using 
  }                      // virtual member function clone()
};

template <class ElementType>
struct EmptyCopier {
  static ElementType* copy(ElementType& e) {   // note: not const
    return &e;       // no copy
  }
};
template <class ElementType>
struct ElementDestroyer {
  static void destroy(ElementType* e) { delete e; }
};

template <class ElementType>
struct EmptyDestroyer {
  static void destroy(ElementType* e) {}  // do nothing
};

template <class ElementType>
struct DynamicTypeChecker {
  static void check(const ElementType& e) { 
    assert(typeid(e)==typeid(ElementType));
  }
};

template <class ElementType>
struct EmptyTypeChecker {
  static void check(const ElementType& e) {}
};
 
最后更上层的laer采用inheritance-based wrappers实现。如LengthList
template <class BasicList>
class LengthList : public BasicList
{
public:
  // export config
  typedef typename BasicList::Config Config;

private:
  // retrieve the needed types from the repository
  typedef typename Config::ElementType          ElementType;
  typedef typename Config::ElementArgumentType  ElementArgumentType;
  typedef typename Config::LengthType           LengthType;
  typedef typename Config::FinalListType        FinalListType;

  LengthType length_;

  LengthType compute_length() const {
    return tail() ? tail()->length()+1 : 1;
  }

public:

  LengthList (ElementArgumentType& h, FinalListType *t = 0)
    : BasicList(h, t) {
    length_ = compute_length();
  }

  void set_tail(FinalListType *t) {
    BasicList::set_tail(t);
    length_ = compute_length();
  }

  LengthType length() const { return length_; }

};
Generators

下面给出为了给用户更好的设置接口,进行抽象的方法。

enum Ownership {ext_ref, own_ref, cp};
enum Morphology {mono, poly};
enum CounterFlag {with_counter, no_counter};
enum TracingFlag {with_tracing, no_tracing};
上面是用户需要知道的,他们所应该关心的List配置参数。
template <bool condition, class Then, class Else>
struct IF {
  typedef Then RET;
};

template <class Then, class Else>
struct IF<false, Then, Else> {
  typedef Else RET;
};

上面是利用模版偏特化,编译期间选择类型的一种技巧。

下面是具体的Generator的实现

template <
  class       ElementType_,
  Ownership   ownership    = cp,
  Morphology  morphology   = mono,
  CounterFlag counter_flag = no_counter,
  TracingFlag tracing_flag = no_tracing,
  class       LengthType_  = int
>
class LIST_GENERATOR
{
public:

  // forward declaration of the configuration repository
  struct Config;

private:

  // define the constants used for type selection
  enum {
    is_copy      = ownership==cp,
    is_own_ref   = ownership==own_ref,
    is_mono      = morphology==mono,
    has_counter  = counter_flag==with_counter,
    does_tracing = tracing_flag==with_tracing 
  };

  // select the components
  typedef typename
  IF<is_copy || is_own_ref,
     ElementDestroyer<ElementType_>,
     EmptyDestroyer<ElementType_>
  >::RET Destroyer_;

  typedef typename
  IF<is_mono,
     DynamicTypeChecker<ElementType_>,
     EmptyTypeChecker<ElementType_>
  >::RET TypeChecker_;

  typedef typename
  IF<is_copy,
     typename 
     IF<is_mono,
	MonomorphicCopier<ElementType_>,
	PolymorphicCopier<ElementType_> >::RET,
     EmptyCopier<ElementType_>
  >::RET Copier_;

  typedef typename
  IF<is_copy,
     const ElementType_,
     ElementType_
  >::RET ElementArgumentType_;

  // define the list type
  typedef PtrList<Config> BasicList;

  typedef typename
  IF<has_counter,
     LengthList<BasicList>,
     BasicList
  >::RET OptLengthList;

  typedef typename
  IF<does_tracing,
     TracedList<OptLengthList>,
     OptLengthList
  >::RET List;

public:

  // return the final list type
  typedef List RET;

  // define the configuration repository
  struct Config {
    typedef ElementType_           ElementType;
    typedef ElementArgumentType_   ElementArgumentType;
    typedef Copier_                Copier;
    typedef Destroyer_             Destroyer;
    typedef TypeChecker_           TypeChecker;
    typedef LengthType_            LengthType;

    typedef RET                    FinalListType;
  };
};

 

用户可以这样使用来配置LIST
ypedef LIST_GENERATOR<int,cp,mono,no_counter,with_tracing>::RET
    TracedIntList;

如果不需要traing

typedef LIST_GENERATOR<int>::RET IntList;

 

最后Shape,Square,Circle用到虚函数,因为我们操作一些列图形几何,需要动态多态。

而list没有采用虚函数,只是继承类对父类的覆盖,因为对list没有用到动态多态,直接用子类指针。

另外当前这个程序只是演示GenVoca构架的思想,其实还存在很多问题,比如当前list如果是拷贝构造而来或者owned reference,它的析构函数只能析构一个list的首元素,其它的

元素无法析构,需要写类外面的一个destory函数,destory(list* l);
 
posted @ 2009-10-05 11:48  阁子  阅读(719)  评论(0编辑  收藏  举报