热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

抽象的意思是,抽取不同事物的共性而成的一种新事物。为什么用事物一词?因为抽象未必抽的是物,也可能是事。抽象是编程的重要思想之一࿰

抽象的意思是,抽取不同事物的共性而成的一种新事物。为什么用事物一词?因为抽象未必抽的是物,也可能是事。
抽象是编程的重要思想之一,其主要目的是为了减少代码重复,使其更易维护。
抽象就是让变化的事物得到一致的处理方式。

抽象是如何应用的?我们怎么去抽象?

当我们面临有共同特性的事物时,需要对它们统一处理,那么就需要抽象。而这种共性的事物在实际项目中会经常碰到。而且在我们使用的各种框架中应用广泛。比如说,用户打开不同的网页,都需要去展现页面,那么所有的网页都有一个共性就是展现,而不同的网页又具有不同的行为;所以在处理网页展现时,只需要处理网页们的抽象的东西——展现。这个“处理网页展现”的代码一般在框架内部实现。他对所有的网页处理都是调用抽象网页的展现代码,所以他的代码是一致不变的。再比如说我们点击某一些按钮,会触发各种事件,点击按钮的行为都是一致的,而事件的内容缺各不相同。那么在点击的这个行为上的处理也是一致的,就是触发事件的内容,至于事件内容的本身,那就是具体的实现问题,跟处理点击没有关系。我们把统一处理抽象事物的代码叫上层代码。抽象就是为了上层代码的一致性,不需要因为具体事物的改变而改变。

抽象与模式

也许大家都知道设计模式,这是经典的实际应用中碰到的各种常见问题而归纳出来的编程技巧,其中大多数都离不开抽象这一概念。掌握的抽象的思想,再去理解他们更容易些。

工厂模式
工厂模式是最易理解的模式之一,他是通过一个工厂类,创建抽象对象(其实是具体的实际对象),因为是抽象对象,所以其他代码在使用这些抽象对象的共性时只需要通过工厂类获取对象即可,而不需要具体new每个实际对象。
代码示例:

public interface IUserRepository
{IEnumerable GetUsers();
}namespace DataRepository.MySql
{public class UserRepository : IUserRepository{private MySql _db;public UserRepository(){_db = new MySql(ConnectionStringManager.Get("MySql"));}public IEnumerable GetUsers(){return _db.ExecuteSql("sql").ToList();}}
}namespace DataRepository.SqlServer
{public class UserRepository : IUserRepository{public IEnumerable 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 GetUsers(){return RepositoryFactory.GetUserRepository().GetUsers();}
}

 

上面的代码看起来比较简单,继承自接口,反射实例化具体子类便可。但抽象意味着是具体实现,而不是继承,继承只是实现的一种。所以在继承上要慎用。而大多数的模式也都是采用各种组合。其不外乎就是抽象出共同的接口,组合接口的实现。

策略模式
策略模式的应用场景我们几乎都碰到过,比如现在都比较流行SinaWeibo登录和QQ登录,再加上自己的Email登录,每种登录方式都有不同的实现,因为Sina和QQ这种OAuth的登录都需要回调网页(其实就是用来验证用户有效性的),而我们的Email登录也需要验证,总不能写3个登录验证页面吧?当然,写三个也不是不可以,但是如果我们还是Wap站,那就是6个,如果再有其他的站点,那就不知道要写多少个了。至少页面数量会很多。如果用MVC框架的话,倒是可以用公共的Controller来省去,那至少也要三个Action,而这三个页面其实也有共性,必然存在代码重复。我们需要消除这种重复,万一哪天再来个开心、人人等登录实现,又会增加很多页面;所以要把因为变化牵扯出来的变化保持不变。

可以看下面的实现代码,实现了3种登录方式的登录、验证和退出。

//登录地址策略接口
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
{private Login GetLogin(){return new Login(HttpContext);}//登录地址public ActionResult Login(string authType){var factory = new AuthFactory(authType);return Redirect(GetLogin().GetLoginUrl(factory.GetLogin()));}//验证地址,不管是Email还是第三方登录回调,均使用该地址。public ActionResult Authenticate(string authType){var factory = new AuthFactory(authType);var user = GetLogin().Authenticate(factory.GetAuth());//....}//退出地址public ActionResult Logout(string authType){var factory = new AuthFactory(authType);return Redirect(GetLogin().GetLogoutUrl(factory.GetLogin()));}
}


因为我们在获取具体策略的时候依然要判断该使用哪种策略,所以用工厂模式来创建具体的策略。但是仍然能看到Controller代码的丑陋之处,这就需要AOP的实现来避免重复的代码,后面我们会讲到。

转:https://www.cnblogs.com/snake-hand/p/3161575.html



推荐阅读
  • MySQL数据库锁机制及其应用(数据库锁的概念)
    本文介绍了MySQL数据库锁机制及其应用。数据库锁是计算机协调多个进程或线程并发访问某一资源的机制,在数据库中,数据是一种供许多用户共享的资源,如何保证数据并发访问的一致性和有效性是数据库必须解决的问题。MySQL的锁机制相对简单,不同的存储引擎支持不同的锁机制,主要包括表级锁、行级锁和页面锁。本文详细介绍了MySQL表级锁的锁模式和特点,以及行级锁和页面锁的特点和应用场景。同时还讨论了锁冲突对数据库并发访问性能的影响。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • 本文详细介绍了如何使用MySQL来显示SQL语句的执行时间,并通过MySQL Query Profiler获取CPU和内存使用量以及系统锁和表锁的时间。同时介绍了效能分析的三种方法:瓶颈分析、工作负载分析和基于比率的分析。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • t-io 2.0.0发布-法网天眼第一版的回顾和更新说明
    本文回顾了t-io 1.x版本的工程结构和性能数据,并介绍了t-io在码云上的成绩和用户反馈。同时,还提到了@openSeLi同学发布的t-io 30W长连接并发压力测试报告。最后,详细介绍了t-io 2.0.0版本的更新内容,包括更简洁的使用方式和内置的httpsession功能。 ... [详细]
  • 本文详细介绍了在ASP.NET中获取插入记录的ID的几种方法,包括使用SCOPE_IDENTITY()和IDENT_CURRENT()函数,以及通过ExecuteReader方法执行SQL语句获取ID的步骤。同时,还提供了使用这些方法的示例代码和注意事项。对于需要获取表中最后一个插入操作所产生的ID或马上使用刚插入的新记录ID的开发者来说,本文提供了一些有用的技巧和建议。 ... [详细]
  • 本文介绍了在使用Python中的aiohttp模块模拟服务器时出现的连接失败问题,并提供了相应的解决方法。文章中详细说明了出错的代码以及相关的软件版本和环境信息,同时也提到了相关的警告信息和函数的替代方案。通过阅读本文,读者可以了解到如何解决Python连接服务器失败的问题,并对aiohttp模块有更深入的了解。 ... [详细]
  • MySQL中的MVVC多版本并发控制机制的应用及实现
    本文介绍了MySQL中MVCC的应用及实现机制。MVCC是一种提高并发性能的技术,通过对事务内读取的内存进行处理,避免写操作堵塞读操作的并发问题。与其他数据库系统的MVCC实现机制不尽相同,MySQL的MVCC是在undolog中实现的。通过undolog可以找回数据的历史版本,提供给用户读取或在回滚时覆盖数据页上的数据。MySQL的大多数事务型存储引擎都实现了MVCC,但各自的实现机制有所不同。 ... [详细]
author-avatar
手机用户2502906281
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有