從抽象談起(一):工廠模式與策略模式

抽象的意思是,抽取不一樣事物的共性而成的一種新事物。爲何用事物一詞?由於抽象未必抽的是物,也多是事。
抽象是編程的重要思想之一,其主要目的是爲了減小代碼重複,使其更易維護。
抽象就是讓變化的事物獲得一致的處理方式。sql

抽象是如何應用的?咱們怎麼去抽象?編程

當咱們面臨有共同特性的事物時,須要對它們統一處理,那麼就須要抽象。而這種共性的事物在實際項目中會常常碰到。並且在咱們使用的各類框架中應用普遍。好比說,用戶打開不一樣的網頁,都須要去展示頁面,那麼全部的網頁都有一個共性就是展示,而不一樣的網頁又具備不一樣的行爲;因此在處理網頁展示時,只須要處理網頁們的抽象的東西——展示。這個「處理網頁展示」的代碼通常在框架內部實現。他對全部的網頁處理都是調用抽象網頁的展示代碼,因此他的代碼是一致不變的。再好比說咱們點擊某一些按鈕,會觸發各類事件,點擊按鈕的行爲都是一致的,而事件的內容缺各不相同。那麼在點擊的這個行爲上的處理也是一致的,就是觸發事件的內容,至於事件內容的自己,那就是具體的實現問題,跟處理點擊沒有關係。咱們把統一處理抽象事物的代碼叫上層代碼。抽象就是爲了上層代碼的一致性,不須要由於具體事物的改變而改變。設計模式

抽象與模式框架

也許你們都知道設計模式,這是經典的實際應用中碰到的各類常見問題而概括出來的編程技巧,其中大多數都離不開抽象這一律念。掌握的抽象的思想,再去理解他們更容易些。網站

工廠模式
工廠模式是最易理解的模式之一,他是經過一個工廠類,建立抽象對象(實際上是具體的實際對象),由於是抽象對象,因此其餘代碼在使用這些抽象對象的共性時只須要經過工廠類獲取對象便可,而不須要具體new每一個實際對象。
代碼示例:spa

public interface IUserRepository
{
	IEnumerable<Users> GetUsers();
}

namespace DataRepository.MySql
{
	public class UserRepository : IUserRepository
	{
		private MySql _db;
		public UserRepository()
		{
			_db = new MySql(ConnectionStringManager.Get("MySql"));
		}
		
		public IEnumerable<Users> GetUsers()
		{
			return _db.ExecuteSql("sql").ToList<Users>();
		}
	}
}

namespace DataRepository.SqlServer
{
	public class UserRepository : IUserRepository
	{
		public IEnumerable<Users> GetUsers()
		{
			return GetDataContext(ConnectionStringManager.Get("SqlServer")).Users.AsEnumerable();
		}
	}
}
public class RepositoryFactory
{
	public IUserRepository GetUserRepository()
	{
		return CreateInsnace(AppSettings.Get("CurrentDatabase"),"UserRepository");
	}
}

public class UserManager()
{
	public IEnumerable<Users> GetUsers()
	{
		return RepositoryFactory.GetUserRepository().GetUsers();
	}
}


上面的代碼看起來比較簡單,繼承自接口,反射實例化具體子類即可。但抽象意味着是具體實現,而不是繼承,繼承只是實現的一種。因此在繼承上要慎用。而大多數的模式也都是採用各類組合。其不外乎就是抽象出共同的接口,組合接口的實現。設計

策略模式
策略模式的應用場景咱們幾乎都碰到過,好比如今都比較流行SinaWeibo登陸和QQ登陸,再加上本身的Email登陸,每種登陸方式都有不一樣的實現,由於Sina和QQ這種OAuth的登陸都須要回調網頁(其實就是用來驗證用戶有效性的),而咱們的Email登陸也須要驗證,總不能寫3個登陸驗證頁面吧?固然,寫三個也不是不能夠,可是若是咱們仍是Wap站,那就是6個,若是再有其餘的站點,那就不知道要寫多少個了。至少頁面數量會不少。若是用MVC框架的話,卻是能夠用公共的Controller來省去,那至少也要三個Action,而這三個頁面其實也有共性,必然存在代碼重複。咱們須要消除這種重複,萬一哪天再來個開心、人人等登陸實現,又會增長不少頁面;因此要把由於變化牽扯出來的變化保持不變。對象

能夠看下面的實現代碼,實現了3種登陸方式的登陸、驗證和退出。blog

//登陸地址策略接口
public interface ILogin
{
	public string GetLoginUrl(HttpContextBase context);
}
//第三方用戶的驗證策略接口
public interface IAuthenticate
{
	public TrdUser Authenticate(HttpContextBase context);
}
//退出地址策略接口
public interface ILogout
{
	public string GetLogoutUrl(HttpContextBase context);
}
//新浪Auth的策略實現
public class SinaAuth : ILogin,IAuthenticate,ILogout
{
	public string GetLoginUrl(HttpContextBase context)
	{
		return "http://weibo.com/oauth/login";
	}

	public TrdUser Authenticate(HttpContextBase context)
	{
		return new TrdUser { Name = "我來自新浪" };
	}
	
	public string GetLogoutUrl(HttpContextBase context)
	{
		return "http://weibo.com/oauth/logout";
	}
}
//QQAuth的策略實現
public class QQAuth : ILogin,IAuthenticate
{
	public string GetLoginUrl(HttpContextBase context)
	{
		return "http://qq.com/oauth/login";
	}

	public TrdUser Authenticate(HttpContextBase context)
	{
		return new TrdUser { Name = "我來自QQ" };
	}
}
//本網站默認策略的實現
public class DefaultAuth :ILogin, IAuthenticate,ILogout
{
	public string GetLoginUrl(HttpContextBase context)
	{
		return "http://mysite.com/oauth/login";
	}	
	
	public TrdUser Authenticate(HttpContextBase context)
	{
		return new TrdUser { Name = "我來自Email" };
	}
	
	public string GetLogoutUrl(HttpContextBase context)
	{
		return "http://mysite.com/oauth/logout";
	}
}
//策略組裝類
public class Login
{
	private HttpContextBase _context;
	public Login(HttpContextBase context)
	{
		_context = context;
	}
	//獲取登陸地址
	public string GetLoginUrl(ILogin login)
	{
		return login.GetLoginUrl(_context);
	}
	//獲取退出地址
	public string GetLogoutUrl(ILogout logout)
	{
		return logout.GetLogoutUrl(_context);
	}
	//獲取本站用戶信息
	public User Authenticate(IAuthenticate auth)
	{
		var trdUser = auth.Authenticate(_context);
		return xx.GetUser(trdUser);
	}
}
//策略工廠,根據不一樣的type建立不一樣的策略
public class AuthFactory
{
	private string _authType;
	public AuthFactory(string authType)
	{
		_authType = authType;
	}

	public ILogin GetLogin()
	{
		return (ILogin)CreateInstance(typeof(ILogin));
	}

	public IAuthenticate GetAuth()
	{
		return (IAuthenticate)CreateInstance(typeof(IAuthenticate));
	}
	
	public ILogout GetLogoutUrl()
	{
		return (ILogout)CreateInstance(typeof(ILogout));
	}
	
	private object CreateInstance(Type type)
	{
		//經過反射獲取具體的類型
		var instance = ....;
		//如果沒有實現,就使用默認的。
		if(instance == null) return new DefaultAuth();
	}
}

//本站的頁面具體代碼
public AccountController : Controller 
{
	//登陸地址
	public ActionResult Login(string authType)
	{
		var login = new Login(HttpContext);
		var factory = new AuthFactory(authType);
		
		return Redirect(login.GetLoginUrl(factory.GetLogin()));
	}
	
	//驗證地址,不論是Email仍是第三方登陸回調,均使用該地址。
	public ActionResult Authenticate(string authType)
	{
		var login = new Login(HttpContext);
		var factory = new AuthFactory(authType);
		
		var user = login.Authenticate(factory.GetAuth());
		//....
	}
	
	//退出地址
	public ActionResult Logout(string authType)
	{
		var login = new Login(HttpContext);
		var factory = new AuthFactory(authType);
		
		return Redirect(login.GetLogoutUrl(factory.GetLogin()));
	}
}

由於咱們在獲取具體策略的時候依然要判斷該使用哪一種策略,因此用工廠模式來建立具體的策略。可是仍然能看到Controller代碼的醜陋之處,這就須要AOP的實現來避免重複的代碼,後面咱們會講到。繼承

相關文章
相關標籤/搜索