從二叉樹和iterator看程式碼結構設計 (關於adapter的運用) (轉)

worldblog發表於2008-01-31
從二叉樹和iterator看程式碼結構設計 (關於adapter的運用) (轉)[@more@]

前段時間,論壇上幾個人很熱烈地討論二叉樹裡做iterator。還有人專為此給一些C++界的名人寫信,儼然很專業的樣子。

老實說,我當時看了這個論題,感覺有點無聊,為什麼?因為二叉樹就是二叉樹,它的結構就是:左兒子,右兒子,父節點等。

iterator的結構是線性的前驅,後繼。

兩者雖然不是風馬牛不相及,但也應該是互相獨立的概念。

要給二叉樹做一個iterator不是不能,只要定義一個遍歷規則,把二叉樹以iterator的方式來訪問完全是可能的。

但是,這種可能就象拿stl可以做 programming一樣,但stl和Windows programming卻又是互相獨立的。 誰也不需要依賴誰。你要非在STL里加入windows sdk的東西,然後說:因為windows programming很常用,所以為了方便,我把HWND加入stl, 就亂套了。

把iterator和二叉樹的實現耦合起來考慮,在我看來,是明顯的設計問題。無謂地把簡單的東西複雜化。雖然可以做,但是寫出來的程式碼重用性和結構上都有毛病。

好了,毛病挑完了。下面該說說我認為正確的做法:

1。二叉樹就是二叉樹, 老老實實地把二叉樹的功能做好。不要考慮iterator.

2。 如果認為需要iterator, 如前序,中序,後序。別急。可以單獨做一個基於二叉樹的iterator的adapter. 這個adapter獨立於任何的二叉樹實現。它做的只是把二叉樹對映成線性訪問的iterator.

這個adapter自己定義一個二叉樹的介面。然後只需要把任何滿足這個介面的對映成iterator就成了。

3. 對一個二叉樹的實現,怎麼利用這個iterator的adapter呢?

把這個二叉樹先對映到這個adapter要求的介面。然後,把adapter往二叉樹上一套,iterator就出來了。這個iterator庫就象一個通用轉換器,往任何的二叉樹上一插,就行了。

這種結構的好處是,iterator不依賴於二叉樹的實現,二叉樹也不依賴於iterator. 一個二叉樹可以選用任何合適的adapter實現。而一個adapter也可以用於任何的一個二叉樹的實現。

 

下面是我剛剛的一段兩百多行的C++ bidirectional iterator adapter的程式碼。它可以用在任何的二叉樹實現上(當然,你得寫一個adapter把那個二叉樹對映到我的二叉樹介面上來,不要告訴我你不知道怎麼做)

 

namespace trit{
/*

自己定義的語法,二叉樹的介面就是這樣:
template
protocol Tree{
 NODE_PTR getLeft();
 NODE_PTR getRight();
 NODE_PTR getParent();
}

這個是生成的iterator介面:
template
protocol Iterator{
 operator bool();
 NODE_PTR deref();
 Self next();
 Self prev();
}
*/

 

template
class Iterator{
protected:
 typedef Iterator It;
public:
 NODE_PTR deref()const{
 return node;
 }
 operator bool()const{
 return node!=NODE_PTR(0);
 }
 Iterator(NODE_PTR const n):node(n){}
 friend bool operator==(const It& a, const It& b){
 return a.deref()==b.deref();
 }
 friend bool operator!=(const It& a, const It& b){
 return !(a==b);
 }
 It& operator=(const It& other){
 this->node = other.node;
 return *this;
 }
 It end(){
 return NODE_PTR(0);
 }
 static NODE_PTR get(NODE_PTR node){
 NODE_PTR ret(node);
 NODE_PTR tmp(ret->getParent());
 for(;tmp!=NODE_PTR(0);ret=tmp, tmp=ret->getParent());
 return ret;
 }
 static NODE_PTR getLeftmost(NODE_PTR node){
 NODE_PTR ret(node);
 NODE_PTR tmp(ret->getLeft());
 for(;!isNull(tmp);ret=tmp, tmp=ret->getLeft());
 return ret;
 }
 static NODE_PTR getLeftDeep(NODE_PTR node){
 NODE_PTR ret(node);
 for(;;){
 NODE_PTR tmpl(ret->getLeft());
 if(isNull(tmpl)){
 NODE_PTR tmpr(ret->getRight());
 if(isNull(tmpr)){
 return ret;
 }
 else{
 ret = tmpr;
 }
 }
 else{
 ret = tmpl;
 }
 }
 }
 static bool isNull(NODE_PTR ptr){
 return ptr==NODE_PTR(0);
 }
private:
 NODE_PTR node;
 
};

template
class Preorder;

template
class Postorder;
template
class Inorder;

template
class Preorder /*supports Iterator*/: public Iterator{

 typedef Preorder Self;
public:
 Self next()const{
 return goLeft(deref());
 }
 Self prev()const{
 typedef RevTree Adapter;
 Adapter rt(deref());
 Postorder it(rt);
 return Self(it.next().deref().getAdaptee());
 }
 Self begin()const{
 return Self(getRoot(deref()));
 }
private:

 static Self ret(NODE_PTR me, NODE_PTR from){
 if(isNull(me)){
 return Self(NODE_PTR(0));
 }
 else if(from==me->getLeft()){
 return goRight(me);
 }
 else{
 return goParent(me);
 }
 }
 
 static Self goLeft(NODE_PTR const node){
 NODE_PTR const left(node->getLeft());
 if(isNull(left)){
 return goRight(node);
 }
 else{
 return Self(left);
 }
 }
 static Self goRight(NODE_PTR const node){
 NODE_PTR const right(node->getRight());
 if(isNull(right)){
 return goParent(node);
 }
 else{
 return Self(right);
 }
 }
 static Self goParent(NODE_PTR const node){
 return ret(node->getParent(), node);
 }
public:
 Preorder(NODE_PTR const n):It(n){}
 Self& operator=(const It& other){
 return (Self&) It::operator=(other);
 }
};

template
class Inorder: public Iterator{
 typedef Inorder Self;
public:
 Self next()const{
 return goRight(deref());
 }
 Self prev()const{
 typedef RevTree Adapter;
 Adapter rt(deref());
 Inorder it(rt);
 return Self(it.next().deref().getAdaptee());
 }
 Self begin()const{
 return Self(getLeftmost(deref()));
 }
private:
 static Self ret(NODE_PTR const me, NODE_PTR const from){
 if(isNull(me)){
 return Self(NODE_PTR(0));
 }
 else if(me->getLeft()==from){
 return Self(me);
 }
 else{
 return goParent(me);
 }
 }

 static Self goRight(NODE_PTR const node){
 NODE_PTR const right(node->getRight());
 if(isNull(right)){
 return goParent(node);
 }
 else{
 return Self(getLeftmost(right));
 }
 }
 static Self goParent(NODE_PTR const node){
 return ret(node->getParent(), node);
 }
public: 
 Inorder(NODE_PTR const n):It(n){}
 Self& operator=(const It& other){
 return (Self&) It::operator=(other);
 }
};

template
class Postorder: public Iterator{
typedef Postorder Self;
public:
 Self next(){
 return goParent(deref());
 }
 Self prev(){
 typedef RevTree Adapter;
 Adapter rt(deref());
 Preorder it(rt);
 return Self(it.next().deref().getAdaptee());
 }
 Self begin(){
 return Self(getLeftDeep(deref()));
 }
 static Self ret(NODE_PTR const me, NODE_PTR const from){
 if(isNull(me)){
 return Self(NODE_PTR(0));
 }
 if(me->getLeft()==from){
 return goRight(me);
 }
 else{
 return Self(me);
 }
 }
private:
 static Self goRight(NODE_PTR const node){
 NODE_PTR const right(node->getRight());
 if(isNull(right)){
 return Self(node);
 }
 else{
 return Self(getLeftDeep(right));
 }
 }
 static Self goParent(NODE_PTR const node){
 return ret(node->getParent(), node);
 }
public:
 Postorder(NODE_PTR const n):It(n){}
 Self& operator=(const It& other){
 return (Self&) It::operator=(other);
 }

};


template
class RevTree/*supports Tree*/{
 typedef RevTree Self;
public:
 RevTree(NODE_PTR const t):tr(t){}
 Self getLeft()const{
 return Self(tr->getRight());
 }
 Self getRight()const{
 return Self(tr->getLeft());
 }
 Self getParent()const{
 return Self(tr->getParent());
 }
 const Self* operator->()const {return this;}

 bool operator == (const Self& b)const{
 return this->tr==b.tr;
 }
 bool operator== (const NODE_PTR b){
 return this->tr==b;
 }
 friend bool operator== (const NODE_PTR a, const Self& b){
 return a==b.tr;
 }
 Self& operator=(const Self& other){
 tr = other.tr;
 return *this;
 }

 NODE_PTR getAdaptee()const{return tr;}
private:
 NODE_PTR tr;
};

}

下面是測試程式碼,因為這裡的重點不是如何實現二叉樹, 它使用了一個非常簡易的二叉樹。只是為了表示你怎麼樣給一個任意的二叉樹提供iterator服務。

#include
#include "trit.h"
using namespace trit;
class TreeCons{
public:
 TreeCons* getLeft()const{return left;}
 TreeCons* getRight()const{return right;}
 TreeCons* getParent()const{return parent;}
 void setParent(TreeCons* p){this->parent = p;}
 const char* get(){return val;}
 TreeCons(TreeCons* l, TreeCons* r, const char* v)
 :left(l), right(r), parent(0), val(v){}
private:
 TreeCons* left;
 TreeCons* right;
 TreeCons* parent;
 const char* val;
};
typedef Preorder Pre;
typedef Inorder In;
typedef Postorder Post;

template
void printIt(IT it){
 for(;it; it=it.next()){
 cout << it.deref()->get();
 }
 cout <}
template
void printRev(IT it){
 for(;it; it=it.prev()){
 cout << it.deref()->get();
 }
 cout <}
int main(){
 TreeCons* d = new TreeCons(0, 0, "D");
 TreeCons* e = new TreeCons(0, 0, "E");
 TreeCons* b = new TreeCons(d, e, "B");
 d->setParent(b);
 e->setParent(b);
 TreeCons* f = new TreeCons(0, 0, "F");
 TreeCons* c = new TreeCons(0, f, "C");
 f->setParent(c);
 TreeCons* a = new TreeCons(b, c, "A");
 b->setParent(a);
 c->setParent(a);
 printIt(Pre(a));
 printIt(Pre(c).begin());
 printIt(In(a));
 printIt(In(a).begin());
 printIt(Post(a));
 printIt(Post(a).begin());
 printRev(Post(a));
 printRev(In(a));
 printRev(Pre(a));
 delete a;
 delete b;
 delete c;
 delete d;
 delete e;
 return 1;

}

也許你會覺得,這個iterator和stl的iterator的介面不大一致。呵呵,這個實現本著最小功能原則,其它的如operator++什麼的,自己寫個小adapter把介面轉換一下應該不難吧?

:〉

 

另外,我還寫了一個版本的binary tree -- iterator 的adapter. 利用java的gc功能, 它不需要節點有父指標。

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-998868/,如需轉載,請註明出處,否則將追究法律責任。

相關文章