說明:寫本文的時候作者完全是把腦子里的東西寫了出來,沒有參考任何的資料,所以對于每一項內容可能都是不完整的,不能作為一個完整的參考。有一些方法學的東西每個人都有自己的喜好,沒有覺得的對和錯。
單元測試
在這之前我們寫的框架只能說是一個在最基本的情況下可以使用的框架,作為一個框架我們無法預測開發人員將來會怎么使用它,所以我們需要做大量的工作來確保框架不但各種功能都是正確的,而且還是健壯的。寫應用系統的代碼,大多數項目是不會去寫單元測試的,原因很多:
- 項目趕時間,連做一些輸入驗證都沒時間搞,哪里有時間寫測試代碼。
- 項目對各項功能的質量要求不高,只要能在標準的操作流程下功能可用即可。
- 項目基本不會去改或是臨時項目,一旦測試通過之后就始終是這樣子了,沒有迭代。
- ……
對于框架,恰恰相反,沒有配套的單元測試的框架(也就是僅僅使用人工的方式進行測試,比如在main中調用一些方法觀察日志或輸出,或者運行一下示例項目查看各種功能是否正常,是非常可怕的)原因如下:
- 自動化程度高,回歸需要的時間短,甚至可以整合到構建過程中進行,這是人工測試無法實現的。
- 框架一定是有非常多的迭代和重構的, 每一次修改雖然只改了A功能,但是可能會影響到B和C功能,人工測試的話你可能只會驗證A是否正常,容易忽略B和C,使用單元測試的話只要所有功能都有覆蓋,那么幾乎不可能遺漏因為修改導致的潛在問題,而且還能反饋出來因為修改導致的兼容性問題。
- 之前說過,一旦框架開放出去,框架的使用者可能會以各種方式在各種環境來使用你的框架,環境不同會造成很多怪異的邊界輸入或非法輸入,需要使用單元測試對代碼進行嚴格的邊界測試,以確保框架可以在嚴酷的環境下生存。
- 單元測試還能幫助我們改善設計,在寫單元測試的時候如果發現目標代碼非常難以進行模擬難以構建有效的單元測試,那么說明目標代碼可能有強依賴或職責過于復雜,一個被單元測試高度覆蓋的框架往往是設計精良的,符合高內聚低耦合的框架。
如果框架的時間需求不是特別緊的話,單元測試的引入可以是走通主線流程的階段就引入,越早引入框架的成熟度可能就會越高,以后重構返工的機會會越小,框架的可靠性也肯定會大幅提高。之前我有寫過一個類庫項目,并沒有寫單元測試,在項目中使用了這個類庫一段時間也沒有出現任何問題,后來花了一點時間為類庫寫了單元測試,出乎我意料之外的是,我的類庫提供的所有API中有超過一半是無法通過單元測試的(原以為這是一個成熟的類庫,其實包含了數十個BUG),甚至其中有一個API是在我的項目中使用的。你可能會問,為什么在使用這個API的時候沒有發生問題而在單元測試的時候發生問題了呢?原因之前提到過,我是框架的設計者,我在使用類庫提供的API的時候是知道使用的最佳實踐的,因此我在使用的時候為類庫進行了一個特別的設置,這個問題如果不是通過單元測試暴露的話,那么其它人在使用這個類庫的時候基本都會遇到一個潛在的BUG。
示范項目
寫一個示例項目不僅僅是為了給別人參考,而且還能夠幫助自己去完善框架,對于示例項目,最好兼顧下面幾點:
- 是一個具有一定意義的網站或系統,而不是純粹為了演示特性而演示。這是因為,很多時候只有那些真正的業務邏輯才會暴露出問題,演示特性的時候我們總是有一些定勢思維會規避很多問題。或者可以提供兩個項目,一個純粹演示特性,一個是示例項目。
- 覆蓋盡可能多的特性或使用難點,在項目的代碼中提供一些注釋,很多開發人員不喜歡閱讀文檔,反而喜歡看一下示例項目直接上手(模仿示例項目,或直接拿示例項目中的代碼來修改)。
- 項目中的代碼,特別是涉及到框架使用的代碼一定要規范,原因上面也說了,作為框架的設計者你不會希望大家復制的代碼粘帖的代碼一團糟吧。
- 如果你的項目針對的不僅僅是Web項目,那么示例項目最好提供Web和桌面兩個版本,一來你自己容易發現因為環境不同帶來的使用差異,二來可以給予不同類型項目不同的最佳實踐。
完善日志和異常
一個好的框架不但需要設計精良,日志和異常的處理是否到位也是非常重要的標準,這里有一些反例:
- 日志的各種級別的使用沒有統一的標準,甚至是永遠只使用某個級別的日志。
- 幾乎沒有任何的日志,框架的運行完全是一個黑盒。
- 記錄的日志多且沒有實際含義,只是調試的時候用來觀察變量的內容。
- 異常類型只使用Exception,不使用更具體化的類型,沒有自定義類型。
- 異常的消息文本只寫"錯誤"字樣,不寫清楚具體的問題所在。
- 永遠只是拋出異常,讓異常上升到最外層,交給框架的使用者去處理。
- 用異常來控制代碼流程,或本應該在方法未達到預期效果的時候使用異常卻使用返回值。
其實個人覺得,一個框架的主邏輯代碼并不一定是最難的,最難的是對一些細節的處理,讓框架保持一套規范的統一的日志和異常的使用反而對框架開發者來說是一個難點,下面是針對記錄日志的一些建議:
-
首先要對框架使用的日志級別有一個規范,比如定義:
- DEBUG:用于觀察程序的運行流程,僅在調試的時候開啟
- INFO:用于告知程序運行狀態或階段的變化,可以在測試環境開啟
- WARNING:用于告知程序可以自己恢復的錯誤或異常,或不影響主線流程執行的錯誤或問題,可以在正式環境開啟
- ERROR:用于告知程序無法恢復,主線流程中斷,需要開發或運維人員知曉干預的錯誤或異常,需要在正式環境開啟
- 按照上面的級別規范,在需要記錄日志的地方記錄日志,除了DEBUG級別的日志其它日志不能記錄過多,如果框架總是在運行的時候輸出幾十個WARNNING也容易讓使用者忽略真正的問題。
- 日志記錄的消息需要是明確的,最好包含一些上下文信息,比如"無法在xxx下找到配置文件xxx.config,框架將采用默認的配置",而不是"加載配置失敗!"
下面是一些針對使用異常的建議:
- 框架由于配置錯誤或使用錯誤或運行錯誤,不能完成API名字所表示的功能,考慮拋出轉化后的異常,讓調用者知道發什么了什么情況,同時框架可以建立自己的錯誤處理機制
- 對于可以預料的錯誤,并且錯誤類型可以枚舉,考慮以返回值的形式告知調用者可以根據不同的結果來處理后續的邏輯
- 對于框架內部功能實現上遇到的調用者無能力解決的錯誤,如果錯誤可以重試或不影響返回,可以記錄警告或錯誤日志
- 可以為每一個模塊都陪伴自定義的異常類型,包含相關的上下文信息(比如ViewException可以包含ViewContext),這樣出現異常可以很方便知曉是哪個模塊出現問題并且可以得到出現異常時的環境信息
- 如果異常跨了實現層次(比如從框架到應用),那么最好進行一下包裝轉換(比如把文件讀取失敗的提示改為加載配置文件失敗的提示),否則上層人員是不知道怎么處理這些內部問題的,內部問題需要由框架自己來處理
- 異常的日志中可以記錄和當前操作密切相關的參數信息,比如搜索的路徑,視圖名等等,有關方法的信息不用過多記錄,異常一般都帶有調用棧信息
- 如果可能的話,出現異常的時候可以分析一下為什么會出現這樣的問題,在異常信息中給一些解決問題的建議或幫助鏈接方便使用者排查問題
-
異常處理從壞到好的層次是,出現了嚴重問題的時候:
- 使用者什么都不知道,程序的完整性和邏輯得到破壞
- 使用者既不知道出現了什么問題也不知道怎么去解決
- 使用者能明確知道出現了什么問題,但無法去解決
- 使用者不但知道發生了什么,還能通過異常消息的引導快速解決問題
完善配置
配置的部分可以留到框架寫的差不多了再去寫,因為這個時候已經可以想清楚哪些配置是:
- 需要公開出去給使用者配置的,并且配置會根據環境不同而不同
- 需要公開出去給使用者來配置的,配置和部署環境無關
- 僅僅需要在框架內供框架開發人員來配置的
- 無需是一個配置,只要在代碼中集中存儲這個設定即可
一般來說配置有幾種方式:
- 通過配置文件來配置,比如XML文件、JSON文件或property文件
- 通過注解或特性(Annotation/Attribute)方式(對類、方法、參數)進行配置
- 通過代碼方式進行配置(比如單獨的配置類,或實現配置類或調用框架的配置API)
很多框架提供了多種配置方式,比如Spring MVC同時支持上面三種方式的配置,個人覺得對配置,我們還是應該區別對待,而不是無腦把所有的配置項都同時以上面三種方式提供配置,我們要考慮高內聚和低耦合原則,對于Web框架來說,高內聚需要考慮的比低耦合更多,我的建議是對不同的配置項提供不同的配置方式:
-
如果配置項目是需要讓使用者來配置的,特別是和環境相關的,那么最好使用配置方式來配置,比如開放的端口、內存、線程數配置,不過要注意:
- 所有配置項目需要有默認值,如果找不到配置使用默認值,如果配置不合理使用默認值(你不會希望使用你框架的人把框架內部的線程池的min設置為999999,或定時器的間隔設置為0毫秒吧?)
- 框架啟動的時候檢測所有配置,如果不合理給予提示,大多人只會在啟動的時候看一下日志,使用的時候根本就不管
- 不知道大家對于配置文件的格式傾向于XML呢還是JSON呢還是鍵值對呢?
-
對于所有僅在開發時進行的配置,都盡量不要去使用配置文件,并且讓配置盡量和它所配置的對象靠在一起:
- 如果是對框架整體性進行的設置擴展類型的配置,那就可以提供代碼方式進行配置,比如我們要實現的MVC框架的各種IRoute、IViewEngine等,最好可以提供IConfig接口讓開發人員可以去實現接口,這樣他們可以知道有哪些東西可以配置,代碼就是文檔
- 如果是那種對模型、Action進行的配置,比如模型的驗證規則、Filter等一律采用注解的方式進行配置
- 有的人說使用配置文件進行配置非常靈活,使用代碼方式和注解方式來配置不靈活而且可能有侵入性。我覺得還是要權衡對待,我的建議是不要把太多框架內在的東西放在配置文件中,增加使用者的難度(而且很多時候,大多數人只是復制配置為了完成配置而配置,并不是為了真正的靈活性而去使用配置文件來配置你的框架,看看網上這么所SSH配置文件的抄來抄去就知道了)。
- 最后,我建議很多太內部的東西對于輕量級的應用型框架可以不去提供任何配置選項,只需要在某個常量文件中定義即可,讓真正有需求進行二次開發的開發人員去修改,對于一個框架如果一下子暴露上百個"高級"配置項給使用者,他們會暈眩的。
提供狀態服務
所謂狀態服務就是反映框架內部運作狀態的服務,很多開源服務或系統(Nginx、Mongodb等)都提供了類似的模塊和功能,作為框架的話我覺得也有必要提供一些內部信息(主要是配置、數據統計以及內部資源狀態)出來,這樣使用你框架的人可以在開發的時候或線上運作的時候了解框架的運作狀態,我們舉兩個例子,對于一個我們之前提到的Web MVC框架來說,可以提供這些信息:
- 路由配置
- 視圖引擎配置
- 過濾器配置
對于一個Socket框架來說,有一些不同,Socket框架是有狀態的,其狀態服務提供的信息除了當前生效的配置信息之外,更多的是反映當前框架內部一些資源的狀態以及統計數據:
- 各種配置(池配置、隊列配置、集群配置)
- Socket相關的統計數據(總打開、總關閉、每秒收發數據、總收發數據、當前打開等等)
- 各種池的當前狀態
- 各種隊列的當前狀態
狀態服務可以以下面幾種形式來提供:
- 代碼方式,比如如果開發人員實現了IXXXStateAware接口的話,就可以為它的實現類來推送一些信息,也可以直接在框架中設立一個StateCenter來公開框架所有的狀態信息
- 自動日志方式,比如如果在配置中開啟了stateLoggingInterval=60s的選項,我們的框架就會自動一分鐘一次輸出日志,顯示框架內部的狀態
- 接口方式,比如開放一個Restful的接口或額外監聽一個端口來提供狀態服務,方便使用者可以拿原始的數據和其它監控平臺進行整合
-
內部外部工具方式
- 比如我們可以直接為框架提供一個專門的頁面(/_route)來呈現路由的配置(甚至我們可以在這個頁面上讓開發人員可以直接輸入地址來測試路由的匹配情況,狀態服務不一定只能看),這樣在開發和測試的時候可以更方便調試
- 我們也可以為框架提供一個專有工具來查看框架的狀態信息(當然,這個工具其實可能就是連接框架的某個網絡服務來獲取數據),這樣即使框架在多個機器中使用,我們可能也只有一個監控工具即可
如果沒有狀態服務,那么在運行的時候框架就是一個黑盒,反之如果狀態服務足夠詳細的話,可以方便我們排查一些功能或性能問題。不過要注意的一點是,狀體服務可能會降低框架的性能,我們可能需要對狀態服務也進行一次壓測,排除狀態服務中損耗性能的地方(有些數據的收集會意想不到得損耗性能)。
檢查線程安全
框架對多線程環境支持的是否好,是框架質量的一個重要的評估標準,往往可以看到甚至有一些成熟的框架也會有多線程問題。這里涉及幾個方面:
1,你無法預料框架的使用者會怎么樣去實例化和保存你的API的入口類,如果你的入口類被用成為了一個單例,在并發調用的情況下會不會有單線程問題?
這是一個老話題,之前已經說過很多次,你在設計框架的時候心里如果把一個類定位成了單例的類但卻沒有提供單例模式,你是無法要求使用者來幫你實現單例的。這其中涉及的不僅僅是多線程問題,可能還有性能問題。比如見過某分布式緩存的客戶端的CacheClient在文檔中要求使用者針對一個緩存集群保持一個CacheClient的單例(因為其中有了連接池),但是用的人還是每一次都實例化了一個CacheClient出來,幾小時后就會產生幾萬個半死的Socket導致網絡奔潰。又見過某類庫的入口工廠的代碼注釋中寫了要求使用的人把XXXFactory作為單例來使用(因為其中緩存了大量數據),但是用的人就沒有注意到這個注釋,每一次都實例化了一個XXXFactory,造成GC的崩潰。所以我覺得作為框架的設計者開發人員,最好還是把框架的最佳實踐直接做到API中,使得使用者不可能出錯(之前說過一句話,再重復一次,好的框架不會讓使用的人犯錯)。你可能會說對于CacheClient的例子,不可能做成單例的,因為我的程序可能需要用到多個緩存的集群,換個思路,我們完全可以在封裝一層,通過一個CacheClientCreator之類的類來管理多個單例的CacheClient。即使在某些極端的情況下,你不能只提供一條路給使用者去走,也需要在框架內做一些檢測機制,及時提醒使用者 "我們發現您這樣使用了框架,這可能會產生問題,你本意是否打算那樣做呢?"
2,如果你的入口類本來就是單例的,那么你是類中是否持有共享資源,你的API在并發的情況下被調用是否可以確保這些資源的線程安全?在解決多線程問題的時候往往有幾個難點:
- 百密難有一疏,你很難想到這段代碼會有人這樣去并發調用。比如某init()方法,某config()方法,你總是假設使用者會調用并且僅調用一次,但事實不一定這樣,有的時候調用者自己也不清楚我的容器會調用我這段代碼多少次。
- 好吧,解決多線程問題各種煩躁,那就對各種涉及到共享資源的方法全部加鎖。對方法進行粗獷(粒度)的鎖可能會導致性能急劇下降甚至是死鎖問題。
- 自以為使用了優雅的無鎖代碼或并發容器但卻達不到目的。我們往往在大量使用了并發集合心中暗自竊喜解決了多線程問題的同時又達到了極佳的性能,但你以為這樣是解決了線程安全問題但其實根本就沒有,我們不能假設A和B都方法是線程安全的,但對A和B方法調用的整個代碼段是線程安全的。
對于多線程問題,我沒有好的解決辦法,不過下面的幾條我覺得可以嘗試:
- 需要非常仔細的過一遍代碼,把涉及到共享資源的地方,以及相關的方法和類列出來,不要去假設什么,只要API暴露出去了則假設它可能被并發調用。共享資源不一定是靜態資源,哪怕資源是非靜態的,在并發環境下對相同對象的資源進行操作也可能產生問題。
- 一般而言對于公開的API,作為框架的設計者我們需要確保所有的靜態方法(或但單例類的實例方法)是線程安全的,對于實例方法我們可以不這么做(因為性能原因),但是需要在注釋中明確提示使用者方法的非線程安全,如果需要并發調用請自行處理線程安全問題。
- 可以看看是否有可能讓這些資源(字段)變為方法內的局部變量,有的時候我們并不是真正的需要類持有一個字段,只是因為多個方法要使用相同的東西,隨手一寫罷了。
- 對于使用頻率低的一些方法相關的一些資源沒有必要使用并發容器,直接采用粗狂的方式進行資源加鎖甚至是方法級別加鎖,先確保沒有線程安全,如果以后做壓測出現性能問題再來解決。
- 對于使用頻率高的一些方法相關的一些資源可以使用并發容器,但需要仔細思考一下代碼是否會存在線程安全問題,必要的話為代碼設計一些多線程環境的單元測試去驗證。
性能測試和優化
之前也提到過,你不會預測到你的項目會在怎么樣的訪問量下使用,我們不希望框架和同類的框架相比有明顯的性能差距(如果你做的是一個ORM框架或RPC框架,這個工作就是必不可少的),所以在框架基本完成后我們需要做Benchmark:
- 確定幾個測試用例,盡量覆蓋主流程和一些重要擴展
- 找幾個主流的同類型框架,實現相同的測試用例,實現到時候要單純一點,盡量不要再依賴其它外部框架
- 為這些框架和自己的框架,使用壓力測試工具在相同的環境和平臺來跑這些測試用例,使用圖表繪制在不同的壓力下的執行時間(以及內存和CPU等主要資源的消耗情況)
-
如果出現明顯的差距則用性能分析工具進行排查和優化,比如:
- 優化框架內的線程安全的實現方式
- 為框架內的代碼做一些緩存(緩存反射得到的元數據等等)
- 減少調用層次
- 這些調整可能會打破原來的主線流程或讓代碼變得難以理解,需要留下相關注釋
- 不斷重壓力測試和優化的過程,每次嘗試優化5%~20%的性能,雖然越到后來可能會越難,如果發現實在無法優化的話(性能分析工具顯示性能的分布已經很均勻了),可以看一下其它框架對于這部分工作實現的代碼邏輯
封裝和擴展
個人覺得一個框架如果只是能用那是第一個層次,能很方便的進行擴展或二次開發那是另外一個層次,如果我們龍骨階段的工作做的足夠好,框架是一個立體飽滿的框架,那么這部分的工作量就會小很多,否則我們需要對框架進行不少的重構以便可以達到這個層次。
-
我們需要縱覽一下框架的所有類型,看看有哪些類型我們是打算提供開發人員進行增強、擴展或替換的,對這些類型進行響應的結構調整。
- 比如希望被增強,則需要從繼承的角度來考慮
- 比如希望被擴展,則需要從Provider的角度來考慮
- 比如希望被替換,則需要在配置中提供組件的替換
-
我們需要再為這些類型進行精細化的調整:
- 檢查是否該封閉的封閉了,該開放的開放了
- 增強擴展或替換是否會帶來副作用
- 對于新來的外來類型,接收和使用的時候做足夠的檢查
- 相關日志的完善
重構還是重構
光是重構這個事情其實就可以說一本書了,其實我有一點代碼的潔癖,這里列一些我自己寫代碼的時候注重的地方:
- 格式:每次提交代碼的時候使用IDE來格式化你的代碼和引用(當然,實現可能需要配置IDE為你喜歡的代碼風格)
- 命名:保持整個類和接口命名統一,各種er,Provider、Creator、Initializer、Invoker、Selector代表的是一件事情,不要使用漢語拼音命名,如果英文不夠好的話多查一下字典,有的時候我甚至會因為一個命名去閱讀一些源代碼看看老外是怎么命名這個對象或這個方法的
- 訪問控制修飾符:這是一個非常難做好的細節,因為有太多的地方有訪問控制修飾符,究竟是給予什么級別的修飾符往往又取決于框架的擴展。可以在一開始的時候給盡量小的權限,在必要的時候慢慢提升,比如對于方法除了一定要給public的地方(比如公共API或實現接口),盡量都給private,在有繼承層次關系的時候去給到protected,對于類可以都給默認包/程序集權限,產生編譯錯誤的時候再去給到public
- 屬性/getter、setter:對于非POJO類字段的公開也要仔細想一下, 是否有必要有setter,因為一旦外部可以來設置類的某個內部字段,那么不僅僅可能改變了類的內部狀態,你還要考慮的是怎么處理這種改變,是不是有線程安全問題等等,甚至要考慮是否有必要開放getter,是否應該把類內部的信息公開給外部
- 方法:思考每一個方法在當前的類中存在是否合理,這是否屬于當前類應該做的事情,方法是否做了太多事情太少事情
- 參數:需要思考,對于調用每一個方法的參數,應該是傳給方法,還是讓方法自己去獲取;應該傳多個參數,還是封裝一個上下文給到方法
- 常量:盡量用枚舉或靜態字符串來代替框架使用到的一些常量或幻數,需要為常量進行一個分類不能一股腦堆在一個常量類Consts中
除了上面說的一些問題,我覺得對于重構,最重要的一句話就是:不要讓同一段代碼出現兩遍,主要圍繞這個原則進行重構往往就會解決很多設計問題,要實現這個目標可能需要:
- 干差不多活的類使用繼承來避免代碼重復(提煉超類),使用模版方法來把差異留給子類實現
- 構造方法可以層次化調用,主構造方法只要一個就可以了,不要在構造方法中實現太多邏輯
- 如果方法的代碼有重復可以考慮對方法提取出更小的公共方法來調用(提煉方法),也可以考慮使用Lambda表達式進行更小粒度重復代碼的提取(提煉邏輯)
- 可以使用IDE或一些代碼分析工具來分析重復代碼,如果你能想盡一切辦法來避免這些重復的話,代碼質量可以提高一個層次
其實也不一定是在重構的時候再去處理上面所有的問題,如果在寫代碼的時候都帶著這些意識來寫的話那么重構的負擔就會小一點(不過寫代碼思想的負擔比較大,需要同時考慮封裝問題、優雅問題、日志異常問題、多線程問題等等,所以寫一套能用的代碼和寫一套好的代碼其實不是一回事情)。
項目文檔
如果要別人來使用你的框架,除了示例項目來說提供和維護一份項目文檔是很有必要的,我建議文檔分為這幾個部分:
-
特性 Features:
- 相當于項目的一個宣傳手冊,讓別人能被你項目的亮點所吸引
- 每一個特性可以是一句話來介紹
-
新手入門 Get started:
- 介紹框架的基本定位和作用
- 從下載開始,通過一步一步的方式讓用戶了解怎么把框架用起來
- 整個文檔的閱讀時間在10分鐘以內
-
新手教程 Tutorials:
- 提供5~10篇文章站在使用者的角度來介紹項目的主要功能點
- 還是通過一步一步的方式,教大家使用框架完成一個小項目(比如CRUD)
- 介紹框架使用的最佳實踐
- 整個文檔的閱讀時間在8小時內
-
手冊 Manual:
- 介紹項目的定位和理念
- 詳細介紹項目的每一個功能點,可以站在框架設計者的角度多介紹一些理念
- 詳細介紹項目的每一個配置,以及默認配置和典型配置
- 詳細介紹項目的每一個擴展點和替換點
文檔最好不是帶格式的,方便以后適配各種文檔生成器和開源網站
開源
開源的好處是有很多人可以看到你的代碼幫助你改進,你的框架也可能會在更多的復雜環境下使用,框架的發展會較快框架的代碼質量也會有很大的提升。
要把框架進行開源,除了上面的各種工作之外可能還有一些額外的工作需要做:
- 選擇一個合適的License,并且檢測自己選擇的License與使用到的類庫的License是否兼容,在代碼頭的地方標記上License。
- 要確保每一個人都可以在自己的環境中可以構建你的代碼,盡量使用Maven等大家熟悉的構建工具來管理依賴和構建。
- 選擇諸如Github等平臺來管理源代碼,并以良好的格式上傳你的文檔,有條件的話對示例子網站進行部署。
- 如果你希望你的代碼讓更多的人一起來參與開發,那么需要制定和公開一些規范,比如風格、命名、提交流程、測試規范、質量要求等等。
- 開源后時刻對項目進行關注,對各種反饋和整合請求進行及時的反饋,畢竟開源是讓別人來幫你一起改進代碼,不是單純讓別人來學習你的代碼也不是讓別人來幫你寫代碼。
看到這里你可能相信我一開始的話了吧,框架可以使用到完善可以商用差距還是很大的,而且還要確保在迭代的過程中框架不能偏離開始的初衷不能有很大的性能問題出現,任重道遠。
文章列表