robot
最新文章(10)
Mqskit 和其它相關工具
CPython 的 GC 二、三事
寫 Mecurial Extension 是件快樂的事!
Mozilla 台灣辨公室徵人啟事
關於 Apple 的兩項專利
core dump 之前的 frame
怎麼發出 beep 聲?
先承認你要找的是奴才吧!
程式碼要清的多乾淨?
FreeBSD 的 Thread-Local Storage 實作
首頁
新編
最新留言
Entries RSS
重要關鍵字(10)
coding (122)
Python (93)
FreeBSD (71)
WEB (61)
URL (48)
hardware (46)
javascript (36)
Linux (34)
blog (30)
C++ (16)
所有關鍵字
新增 URL
Class 的動態組合
by thinker
2 Columns
關鍵字:
Python
coding
臨時有了一個$想法$,寫了一個 $Python$ 小工具, composite.py (文後)。緣起於 unittest 時,很難針對個別的功能進行測試。因為,有些 class 同時負責多種任務。例如,一個負責資料查詢的 class ,可能同時還得包括 權限檢查。例如: {{{#!python class query(object): def get_daily_total(self): if not is_authorized(): raise error .... def get_monthly_total(self): if not is_authorized(): raise error .... }}} 由於 query 有進行身分檢查,於是測試 query 時,就得連權限系統一起進行測試。或許用功一點的 programmer ,會寫成一個 linkname:decorator http://en.wikipedia.org/wiki/Decorator_pattern {{{#!python class query(object): def get_daily_total(self): .... def get_monthly_total(self): .... class authorized_query(query): def get_daily_total(self): if not is_authorized(): raise error return query.get_daily_total(self) def get_monthly_total(self): if not is_authorized(): raise error return query.get_monthly_total(self) }}} 如此一來,或者你可以對 query 進行獨立的測試,但 authorized_query 的權限檢查功能,卻得和 query 一起測試。若試圖提供多種權限機制,讓使用者設計、選擇,另一方面要保護的對象又多如失毛時,類似 authorized_query 這樣的 class 將疊的像 101 一樣高 (refactoring out generic wrapper see: Nathanael Schärli, Stéphane Ducasse, Oscar Nierstrasz and Andrew Black, linkname:["Traits: Composable Units of Behavior,"] http://www.iam.unibe.ch/~scg/Archive/Papers/Scha03aTraits.pdf , Proceedings of European Conference on Object-Oriented Programming (ECOOP'03), LNCS, vol. 2743, Springer Verlag, July 2003, pp. 248-274.)。如果你還要加上 cache 的功能,避免不必要的計算,於是隨著 aspect 的增加, class 將堆的如玉山雄偉。 這時侯, linkname:mixin http://en.wikipedia.org/wiki/Mixin 或 linkname:trait http://en.wikipedia.org/wiki/Trait_%28abstract_type%29 就變成一項很有利的工具。這裡提供的工具,和 mixin 有些差異,接近 trait ,但又不完全是。算是一個混合的作法。前面的例子,可改寫成 {{{#!python class query(object): def real_get_daily_total(self): .... def real_get_monthly_total(self): .... class authory(object): def get_daily_total(self): if not is_authorized(): raise error return self._get_daily_total(self) def get_monthly_total(self): if not is_authorized(): raise error return self._get_monthly_total(self) def _get_daily_total(self): raise NotImplementedError def _get_monthly_total(self): raise NotImplementedError import composite auth_query = composite.composite(query, authory) composite.link(auth_query, authory._get_daily_total, query.real_get_daily_total) composite.link(auth_query, authory._get_monthly_total, query.real_get_monthly_total) }}} `auth_query' 是組合 `query' 和 `authory' 動態產生的。 `authory' 定義了兩個待實作的 method (raise NotImplementedError), 分別為 `_get_daily_total' 和 `_get_monthly_total' 。我們透過 `composite.link' 這個指令,將這兩個 method 重新指向 `query.real_get_daily_total' 和 `query.real_get_monthly_total' 。如此 `query' 和 `authory' 就能分藕 (decouple)。於是 `auth_query' 就能依使用者的設定,和不同的 authory 組合成新的 class 。 以前書中會將繼承寫的很萬能,但如果仔細回想 OO 的目的,會發覺基本的 reuse 都很難達到,要不然就要設計一個碩大的 framework 。事實上,傳統以繼承為主的 OO 存在許多難處,讓 programmer 到處受限,弄了一堆 pattern 也解決不了問題。 mixin 或 trait 之類的 class 組合工具,反而提供更多有彈性的,找回 OO 的初衷。或許我們應該更 open mind ,接受有別於傳統 OO 的更多可能性。 == 程式碼 == {{{#!python ## \file # \brief Class composition tools. # # This is a set of tools to composing classes. # It provides tools to generate a new class from other classes. # It also includes tools to re-link methods from composed classes # for the new class. # # With class composition tools, programmer can seperate concerns # into classes. The classes are tested independently and composed # to create new classes from classes with different aspect. # # A composed class is dealing about an aspect of concern. It # is template class that some methods may are not implemented. # Programmers use classes to generate a new class should # re-link not implemented methods to the method provides by other # composed classes to complete the implementation of the composed # classes. ## \brief Composite classes to create a new class. # # It makes composed classes as base classes of the new class. def composite(clz_name, *clazzs): new_clz = type(clz_name, clazzs, {}) return new_clz ## \brief Re-link a source method to destination method. # # It relinks methods of a composition class. def link(composition, src_method, dst_method): setattr(composition, src_method.__name__, dst_method) pass ## \brief Relink method pairs of a composition class. def slink(composition, src_dst_pairs): for src, dst in src_dst_pairs: setattr(compsition, src.__name__, dst) pass pass }}}
最後更新時間: 2007-11-11 23:23:20 CST |
引用
查詢:
COMMENTS: