从基类派生出新的类时,往往会添加新的方法,当该类的对象以基类指针的形式表现出来的时候,我们就无法调用这些的新的方法了。从概念上来看,是因为行为绑定到类型上,父类自然无法调用子类新的行为。在面向对象编程中,行为与类型耦合的比较紧密。
组件系统将对象与行为解耦,所有对象都表现为组件指针,所有行为都表现为接口指针。通过查询接口来获取行为。所有组件都从工厂类产生。
以下是示例代码,代码是从游戏编程精粹里修改而来。
首先我们需要一个类型标识类,因为当对象都表现为组件指针时,只有依赖类型标识类才能区分。
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 }
接下来需要一个接口标识类,用于保存组件里的接口指针。本来类型标识类也足以完成这个功能,但是游戏编程精粹里给接口添加了一个版本号的功能。而我这为了简单就没有使用该类。
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 };
有了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 }
最后一个是工厂类,如果写的更精细一点,应该将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()
至此一个简单的组件系统就有了。接下来是测试代码。
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 }