不過這種技術接口的制訂是個難題,設計不好很影響以後的功能擴展 —— fking
比較簡單的插件想法,擴展的功能是有限的。 應該考慮主程序本身也應該是一個插件的結構。也就是說插件分為宿主插件和擴展插件兩類。這兩類也可以在一起。這樣的話才可能有好的擴展性。象eclipse的擴展和擴展點的思想,和sharpdevelop的插件樹的思想比較好解決了擴展性的問題。 —— jan
以上是兩位網友對筆者《C#插件構架實戰》一文作出的評價。首先對兩位熱心的讀者表示感謝。
的確如此,在軟件開發的過程中,設計的過程往往比寫代碼的過程要難得多。因此,通常除了軟件測試之外,耗時最多的也就是系統建模了。一個好的軟件系統應當具有較高的穩定性(可靠性)、易操作性以及可擴展性支持,尤其是可擴展性。我認為,良好的可擴展性支持是一個軟件團隊在開發中變被動為主動的必要條件。對於一個應用,我們希望在用戶增加需求時,我們能夠用最少的時間、最少的人力來解決問題。當別人在用戶快速增長的需求中忙得不可開交時(用戶總是不能在第一次需求分析時將需求完完整整的告訴你 ^_^),而你,你的團隊只需要作一點工作就可以讓“貪得無厭”(-_-)的用戶得到滿足,從而提高了效率,讓團隊有更多的的時間來創造,而不是去做無謂的修改。
很遺憾的是,在《C#插件構架實戰》一文中,我並未考慮到這一點。當然,對於一個十八歲的沒有也不可能有團隊工作經驗的年輕人來說,這樣的失誤(失誤就是失敗——老師如是說)是可以原諒的(自我開脫之辭)。不過,我決定對這個插件系統進行重構。
考慮到系統的復雜性,這次我准備使用UML(大上個月才開始學的,畫得不好,見笑了)。
1. 著手分析
對於網友 jan 的指教,我大概明了,但人的思維差別太大,我不敢保證我的理解是完全符合 jan 的意思的。但是,我仍然會根據自己對可擴展性的理解構建一個應用程序框架模型。
直入正題。我現在假設我屬於一個軟件團隊(就暫且叫她 AbstractSoft 吧),並任系統分析師(^o^)。任何事物都有它規范的一面,我們希望我們的團隊出品的部署在同一平台的所有應用都有相同的框架,相同的部署形式。這樣便可以形成獨有的團隊特色,並在競爭中以效率取勝。因為我們不需要為每一套應用設計不同的框架——這可以節約不少時間!
這樣我需要把程序實現與用戶界面分開到不同的框架中。我的意思是:
如此一來,在 Application Frame Level 的核心庫中存在的是抽象接口以及一些泛化的細節。這些內容在第一次安裝團隊產品時就已經部署在用戶的機器上了。它不會自動銷毀,直到用戶提交把它從本地移除的請求。GUI Level 提供了團隊產品泛化後的統一的界面組件(比如:屬性編輯器、數據庫操作界面等可重用組件)。特化的產品(Speciallized Application)通過實現 Application Frame Level 中的某些接口實現可擴展性,通過使用 GUI Level 中的的類來實現用戶界面。
以下是一個簡單的靜態圖(接口和類的成員將在下面詳細闡述):
2. IConnectableObject
public interface IConnectable {
// application 為插件所屬的主框架對象。若為null則表示插件本身就是主框架
ConnectionResult Connect( object application );
ExtendibleVersionInfo VersionInfo { get; }
void OnDestory();
void OnLoad();
void Run();
}
public enum ConnectionResult {
Connection_Success ,
Connection_Failed
}
public class ExtendibleVersionInfo {
private ExtendibleVersionInfo() {}
public ExtendibleVersionInfo( string name , string version , string copyright ) { // Omitted }
public ExtendibleVersionInfo(string name,int version1,int version2,int version3,string copyright) { // Omitted }
public int PrimaryVersion { get { return _Version1; } }
public int SecondaryVersion { get { return _Version2; } }
public int BuildVersion { get { return _Version3; } }
public string Name { get { return _Name; } }
public string VersionString { get { // Omitted } }
public string Copyright { get { return _Copyright; } }
private string _Name;
private int _Version1 = 1;
private int _Version2 = 0;
private int _Version3 = 0;
private string _Copyright;
public static ExtendibleVersionInfo Empty = new ExtendibleVersionInfo();
}
所有可連接的對象必須實現這個接口。這是所有 Application Frame Level 中類的鼻祖。
3. IExtendible
public interface IExtendible {
IConnectable GetLatestVersion();
IConnectable QuerySpecifIEdVersion( ExtendibleVersionInfo version );
ExtendibleVersionInfo[] EnumerateVersions();
}
4. 使用類工廠創建應用程序和插件的最新版本
我們的主程序以及插件會設計成 internal class 。程序只輸出一個工廠類,用戶界面通過調用 IExtendible 接口的 GetLatestVersion() 方法獲得這些用來完成實際任務的對象的實例,並把它們顯示出來。或者,也可以枚舉所有的版本,讓用戶來挑選所需要版本。
5. 可擴展性
不得不承認,這樣的方式可擴展性仍不是很強。程序需要升級時同時需要修改提供給用戶的工廠類(雖然接口不變)。為了實現更好的可擴展性,可以把簡單工廠模式轉換為工廠方法模式。