重构成为保持C中多态性的组合
我可能会在将来遇到问题,我希望今天做好充分的准备.该问题涉及C上下文中的继承,多态和组合.我们如何将“继承代码重用”重构为组合并仍然能够保持多态方法?
我在这里寻找的是关于这个问题的更多“实际操作”指导.我来了一个非常简单的例子来向您展示,我相信您将能够阅读它并将其改进为我需要的答案. class Multilingual_entity { public: enum class t_languages {LAN_ENGLISH,LAN_RUSSIAN,LAN_CHINESE}; private: std::map<t_languages,std::string> texts; public: std::string set_text(t_language t,const std::string s) {texts[t]=s;} void get_text(t_language t) const {return texts.at(t);} } 后来这样扩展…… class Category_shopping_article:public Multilingual_entity { private: unsigned int pk_identifier; public: unsigned int get_pk() const {return pk_identifier;} //.... } class Shopping_article:public Multilingual_entity { private: unsigned int category_identifier; float price; public: //.... } 并应用如下: void fetch_all_titles_for(Multilingual_entity& m); Category_shopping_article get_category(unsigned int pk) { Category_shopping_article result=get_me_category_by_pk(pk); fetch_all_titles_for(result); return result; } std::vector<Shopping_article> get_articles_by_category(const Category_shopping_article& cat) { std::vector<Shopping_article> result=get_me_articles_by_category_id(cat.get_pk()); for(Shopping_article& a : result) fetch_all_titles_for(a); return result; } 正如您所看到的,一切都非常简单:我可以使用此定义一个小型购物目录(首先想到的例子)并将其以存储在某处的各种语言呈现给用户.假设语言存储在数据库中,因此“fetch_all_titles_for”将如下所示: void fetch_all_titles_for(Multilingual_entity& m) { Database_table T=m.get_database_language_table(); //I know,this is not implemented. Database_criteria C=m.get_database_language_criterie(); //Nor is this. std::map<Multilingual_entity::t_languages,const::std::string> texts=Database_object::get_me_the_info_i_need(T,C); for(const std::pair<Multilingual_entity::t_languages,const::std::string>& p : texts) m.set_texts(p.first,p.second); } 好吧,让我们说这是一个非常有限的快速启动,因为明天我将要为文章添加另一个“多语言文本属性”,以便我可以有一个描述.我不需要类别中的描述,所以我不能把它放在Multilingual_entity基类中…也许后天我会添加一个“text_review”,一切都会更加破碎,所以我们进入组合车皮: class Category_shopping_article: { private: unsigned int pk_identifier; Multilingual_entity titles; public: unsigned int get_pk() const {return pk_identifier;} std::string set_title(t_language t,const std::string s) {titles.set_text(t,s);} void get_title(t_language t) const {return titles.get_text(t);} } class Shopping_article: { private: unsigned int category_identifier; float price; Multilingual_entity titles; Multilingual_entity descriptions; public: std::string set_title(t_language t,s);} void get_title(t_language t) const {return titles.get_text(t);} std::string set_description(t_language t,const std::string s) {descriptions.set_text(t,s);} void get_description(t_language t) const {return descriptions.get_text(t);} } 好的,很好……现在有这些转发方法(我猜是可以容忍的)但我完全打破了“fetch_all_titles_for(Multilingual_entity& m)”的任何方法,因为不再有Multilingual_entity了.我熟悉“偏好合成而非继承”的经验法则,但在示例的开头,有一个基类可以提供有关在何处查看语言数据的信息. 这是一个问题……我是否必须利用权衡取舍或者我在这里遗漏了什么?是否有类似界面的解决方案可以帮助我解决这个问题?我想到了类似的东西: class Multilingual_consumer { private: std::vector<Multilingual_entity> entities; public: Multilingual_entity& add_entity() { entities.push_back(Multilingual_entity); return entities.back(); } Multilingual_entity& get_entity(unsigned int i) {return entities.at(i);} }; class Category_shopping_article:public Multilingual_consumer { private: unsigned int pk_identifier; enum entities{TITLE,DESCRIPTION}; public: unsigned int get_pk() const {return pk_identifier;} Category_shopping_article() { add_entity(); add_entity(); //Ugly... I know to come with something better than this but I could store references to these entities. } void get_title(Multilingual_entity::t_language t) const {return get_entity(TITLE).get_text(t);} void get_description(Multilingual_entity::t_language t) const {return get_entity(DESCRIPCION).get_text(t);} } 但似乎有很多障碍.关于如何能够组成许多多语言属性的对象并让它们可扩展的任何想法? 谢谢. 解决方法
一个简单的解决方案是将MultilingualEntity实例作为该类的公共成员:
class ShoppingItem { public: MultilingualEntity title; MultilingualEntity description; MultilingualEntity tech_specs; private: ... }; 这样您就可以直接访问方法,而无需创建额外的名称和编写转发器. 如果你是一个常见的偏执狂,你也可以让他们更难改变 class ShoppingArticle { public: const MultilingualEntity& title() const { return title_; } const MultilingualEntity& description() const { return description_; } const MultilingualEntity& tech_specs() const { return tech_specs_; } private: MultilingualEntity title_; MultilingualEntity description_; MultilingualEntity tech_specs_; ... }; 对于合成的每个元素,只需要一个额外的行. 要编写处理具有多语言实体部分的对象的泛型函数,您可以使用基于方法指针的访问器: // Search elements matching in current language template<typename T> std::set<int> searchItems(const std::string& needle,const std::vector<T>& haystack,const MultilingualEntity& (T::*a)() const) { std::set<int> result; for (int i=0,n=haystack.size(); i<n; i++) { if (match(needle,(haystack[i].*a)().get(current_language))) { result.insert(i); } } return result; } 然后使用它传递访问器: std::set<int> result = searchItems("this",items,&ShoppingItem::title); (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |