[扫盲]利用架构实例解析面向对象的封装,多态,继承,接口,泛型
刚刚看到园子里有人问这个问题,心血来潮,整理一番,希望对初学oop的有所帮助,同时有理解不对的欢迎指正。
假设我们做一个简单的小系统,需要的操作为:
1.管理用户,对用户进行增、删、改(User表)
2.管理角色,对角色进行增、删、改(Role表)
3.管理功能,对系统功能进行增、删、改(Fun表)
由此可以分析,我们这个小系统需要的表为,User用户表,Role角色表,Fun权限表,还要派生出一张表 角色-权限(RoleFun) 关联表。其中用户表里面有字段存放角色Role的ID。
一般情况下,我们都采用的是3层架构模式,即使是复杂模式也是在此基础上延伸的。
上面这幅图是最最基础的一个框架。总共分为5个层次,Model层,DAL层,BLL层,Tools层,UI层
对于一般的编程者来说,实现起来并不复杂,可能不会将那么多的思想应用到实例中来,那么这里我们模拟下,先抛开这些思想。
(1)首先每个数据库表对应一个Model类这个是必须的吧,那么我们在Model层新建4个类,User,Role,RoleFun,Fun
(2)这个四个对象都需要数据库增、删、改操作,那么我们在DAL里面对应的创建4个类,UserDAL,RoleDAL,RoleFunDAL,FunDAL,然后每个类里面写入相同的方法,增、删、改。
Insert(对象),Delete(对象),Update(对象)
这里你可能已经意识到问题了,hold,继续看。
(3)同样在BLL层要实现对DAL的调用,需要建立同样的四个业务操作类,UserMgr,RoleMgr,RoleFunMgr,FunMgr,每个类里面实现同样的调用方法。
(4)Tools暂不考虑,主要存放经常用的类
(5)UI层,表现层,可以是Web或者Winform等形式
搭建好的代码框架如下:
面向对象的第一个特性是:封装,其实我们已经实现了,分层就是封装的最好体现,各层相互调用,“低耦合,高内聚”
这里不在解释封装了。
通过上面的代码编写你肯定遇见了灾难了,当随着我们业务功能的不断增加,我们的DAL和BLL类越来越多,越来越难管理了。
更悲催的是,如果每个实体类我想实现一个查询(Search)操作,那么需要在众多的类里面实现这个Search代码,试想那是多么的可怕,你的价值全部浪费在了基础代码的编写上。
如何解决这个问题呢?
那就是采用继承的思想实现,父子类来实现,一个公共的父类(BASE),所有子类继承这个子类,那么子类也就具有了父类的public方法,如果需要增加公共的方法,只需要在父类中添加方法即可实现,所有子类自动会继承。
模式如上图所示,这样实现一个Search()方法,我们只需要变动BaseDAL类即可,而其他实体类都不需要动了。试想对若干实体类的系统,这意思想是多么的重要,可以节省大量的时间。
还没有结束,细心的你可能发现,我们的Insert,Update,Delete都需要自己的Model呀,那BaseDAL应该如何实现呢?怎么知道具体的方法去调用具体的Model呢?
哈哈,这里C#提出了引以为豪的“泛型”思想。通过泛型,可以轻松的解决这个问题。
对于BaseDAL我们可以设计成为泛型的类,在使用该类的时候需要将该类具体的实例化,看代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DAL { public class BaseDAL<T> where T:new () { public bool Insert(T t) { //TODO 解析T,通过反向工程构造SQL return true; } public bool Update(T t) { //TODO 解析T,通过反向工程构造SQL return true; } public bool Delete(T t) { //TODO 解析T,通过反向工程构造SQL return true; } } }
这样,我们其他的类可以直接继承父类了。但为了整体架构的可塑行,我们DAL层将不做对BaseDAL的继承操作,原因很简单,因为我们可能会切换DAL的技术,例如由ADO.Net切换成HNibernate等,同时DAL作为数据操作层尽量要保持简约。
我们这里在业务逻辑层BLL来实现继承,同样也需要一个BaseBLL的基类。
using DAL; using System.Collections; using System.Collections.Generic; namespace BLL { public class BaseMgr<T> where T : new() { //可以重写 public virtual bool Insert(T t) { return new BaseDAL<T>().Insert(t); } public bool Update(T t) { return new BaseDAL<T>().Update(t); } public bool Delete(T t) { return new BaseDAL<T>().Delete(t); } //重载 public bool Delete(List<T> list) { return true; } } }
然后具体的实体类继承这个BaseMgr
using Model; namespace BLL { public class UserMgr : BaseMgr<User> //继承 { //继承基类最大的好处是,可以写本类自己的特有方法 //例如User,我想获取所有的用户,而其他的业务逻辑不需要实现这个方法, //那么我们可以单独写在这个类里面 //当然你也可以写在BaseMgr里面,哪样所有的子类都具有这个方法了 //可以对基类进行重写(多态) public override bool Insert(User t) { return true; } //继承基类前的代码 //public bool Insert(User t) //{ // return new UserDAL().Insert(t); //} //public bool Delete(User t) //{ // return new UserDAL().Delete(t); //} //public bool Update(User t) //{ // return new UserDAL().Update(t); //} } }
通过泛型实现继承我们获得的最大好处:
1,数据库每增加一个实体,代码的Model层只需要增加一个对应的实体类,那么该类的基础操作随即产生了(即,BaseDAL类的所有方法)
2,通过在业务层对基类的继承,可以实现特有方法,当然并不是所有的实体类都需要继承,具体看自己的需求
多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
实现多态,有二种方式,覆盖,重载。
覆盖:override,上面代码UserMgr类中的Insert方法就是用的覆盖方法,可以重写父类的某些方法来达到自己的业务要求。一定要注意将允许重写的方法事先声明为virtual
重载:通俗点就是一个方法名,不同的参数类型,例如我想实现一个批量删除功能,方法的名字同样想起名为Delete,那么只需要改变参数即可
public bool Delete(List<T> listT);
顾名思义,接口,即插即用,例如插座,只要有对应插头,插入即可使用了。
接口:在编程里面起到的是一种规范,既然是规范,必定是约束若干人的,也就是团队开发项目,大家共同遵循一个规范,协调开发代码,而且互不干扰。
接口更多的是被软件架构师使用,设计好整体的软件架构,直接交给程序员开发即可,程序员只需要按照这种规范做就可以了,对每个接口进行具体的实现。
注意哦:接口不实现,是可以通过编译的哦
通过采用上面的思想,基本上我们可以实现一套牢固的架构了,而且可以复用到任何的系统中。
但技术是在不断革新的,而且需求也是不断变化的,更可怕的是能够运行系统的设备越来越多了,从PC到普通手机,再到智能手机(Android,IOS,WP7),再到平板电脑等等。
还有语言的差异,Java,php等等
这就要求一套架构不但要牢固,更能够适用于各种平台的开发。
为此我们的架构可以引申成一个服务,将上面的架构在加上一个服务层(可以用WCF,可以用MVC4的WebAPI,Restful规范),那么整体的架构就是下面这个样子了。
软件开发其实就是一种思想,将人的一种思想通过代码形式反映出来。好好用心思考,你就明白为什么会这么做,仔细体会其中的妙处。