本文版權歸博客園和作者吳雙本人共同所有。歡迎轉載,轉載和爬蟲請注明原文地址 http://www.cnblogs.com/tdws/p/5861842.html
昨天晚上,有個朋友說學了好久,依然沒搞懂多態,讓我簡單講解一下。我覺得多態在面向多想的三大特性當中,算是最簡單的,最難的是看似容易的封裝。在編寫面向對象代碼時,如何讓代碼可讀性更強,除了變量和方法命名標准外,要做的到一個方法只做一件事情,這樣的思想是《代碼整潔之道》一書中主要推崇的思想,其實有經驗的各位都希望自己看到的代碼是簡短,可維護,可讀性強的,相信大家也都“有幸”遇到過幾百上千行的代碼,更過分的是有個朋友曾經維護一個上萬行的Action,誇張的說,調試並走通邏輯,一次要三天,有的人說這是業務邏輯不斷增加所導致,但我認為,在這種情況下,更應該盡量做到一個方法做一件事情。我也不多吐槽了,關於代碼整潔,我在大三的時候,就"吐槽"過http://www.cnblogs.com/tdws/p/4674489.html。
封裝也不是今天的主題,今天我們要說的是多態,在朋友問我的時候,我給他舉了下面這個簡短的例子。
總體概括這個例子來講就是在基本的三層架構當中,DAL層建兩個類AdminDal,UserDal。兩個類中,都有增加對象和刪除對象地方法,那這個時候,我們應該給兩個類抽象出一個父類BaseDal<T>,父類中是他們的公共方法,並且父類需要一個泛型T,這樣父類的方法,才能明白你所要添加或者刪除的object到底是什麼類型的。請看如下代碼。雖然兩個類的公共方法在父類當中,但是他們自身特有的方法,還是要寫在自己的Dal層當中。
1 public class UserDal: BaseDal<UserEntity> 2 { 3 4 }
1 public class AdminDal: BaseDal<AdminEntity> 2 { 3 public void Manage() 4 { 5 Console.WriteLine("管理員管理網站"); 6 } 7 }
1 public class BaseDal<T> 2 { 3 public void AddObj(T obj) 4 { 5 Console.WriteLine("添加對象成功,對象屬於"+obj.GetType().ToString()); 6 } 7 8 public void DeleteObj(T obj) 9 { 10 Console.WriteLine("刪除對象成功,對象屬於"+obj.GetType().ToString()); 11 } 12 13 }
下面給出邏輯層代碼,如果說普通的開發過程當中,你的代碼也許是這樣的。
1 public class UserBll 2 { 3 UserDal dal = new UserDal(); 4 5 public void Add(UserEntity obj) 6 { 7 dal.AddObj(obj); 8 } 9 10 public void Delete(UserEntity obj) 11 { 12 dal.DeleteObj(obj); 13 } 14 }
public class AdminBll
{ AdminDal dal = new AdminDal(); public void Add(AdminEntity admin) { dal.AddObj(admin); } public void Delete(AdminEntity admin) { dal.DeleteObj(admin); } public void Manage() { dal.Manage(); } }
也就是在各自的邏輯層當中,調用dal層。這個時候你又看到依然有這麼多重復的代碼,是不是應該再次封裝成一個BaseBll<T>呢。答案是肯定的,但是問題又來了,在封裝父類的過程中,你會發現,這個dal的對象怎麼封裝呢?這就是用到多態的關鍵點。下面看一下BaseBll.cs的代碼。
public abstract class BaseBll<T> where T:class, new() { public BaseDal<T> currentDal; public BaseBll() { SetCurrentDal(); } public abstract void SetCurrentDal(); public void BaseAdd(T obj) { currentDal.AddObj(obj); } public void BaseDelete(T obj) { currentDal.DeleteObj(obj); } }
我給了一個抽象的基類,並且給出抽象的SetCurrentDal的抽象方法定義。該方法用於設置當前類的currentDal到底是adminDal還是userDal。我們在構造函數中調用SetCurrentDal這個抽象方法,為什麼在構造函數中調用的原因是,當實例化子類對象時,一定是首先進入其父類的構造函數。當子類AdminBll和UserBll繼承BaseBll<T>的時候,必須重寫抽象方法,並且為BaseDal<T> currentDal對象設置實際的值。我先給出子類的代碼
1 public class AdminBll : BaseBll<AdminEntity> 2 { 3 AdminDal dal = new AdminDal(); 4 public AdminBll() 5 { 6 7 } 8 public void Manage() 9 { 10 new AdminDal().Manage(); 11 } 12 13 public override void SetCurrentDal() 14 { 15 currentDal = new AdminDal(); 16 } 17 }
1 public class UserBll : BaseBll<UserEntity> 2 { 3 public override void SetCurrentDal() 4 { 5 base.currentDal = new UserDal(); 6 } 7 }
當實例化子類的對象時,過程為:子類構造函數(不進入)—進入父類構造函數—父類構造內部調用子類重寫的SetCurrentDal(當前多態的currentDal到底是誰的實例)—父類構造執行完畢(設置currentDal完成)—子類構造函數。這就是抽象方法實現的多態。
下面在UI層調用一下,看看結果:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 AdminBll adminBll = new AdminBll(); 6 AdminEntity admin = new AdminEntity() {AdminName="吳雙",AdminPwd="123" }; 7 adminBll.Manage(); 8 adminBll.BaseAdd(admin); 9 Console.ReadKey(); 10 } 11 }
輸出結果:
在開發的過程中,也許你會有很多實體類,每個實體類都有各自的增刪改查等其他共有方法,基於這樣的情況,我們就需要手段來將其封裝。為什麼在邏輯層使用了多態,原因就是我們封裝父類的時候,不確定當前的currentDal到底是adminDal還是userDal還是xxxDal。為了封裝出基類,這個多態的對象就必不可少了。
當然在實際當中,如果你是寫原生sql,這樣封裝的確不容易,各種拼接sql。但如果說你用ORM框架,EF,Dapper之類的,這個方法真的是必不可少的,你可能再加上接口層,加上工作單元,創建對象非new,使用抽象工廠,依賴注入等。無論怎樣,這一層的多態一定能用到,只是創建對象稍作修改。
下一階段也打算進行後台架構搭建分享,MVC WebApi+EF/Dapper+工作單元+抽象工廠/依賴注入Autofac+AutoMapper+日志組件等。
我也曾多次在項目中搭建此類框架,在緩存提高性能,處理高並發,應用服務器集群,緩存集群,隊列集群等方面,本次也會加入到分享當中。
如果今天的點滴分享,對您有點滴幫助,請點贊支持,也為自己的進步點贊。
點擊下方關注,我們共同進步。