从抽象谈起(一):工厂模式与策略模式

抽象的意思是,抽取不一样事物的共性而成的一种新事物。为何用事物一词?由于抽象未必抽的是物,也多是事。
抽象是编程的重要思想之一,其主要目的是为了减小代码重复,使其更易维护。
抽象就是让变化的事物获得一致的处理方式。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的实现来避免重复的代码,后面咱们会讲到。继承

相关文章
相关标签/搜索