重新认识面向对象.ppt_第1页
重新认识面向对象.ppt_第2页
重新认识面向对象.ppt_第3页
重新认识面向对象.ppt_第4页
重新认识面向对象.ppt_第5页
已阅读5页,还剩49页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

重新认识面向对象,OOAD,引子,经常可以从开发人员口中听到 “面向对象” 这个词,场景1,A:我今天开始用面向对象的方法设计程序了 B:你怎么做的? A:我把保存文件、加载文件封装成了一个 类,以后只要调用这个类就可以实现文件 操作了,写几个类就是面向对象了吗?,场景2,A:我开始学习Java了,面向对象的语言,你 不要再学VB了,好土呀! B:VB怎么了? A:VB是面向过程的,已经过时了,Java中都 是类,很时髦! B:VB中也有类呀! A:(无语),继承父类就是为了重用父类的代码吗?,场景3,A:面向对象思想就是好呀,我真的离不开 Java了! B:你又用什么高超技术了? A:我今天从一个操纵数据库的类继承了一个 子类,然后重写了它的保存到数据库的方 法,然后把数据通过Socket发送到了远程 客户端了,而调用者根本不知道,哈哈!,覆盖父类的方法就可以瞒天过海了吗?,场景4,A:我推荐你用的Java不错吧? B:真是不错,面向对象就是好,JDK里边也 有好多好多的类可以用,不用像在VB里边 那样要去查API文档了 A:但是我听说现在又出了个面向方面编程, 咱们看来又落伍了呀,看来做编程真的不 是长久之计。,VB中也有类,它是面向对象吗?,类与对象 - 1,从程序设计语言的角度来分析 “类”是用户自定义的具有一定行为的数据类型 “对象”则是“类”这种数据类型的变量,类与对象 - 2,以生活化的语言说明 “类”是具有相同或相似行为的事物的抽象 “对象”是“类”的实例,是是一组具有相关性的代码和数据的组合体,是有一定责任的实体,类与对象 - 3,类本身还可以进一步抽象为类型,类型是一种更高层次上的抽象,它只用来描述接口 比如抽象类和接口就是一种类型,面向对象编程的特性,面向对象编程有三个特性:封装,继承,多态 一种语言只有同时具备这三种特性才能被称为面向对象的语言 VB中也有类,它的类也支持封装和简单的继承,但是它不支持所有的继承语义和多态,因此VB只能被称为基于对象的语言,封装 - 1,将程序按照一定的逻辑分成多个互相协作的部分 并将对外界有用的稳定的部分暴露出来,而将会发生的改变隐藏起来 外界只能通过暴露的部分向这个对象发送操作请求从而享受对象提供的服务 而不必管对象内部是如何运行的,这就是封装,封装 - 2,理解封装是理解面向对象的第一个步骤,40%的程序员对面向对象的理解仅停留在封装这个层次,继承 - 1,继承也称为派生,继承关系中,被继承的称为基类,从基类继承而得的被称为派生类或者子类 继承是保持对象差异性的同时共享对象相似性的复用 能够被继承的类总是含有并只含有它所抽象的那一类事务的共同特点 继承提供了实现复用,只要从一个类继承,我们就拥有了这个类的所有行为,继承 - 2,理解继承是理解面向对象的第二个步骤,50%的程序员对面向对象的理解仅停留在继承这个层次,多态 - 1,多态是“允许用户将父对象设置成为一个或更多的它的子对象相等的技术,赋值后,基类对象就可以根据当前赋值给它的派生类对象的特性以不同的方式运作” 多态扩大了对象的适应性,改变了对象单一继承的关系 多态是行为的抽象,它使得同名方法可以有不同的响应方式,可以通过名字调用某一方法而无需知道哪种实现将被执行,甚至无需知道执行这个实现的对象类型,多态 - 2,多态是面向对象编程的核心概念,只有理解了多态,才能明白什么是真正的面向对象,才能真正发挥面向对象的最大能力 不过可惜的是,只有极少数程序员能真正理解多态,对象之间的关系,对象之间有两种最基本的关系: 继承关系 组合关系,继承关系,继承关系可以分为两种: 一种是类对接口的继承,被称为接口继承 另一种是类对类的继承,被称为实现继承 继承关系是一种“泛化/特化”关系,基类代表一般,而派生类代表特殊,组合关系,组合是由已有的对象组合而成新对象的行为,组合只是重复运用既有程序的功能,而非重用其形式 组合与继承的不同点在于它表示了整体和部分的关系 比如电脑是由CPU、内存、显示器、硬盘等组成的,继承,组合,派生类是基类的一个特殊种类,而不是基类的一个角色 语义上的“继承”表示“is-a”(是一种)的关系,派生类“is-a”基类,这是使用继承关系的最基本前提,继承!?,语义上的“继承”表示“是一种(is-a)”的关系。很多人体会到了继承在代码重用方面的优点,而忽视了继承的语义特征 可以使用 “里氏替换原则 - LSP”来防止滥用继承的情况就发生了,里氏替换原则 - LSP,如果类A是类B的基类,那么类B应该可以在任何A出现的地方取代A,继承 实例 客户信息系统1,某企业中有一套信息系统,其中有一个“客户(Customer)”基础资料,里边记录了客户的名称、地址、email等信息 后来系统要进行升级,增加一个“供应商(Supplier)”基础资料,相比客户只是多了一个“银行帐号” 设计人员很自然就把“供应商”设置成“客户”客户的子类,继承 实例 客户信息系统2,到了年终,老板要求给所有的客户通过Email发送新年祝福 由于“供应商”是一种(is-a)“客户”,所以系统就给“供应商”和“客户”都发送了新年祝福 第二天很多供应商都感动流涕的给老板打电话“谢谢老板呀,我们供应商每次都是求着贵公司买我们的东西,到了年终你们还忘不了我们,真是太感谢了!” 老板很茫然,找来开发人员,开发人员这才意识到问题 于是在发送Email的程序里做了判断“如果是供应商则不发送,否则发送”,一切ok了,继承 实例 客户信息系统3,到了年初,老板要求给所有很长时间没有购买他们产品的“客户”,打电话进行问候和意见征集 由于“供应商”是一种(is-a)“客户”,所以第二天电话里不断出现这样的回答:“你们搞错了吧,我们是你们的供应商呀!” 老板大发雷霆,开发人员这才意识到问题的严重性 在所有涉及到客户的地方都加了判断 “如果是供应商则” 一共修改了60多处,当然由于疏忽遗漏了两处,所以后来又出了一次类似的事故,继承 实例 客户信息系统4,更好的解决方案应该是,从“客户”和“供应商”中抽取一个共同的基类“外部公司”出来:,派生类不应大量覆盖基类的行为,派生类具有扩展基类的责任,而不是具有覆盖(override)基类的责任 如果派生类需要大量的覆盖或者替换掉基类的行为,那么就不应该在两个类之间建立继承关系,继承 实例 库存管理系统1,客户需求: 一张入库单 一张出库单 一张盘点单 并且这三张单都有登帐的功能 三张单的登帐逻辑都相同: 遍历单据中的所有物品记录,然后逐笔登到台帐上去,继承 实例 库存管理系统2,过了三个月,用户提出了新的需求:盘点单在盘点过程中,如果发现某个货物的盘亏量大于50则停止登帐,并向操作人员报警 所以开发人员在盘点单中重写了“库存业务单据”的“登帐”方法,实现了客户要求的逻辑 又过了半个月,客户要求出库登帐的时候不仅要进行原先的登帐,还要以便登帐一边计算出库成本 所以开发人员在出库单中重写了“库存业务单据”的“登帐”方法,实现了客户要求的逻辑 到了现在“库存业务单据”的“登帐”方法的逻辑只是对“入库单”有用了,因为其他两张单据都“另立门户”了,继承 实例 库存管理系统3,“库存业务单据”的“登帐”方法设置成抽象方法,具体的实现代码由具体子类自己决定:,继承 实例 库存管理系统4,更优的设计:使用组合,继承的优点,实现新的类非常容易,因为基类的大部分功能都可以通过继承关系自动赋予派生类; 修改或者扩展继承来的实现非常容易; 只要修改父类,派生的类的行为就同时被修改了,继承的缺点,继承破坏封装性 基类的很多内部细节都是对派生类可见的,因此这种复用是“白箱复用”; 如果基类的实现发生改变,那么派生类的实现也将随之改变。这样就导致了子类行为的不可预知性; 从基类继承来的实现是无法在运行期动态改变的,因此降低了应用的灵活性,合理使用继承,提倡使用组合,使用组合将系统对变化的适应力从静态提升到动态,而且由于组合将已有对象组合到了新对象中,因此新对象可以调用已有对象的功能 由于组合关系中各个对象的内部实现是隐藏的,我们只能通过接口调用,因此我们完全可以在运行期用实现了同样接口的另外一个对象来代替原对象,从而灵活实现运行期的行为控制 而且使用合成关系有助于保持每个类的职责的单一性,这样类的层次体系以及类的规模都不太可能增长为不可控制的庞然大物 优先使用组合而不是继承,课间休息,接口的概念,接口是一种类型,它定义了能被其他类实现的方法 接口不能被实例化,也不能自己实现其中的方法,只能被支持该接口的其他类来提供实现 接口只是一个标识,标识了对象能做什么,至于怎么做则不在其控制之内,它更像一个契约 任何一个类都可以实现一个接口,这样这个类的实例就可以在任何需要这个接口的地方起作用,这样系统的灵活性就大大增强了,接口编程的实例,SQL语句在各个不同的数据库之间移植最大的麻烦就是各个数据库支持的语法不尽相同,比如取出表的前10行数据在不同数据库中就有不同的实现 MSSQLServer: Select top 10 * from T_Table MySQL: select * from T_Table limit 0,10 Oracle: select * from T_Table where ROWNUM =10,面向接口实例:SQL语句翻译器1,我们先来看一下最朴素的做法是怎样的:,public class Test1SQLTranslator private int dbType; public Test1SQLTranslator(int dbType) super(); this.dbType = dbType; public String translateSelectTop(String tableName, int count) switch (dbType) case 0: return “select top “ + count + “ * from “ + tableName; case 1: return “select * from “ + tableName + “ limit 0,“ + count; case 2: return “select * from “ + tableName + “ where ROWNUM=“ + count; default: return null; ,public static void main(String args) String tableName = “T_Table“; int count = 10; int dbType = 0; Test1SQLTranslator translator = new Test1SQLTranslator(dbType); String sql = translator.translateSelectTop(tableName,count); System.out.println(sql); ,面向接口实例:SQL语句翻译器2,如果要增加对新的数据库的支持 比如DB2,那么就必须修改Test1SQLTranslator类,增加一个对DB2的case语句,这种增加只能是在编辑源码的时候进行添加,无法在运行时动态添加 再来看一下如果用基于接口的编程方式是如何实现的,面向接口实例:SQL语句翻译器3,首先,定义接口ISQLTranslator,这个接口定义了所有SQL翻译器的方法,目前只有一个翻译Select top的方法: public interface ISQLTranslator public String translateSelectTop(String tableName, int count); ,面向接口实例:SQL语句翻译器4,为各个数据库写不同的翻译器类,这些翻译器类都实现了ISQLTranslator接口: public class MSSQLServerTranslator implements ISQLTranslator public String translateSelectTop(String tableName, int count) return “select top “ + count + “ * from “ + tableName; public class MySQLTranslator implements ISQLTranslator public String translateSelectTop(String tableName, int count) return “select * from “ + tableName +“ limit 0,“+count; public class OracleSQLTranslator implements ISQLTranslator public String translateSelectTop(String tableName, int count) return “select * from “ + tableName+“ where ROWNUM=“+count; ,面向接口实例:SQL语句翻译器5,如下调用: public static void main(String args) String tableName = “T_Table“; int count = 10; ISQLTranslator translator = new MSSQLServerTranslator(); String sql = translator.translateSelectTop(tableName, count); System.out.println(sql); 运行以后,打印出了: select top 10 from T_Table,面向接口实例:SQL语句翻译器6,可以看到,不同的数据库翻译实现由不同的类来承担,这样最大的好处就是可扩展性极强 比如也许某一天出现了了支持中文语法的数据库,我要为它做翻译器只需再增加一个类: public class SinoServerTranslator implements ISQLTranslator public String translateSelectTop(String tableName, int count) return “读取表“+tableName+“的前“+count+“行“; 修改调用代码: public static void main(String args) String tableName = “T_Table“; int count = 10; ISQLTranslator translator = new SinoServerTranslator(); String sql = translator.translateSelectTop(tableName, count); System.out.println(sql); 运行后控制台打印出: 读取表T_Table的前10行,面向接口实例:SQL语句翻译器7,如果要给SQL语句加上验证功能,也就是翻译的时候首先验证一下翻译的结果是否能在数据库中执行,我们就可以采用偷天换日的方式来进行,面向接口实例:SQL语句翻译器9,首先创建一个VerifyTranslator类: public class VerifyTranslator implements ISQLTranslator private ISQLTranslator translator; private Connection connection; public VerifyTranslator(ISQLTranslator translator, Connection connection) super(); this.translator = translator; this.connection = connection; public String translateSelectTop(String tableName, int count) String sql = translator.translateSelectTop(tableName, count); PreparedStatement ps = null; try ps = connection.prepareStatement(sql); ps.execute(); catch (SQLException e) DbUtils.close(ps); return “wrong sql“; return sql; ,面向接口实例:SQL语句翻译器10,这个类接受一个实现了ISQLTranslator 接口的变量和数据库连接做为构造参数,最重要的是这个类本身也实现了ISQLTranslator 接口,这样它就完全能“伪装”成SQL翻译器来行使翻译的责任了,不过它没有真正执行翻译,它把翻译的任务转发给了通过构造函数传递来的那个翻译器变量: String sql = translator.translateSelectTop(tableName, count); 它自己的真正任务则是进行SQL语句的验证: ps = connection.prepareStatement(sql); 再次修改调用代码: public static void main(String args) String tableName = “T_Table“; int count = 10; ISQLTranslator translator = new VerifyTranslator( new SinoServerTranslator(), getConnection(); String sql = translator.translateSelectTop(tableName, count); System.out.println(sql); 运行后控制台打印出: wrong sql,面向接口实例:SQL语句翻译器11,下面这段代码看上去是不是很眼熟呢? ISQLTranslator translator = new VerifyTranslator( n

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论