组件系统

从基类派生出新的类时,往往会添加新的方法,当该类的对象以基类指针的形式表现出来的时候,我们就无法调用这些的新的方法了。从概念上来看,是因为行为绑定到类型上,父类自然无法调用子类新的行为。在面向对象编程中,行为与类型耦合的比较紧密。

组件系统将对象与行为解耦,所有对象都表现为组件指针,所有行为都表现为接口指针。通过查询接口来获取行为。所有组件都从工厂类产生。

以下是示例代码,代码是从游戏编程精粹里修改而来。

首先我们需要一个类型标识类,因为当对象都表现为组件指针时,只有依赖类型标识类才能区分。

 1 /*------------------------------------------------------------------
 2 // 著作版权:Copyright (C) liuxb
 3 // 创建时间:[liuxb|20131005]
 4 // 功能描述:类型id
 5 //
 6 // 修改时间:
 7 // 修改描述:
 8 // 
 9 //----------------------------------------------------------------*/
10 
11 #pragma once
12 
13 #include <cassert>
14 
15 
16 // 类型id
17 class classid
18 {
19 public:
20     const static unsigned int INVALID_HASH = 0xffffffff; 
21 
22 public:
23     classid(void) { m_hash_value = INVALID_HASH; }
24     explicit classid(unsigned int hash_value) : m_hash_value(hash_value) {}
25     explicit classid(const char* class_name) { m_hash_value = make_hash(class_name); }
26     classid(const classid& rhs) : m_hash_value(rhs.m_hash_value) {}
27     
28     template<typename Type>
29     classid(Type const* type);
30 
31     inline classid& operator = (const classid& rhs);
32     inline classid& operator = (unsigned int hash_value);
33 
34 public:
35     bool is_valid() { return m_hash_value == INVALID_HASH; }
36     operator unsigned int(void) { return m_hash_value; }
37 
38     const bool operator<(const classid& rhs) const { return m_hash_value<rhs.m_hash_value; }
39     const bool operator<=(const classid& rhs) const { return m_hash_value<=rhs.m_hash_value; }
40     const bool operator>(const classid& rhs) const { return m_hash_value>rhs.m_hash_value; }
41     const bool operator>=(const classid& rhs) const { return m_hash_value>=rhs.m_hash_value; }
42     const bool operator==(const classid& rhs) const { return m_hash_value==rhs.m_hash_value; }
43     const bool operator!=(const classid& rhs) const { return m_hash_value!=rhs.m_hash_value; }
44     
45 
46 private:
47     inline unsigned int make_hash(const char* class_name);
48 
49 private:
50     unsigned int m_hash_value;
51 };
52 
53 
54 
55 
56 
57 inline classid& classid::operator = (const classid& rhs) 
58 {
59     if(&rhs != this) m_hash_value = rhs.m_hash_value;
60     return *this;
61 }
62 
63 inline classid& classid::operator = (unsigned int hash_value) 
64 {
65     m_hash_value = hash_value;
66     return *this;
67 }
68 
69 inline unsigned int classid::make_hash(const char* class_name)
70 {
71     const static unsigned int HASH_INIT = 0x811c9dc5;
72     const static unsigned int HASH_PRIME = 0x01000193;
73 
74     assert(class_name!=0 && class_name[0]!=0);
75     const unsigned char* pname = (const unsigned char*)class_name;
76     unsigned int hash = HASH_INIT;
77 
78     while (*pname)
79     {
80         hash *= HASH_PRIME;
81         hash ^= (unsigned int)(*pname);
82         ++pname;
83     }
84     return hash;
85 }
86 
87 template<typename Type>
88 inline classid::classid(Type const* type)
89 {
90     m_hash_value = make_hash(typeid(Type).name());
91 }
View Code

接下来需要一个接口标识类,用于保存组件里的接口指针。本来类型标识类也足以完成这个功能,但是游戏编程精粹里给接口添加了一个版本号的功能。而我这为了简单就没有使用该类。

 1 /*------------------------------------------------------------------
 2 // 著作版权:Copyright (C) liuxb
 3 // 创建时间:[liuxb|20131006]
 4 // 功能描述:接口标识id
 5 //
 6 // 修改时间:
 7 // 修改描述:
 8 // 
 9 //----------------------------------------------------------------*/
10 
11 #pragma once
12 
13 #include "class_version.hpp"
14 #include "classid.hpp"
15 
16 struct interfaceid
17 {
18 public:
19     classid classid;
20     int version;
21 
22 public:
23     template<class Type>
24     explicit interfaceid(const Type* type)
25         : version(version_number(type))
26         , classid(type)
27     {
28 
29     }
30 
31 public:
32     int version_number(...) { return 0; }
33 
34     template<int number>
35     int version_number(const class_version<number>*)
36     {
37         return number;
38     }
39 
40     bool operator==(const interfaceid& iid)
41     {
42         return    (version == -iid.version || version == iid.version) && classid == iid.classid;
43     }
44 
45 
46     bool operator<(const interfaceid& iid) const
47     {
48         if(classid == iid.classid) return version < iid.version;
49         return classid < iid.classid;
50     }
51 };
52 
53 
54 
55 struct interfaceid_withno_version
56 {
57     classid classid;
58 
59 
60     template<class Type>
61     explicit interfaceid_withno_version(const Type* type)
62         :    classid(type)
63     {}
64 
65 
66     bool operator==(const interfaceid_withno_version& iid)
67     {
68         return    classid == iid.classid;
69     }
70 
71     bool operator<(const interfaceid_withno_version& iid) const
72     {
73         return classid < iid.classid;
74     }
75 };
View Code

有了classid,interfaceid类之后就可以写组件基类了。handle_message方法使得对象可以接收以及处理感兴趣的事件。

 1 /*------------------------------------------------------------------
 2 // 著作版权:Copyright (C) liuxb
 3 // 创建时间:[liuxb|20131005]
 4 // 功能描述:组件接口
 5 //
 6 // 修改时间:
 7 // 修改描述:
 8 // 
 9 //----------------------------------------------------------------*/
10 
11 #pragma once
12 
13 #include <map>
14 #include "classid.hpp"
15 #include "interfaceid.hpp"
16 
17 
18 // 消息处理函数的返回类型
19 enum emessage_result
20 {
21     mr_false,
22     mr_true,
23     mr_ignored,
24     mr_error
25 };
26 
27 
28 // 前置声明
29 class ientity;
30 class message;
31 
32 
33 
34 // 组件接口
35 class icompoenent
36 {
37     typedef interfaceid_withno_version                    interfaceid;
38     typedef std::map<interfaceid, void*>                interface_map;
39 public:
40     virtual ~icompoenent(void) {};
41 
42 public:
43     virtual emessage_result        handle_message(const ientity* entity, const message& msg) { return mr_ignored; }
44     classid            get_classid(void) const { return m_classid; }
45 
46     // 添加需要导出的接口指针,用于对象构造时确定需导出的接口指针
47     // 
48     template<class Type>
49     void            expose_interface(Type* type);
50     // 查询接口指针
51     template<class Type>
52     Type*            query_interface();
53     
54 private:
55     void            set_classid(const classid& oid) { m_classid = oid; }
56     void*            query_interface(const interfaceid& id);
57 private:
58     classid            m_classid;
59     interface_map    m_interface_map;
60 };
61 
62 
63 
64 template<class Type>
65 inline void icompoenent::expose_interface(Type* type)
66 {
67     m_interface_map[interfaceid(type)] = type;
68 }
69 
70 template<class Type>
71 inline Type* icompoenent::query_interface()
72 {
73     Type* type = 0;
74     return reinterpret_cast<Type*>(query_interface(interfaceid(type)));
75 }
76 
77 inline void* icompoenent::query_interface(const interfaceid& id)
78 {
79     interface_map::iterator it = m_interface_map.find(id);
80     if(it!=m_interface_map.end()) return it->second;
81     return 0;
82 }
View Code

最后一个是工厂类,如果写的更精细一点,应该将create函数模板化,以接受带参数的对象构造函数。这里没有提供,各位可以自行添加。

  1 /*------------------------------------------------------------------
  2 // 著作版权:Copyright (C) liuxb
  3 // 创建时间:[liuxb|20131005]
  4 // 功能描述:工厂
  5 //
  6 // 修改时间:
  7 // 修改描述:
  8 // 
  9 //----------------------------------------------------------------*/
 10 
 11 #pragma once
 12 
 13 #include <cassert>
 14 #include <list>
 15 #include <map>
 16 
 17 #include "classid.hpp"
 18 #include "icompoenent.hpp"
 19 
 20 // 工厂类
 21 class factory
 22 {
 23 public:
 24     virtual        ~factory(void) ;
 25 
 26 public:
 27     static        factory& singleton();
 28 
 29 public:
 30     template<class Type> 
 31     icompoenent*    create(void);
 32 
 33 public:
 34     template<class Type> 
 35     void            remove_support(void);
 36 
 37     template<class Type> 
 38     void            support(void);    
 39 
 40     template<class Type>
 41     bool            is_supported();
 42 
 43     classid            supported_typeid(unsigned int index);
 44     unsigned int    number_of_supported_types();
 45 
 46 private:
 47     struct base_constructor
 48     {
 49         virtual icompoenent* construct() = 0;
 50     };
 51 
 52     template<class Type>
 53     struct constructor : base_constructor
 54     {
 55         icompoenent* construct()
 56         {
 57             return new Type();
 58         }
 59     };
 60 
 61     typedef std::map<classid, base_constructor*> constructor_map;
 62 
 63 private:
 64     base_constructor* find_constructor(const classid& id);
 65 
 66     icompoenent*    create(const classid& id);
 67     bool            is_supported(const classid& id);
 68     void            remove_support(const classid& id);
 69 
 70 private:
 71     constructor_map        m_constructor_map;
 72 };
 73 
 74 
 75 
 76 inline factory::~factory()
 77 {
 78     typedef constructor_map::iterator iterator;
 79     for(iterator it = m_constructor_map.begin(); it!=m_constructor_map.end(); ++it)
 80     {
 81         delete it->second;
 82     }
 83 }
 84 
 85 
 86 inline factory& factory::singleton()
 87 {
 88     static factory    fac;
 89     return fac;
 90 }
 91 
 92 
 93 
 94 
 95 inline icompoenent* factory::create(const classid& id)
 96 {
 97     base_constructor* constructor = find_constructor(id);
 98     if(constructor) return constructor->construct();
 99     return 0;
100 }
101 
102 
103 template<class Type>
104 inline icompoenent* factory::create(void)
105 {
106     Type* type = 0;
107     return create(classid(type));
108 }
109 
110 
111 
112 inline void factory::remove_support(const classid& id)
113 {
114     constructor_map::iterator it = m_constructor_map.find(id);
115     if(it!=m_constructor_map.end())
116     {
117         delete it->second;
118         m_constructor_map.erase(it);
119     }
120 }
121 
122 
123 template<class Type>
124 inline void factory::remove_support()
125 {
126     Type* type = 0;
127     remove_support(classid(type));
128 }
129 
130 
131 template<class Type>
132 inline void factory::support()
133 {
134     Type* type = 0;
135     m_constructor_map[classid(type)] = new constructor<Type>();
136 }
137 
138 
139 inline classid factory::supported_typeid(unsigned int index)
140 {
141     constructor_map::iterator it = m_constructor_map.begin();
142     assert(index < m_constructor_map.size());
143 
144     while(index--) 
145     {
146         ++it;
147     }
148     return it->first;
149 }
150 
151 
152 
153 inline unsigned int factory::number_of_supported_types()
154 {
155     return m_constructor_map.size();
156 }
157 
158 
159 inline bool factory::is_supported(const classid& id)
160 {
161     return find_constructor(id)!=0;
162 }
163 
164 
165 template<class Type>
166 inline bool factory::is_supported()
167 {
168     Type* type = 0;
169     return is_supported(classid(type))!=0;
170 }
171 
172 
173 inline factory::base_constructor* factory::find_constructor(const classid& id)
174 {
175     constructor_map::iterator it = m_constructor_map.find(id);
176     if(it!=m_constructor_map.end()) return it->second;
177     return 0;
178 }
179 
180 // 让生活变得更美好
181 #define Factory                factory::singleton()
View Code

至此一个简单的组件系统就有了。接下来是测试代码。

 1 // component_system.cpp : 定义控制台应用程序的入口点。
 2 //
 3 
 4 #include "stdafx.h"
 5 
 6 #include "factory.hpp"
 7 #include "interfaceid.hpp"
 8 #include "classid.hpp"
 9 #include "icompoenent.hpp"
10 
11 #include <string>
12 
13 
14 struct iname
15 {
16     virtual const char* name() = 0;
17 };
18 
19 struct ipos
20 {
21     virtual int length() = 0;
22 };
23 
24 struct position : public ipos
25 {
26     int x;
27     int y;
28 
29     virtual int length() { return x*x + y*y; }
30 };
31 
32 
33 class compoenent : public icompoenent, public iname
34 {
35 public:
36     compoenent()
37     {
38         expose_interface<ipos>(&m_pos);
39         expose_interface<iname>(this);
40         m_name = "liuxb";
41         m_pos.x = 3;
42         m_pos.y = 4;
43     }
44 public:
45     const char* name() { return m_name.c_str(); }
46 
47 private:
48     std::string        m_name;
49     position        m_pos;
50 };
51 
52 
53 int _tmain(int argc, _TCHAR* argv[])
54 {
55     Factory.support<compoenent>();
56 
57     icompoenent* pcmp = factory::singleton().create<compoenent>();
58 
59     iname* name = pcmp->query_interface<iname>();
60     ipos* ppos = pcmp->query_interface<ipos>();
61 
62 
63     printf("cmp name = %s\n", name->name());
64     printf("cmp length = %d\n", ppos->length());
65 
66     return 0;
67 }
View Code

 

 

 

 

posted @ 2013-10-07 01:10  traits  阅读(287)  评论(0编辑  收藏  举报