這個好像、也許、或許、大概、應該、Maybe真的可以算是傳說中的面向接口編程了吧。
假設現在有如下情況,某項目的框架結構用的是ADO.NET寫的簡單三層。且已經開始寫了一部分了。現在因為時間比較緊急,經理認為用EF寫速度會快一些,於是要求我們將訪問驅動層改成用EF寫的。
那么,恩,好吧,我們得改了。先看一下原來DAL層和BLL層的代碼。因僅演示而已,此處只寫了一個大概思路的代碼。另外此處筆者只打算寫一個DAL層了BLL層的一個松耦合的一個漸近過程的處理。方法只寫一個。諸位見諒。主要是要有那個思路。

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using Anmutu.OA.Model; 7
8 namespace Anmutu.OA.AdoNetDAL 9 { 10 public class UserAdoNetDal 11 { 12 public User AddUser(User user) 13 { 14 //在此處寫一此插入的一個SQL.會用到DBSQLHelper.using()...寫就是一個insert操作。你知道的,主要是那個思想。代碼略過。
15 return null; 16 } 17 } 18 }

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using Anmutu.OA.AdoNetDAL; 7 using Anmutu.OA.Model; 8
9 namespace Anmutu.OA.BLL 10 { 11 public class UserService 12 { 13 //調用一下數據庫訪問層的方法,打個醬油。
14 private AdoNetDAL.UserAdoNetDal userAdoNetDal = new UserAdoNetDal(); 15 public User Add(User user) 16 { 17 return userAdoNetDal.AddUser(user); 18 } 19 } 20 }
還好還好,DB里有的相應的表的。我們DBFirst一下,拿到EF的上下文。那么新的訪問驅動層的代碼將會是這個樣子的:

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using Anmutu.OA.Model; 7
8 namespace Anmutu.OA.DAL 9 { 10 public class UserEFDal 11 { 12 public User AddUser(User user) 13 { 14 //得到的EF上下文,DBFirst不用解釋了吧。
15 AnmutuModelContainer db = new AnmutuModelContainer(); 16 //加到DB里去。
17 db.UserSet.Add(user); 18 //保存
19 db.SaveChanges(); 20 return user; 21 } 22 } 23 }

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using Anmutu.OA.AdoNetDAL; 7 using Anmutu.OA.DAL;//添加EF層的程序集。
8 using Anmutu.OA.Model; 9
10 namespace Anmutu.OA.BLL 11 { 12 public class UserService 13 { 14 #region 用EF作為訪問驅動層時的代碼。
15 private DAL.UserEFDal _userEfDal = new UserEFDal(); 16 public User AddUser(User user) 17 { 18 return _userEfDal.AddUser(user); 19 } 20 #endregion
21 #region 用ADO.NET作為訪問驅動層時的代碼
22 //private AdoNetDAL.UserAdoNetDal userAdoNetDal = new UserAdoNetDal(); 23 //public User Add(User user) 24 //{ 25 // return userAdoNetDal.AddUser(user); 26 //}
27 #endregion
28 } 29 }
這樣貌似就算是修改好了。在這個小案例中。那么兩個分析一下具體的情況。現在的情況是邏輯層的的代碼,依賴與一個具體的實現,那么依賴者就必須跟着變化。就如同這里的實例名字不一樣,那么下面具體用到此實例的時候就都得改了。如果這里調用數據庫訪問層的方法的名字不一樣,同樣也是需要改動的。目前這個小案例中,修改的地方還真就不算多。但是,正如你知道的,一個項目中不可能真的只有這么一個方法的。會有很多方法。那么,是的,你得修改很多的地方了。來圖看一下,它可能是這樣。
程序猿甲說,這個好辦,我們可以CTRL+F,然后選擇將相應的需要改的全部替換也就好了。
攻城獅乙說,這個方法,固然可以,可是如果哪個地方替換錯了,就有那么一個地方名字一樣,替換錯誤了,那豈不是很麻煩么?
程序猿丙說,我們可以依舊CTRL+F,我們選擇單個替換就好了呀,我們可以小心一點的。
攻城獅丁說,這個方法,依舊且算可以,只是就算你很小心沒有替換錯,不是很耽誤時間的么?時間可就是金錢啊。
那么,思路是我們就可以不讓其依賴與一個具體的實例。而是依賴與一個抽象的實例。比如讓其依賴與一個接口。用接口解除其對具體數據庫訪問的耦合。通過讓BLL層去依賴一個公共的變化很少的契約接口,依賴抽象,具體的實現在變化的時候,BLL層就不用變化了。這是對變化點的一個封裝。有點繞口且有點‘WHERE IS 南 WHERE IS 北’(筆者用此句表示不太容易理解,如果沒有了解過接口神馬的) 的感覺。簡單的說就是用一個抽象的實例,但是后面new出來的就是一個具體的了。
那么,將修改的代碼將會是這個樣子:

1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 using Anmutu.OA.Model; 8
9 namespace Anmutu.OA.IDAL 10 { 11 /// <summary>
12 /// 創建一個接口,約定其返回類型是User類,參數是一個user實體。 13 /// </summary>
14 interface IUserDal 15 { 16 User AddUser(User user); 17 } 18 }

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using Anmutu.OA.IDAL; 7 using Anmutu.OA.Model; 8
9 namespace Anmutu.OA.AdoNetDAL 10 { 11 public class UserAdoNetDal:IUserDal//此處實現接口里的方法。因為接口里的方法名字也就AddUser,這里就沒有必要CTRL+SHIFT+...了。我用的是Resharper插件,也就不用點實現了。
12 { 13 public User AddUser(User user) 14 { 15 //在此處寫一此插入的一個SQL.會用到DBSQLHelper.using()...寫就是一個insert操作。
16 return null; 17 } 18 } 19 }

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using Anmutu.OA.IDAL; 7 using Anmutu.OA.Model; 8
9 namespace Anmutu.OA.DAL 10 { 11 public class UserEFDal:IUserDal//這里實現接口。
12 { 13 public User AddUser(User user) 14 { 15 //得到的EF上下文,DBFirst不用解釋了吧。
16 AnmutuModelContainer db = new AnmutuModelContainer(); 17 //加到DB里去。
18 db.UserSet.Add(user); 19 //保存
20 db.SaveChanges(); 21 return user; 22 } 23 } 24 }

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using Anmutu.OA.AdoNetDAL; 7 using Anmutu.OA.DAL;//添加EF層的程序集。
8 using Anmutu.OA.Model; 9
10 namespace Anmutu.OA.BLL 11 { 12 public class UserService 13 { 14 #region 實現接口后的代碼
15 //IDAL.IUserDal userDal=new UserAdoNetDal();//當要用到ADO.NET作為訪問驅動時。
16 IDAL.IUserDal userDal=new UserEFDal();//當要用到EF作為訪問驅動時。
17 public User AddUser(User user) 18 { 19 return userDal.AddUser(user); 20 } 21 #endregion
22 #region 用EF作為訪問驅動層時的代碼。
23 //private DAL.UserEFDal _userEfDal = new UserEFDal(); 24 //public User AddUser(User user) 25 //{ 26 // return _userEfDal.AddUser(user); 27 //}
28 #endregion
29 #region 用ADO.NET作為訪問驅動層時的代碼
30 //private AdoNetDAL.UserAdoNetDal userAdoNetDal = new UserAdoNetDal(); 31 //public User Add(User user) 32 //{ 33 // return userAdoNetDal.AddUser(user); 34 //}
35 #endregion
36 } 37 }
如果要換訪問驅動層的話,請看圖:
是的,只需要在如圖的位置new一個不同的就好了。且VS會給我們提示。
這個好像、也許、或許、大概、應該、Maybe真的可以算是傳說中的面向接口編程了吧。
攻城獅戊說,可是我可能會有很多服務的地方都用到這個實例,那我都得改么?這樣寫很麻煩的感覺呢。
李寧說過一句話,他說,一切皆有可能。所以,是的,也許以后園子時會多一篇叫做“這個好像、也許、或許、大概、應該、Maybe真的可以算是傳說中的簡單工廠了吧”的文章。
筆者寫的都是很簡單的東西。歡迎指正,共同進步。