重新认识面向对象

上传人:痛*** 文档编号:171811179 上传时间:2022-11-29 格式:PPT 页数:54 大小:223.54KB
收藏 版权申诉 举报 下载
重新认识面向对象_第1页
第1页 / 共54页
重新认识面向对象_第2页
第2页 / 共54页
重新认识面向对象_第3页
第3页 / 共54页
资源描述:

《重新认识面向对象》由会员分享,可在线阅读,更多相关《重新认识面向对象(54页珍藏版)》请在装配图网上搜索。

1、重新认识面向对象重新认识面向对象 OOAD 引子引子经常可以从开发人员口中听到经常可以从开发人员口中听到“面向对象面向对象”这个词这个词场景场景1 A:我今天开始用面向对象的方法设计程序了 B:你怎么做的?A:我把保存文件、加载文件封装成了一个类,以后只要调用这个类就可以实现文件操作了 写几个类就是面向对象了吗?写几个类就是面向对象了吗?场景场景2 A:我开始学习Java了,面向对象的语言,你 不要再学VB了,好土呀!B:VB怎么了?A:VB是面向过程的,已经过时了,Java中都是类,很时髦!B:VB中也有类呀!A:(无语)继承父类就是为了重用父类的代码吗?继承父类就是为了重用父类的代码吗?场

2、景场景3 A:面向对象思想就是好呀,我真的离不开Java了!B:你又用什么高超技术了?A:我今天从一个操纵数据库的类继承了一个子类,然后重写了它的保存到数据库的方法,然后把数据通过Socket发送到了远程客户端了,而调用者根本不知道,哈哈!覆盖父类的方法就可以瞒天过海了吗?覆盖父类的方法就可以瞒天过海了吗?场景场景4 A:我推荐你用的Java不错吧?B:真是不错,面向对象就是好,JDK里边也有好多好多的类可以用,不用像在VB里边那样要去查API文档了 A:但是我听说现在又出了个面向方面编程,咱们看来又落伍了呀,看来做编程真的不是长久之计。VB中也有类,它是面向对象吗?中也有类,它是面向对象吗?

3、类与对象类与对象-1 从程序设计语言的角度来分析“类”是用户自定义的具有一定行为的数据类型“对象”则是“类”这种数据类型的变量类与对象类与对象-2 以生活化的语言说明“类”是具有相同或相似行为的事物的抽象“对象”是“类”的实例,是是一组具有相关性的代码和数据的组合体,是有一定责任的实体 类与对象类与对象-3 类本身还可以进一步抽象为类型,类型是一种更高层次上的抽象,它只用来描述接口 比如抽象类和接口就是一种类型面向对象编程的特性面向对象编程的特性 面向对象编程有三个特性:封装,继承,多态 一种语言只有同时具备这三种特性才能被称为面向对象的语言 VB中也有类,它的类也支持封装和简单的继承,但是它

4、不支持所有的继承语义和多态,因此VB只能被称为基于对象的语言封装封装-1 将程序按照一定的逻辑分成多个互相协作的部分 并将对外界有用的稳定的部分暴露出来,而将会发生的改变隐藏起来 外界只能通过暴露的部分向这个对象发送操作请求从而享受对象提供的服务 而不必管对象内部是如何运行的,这就是封装封装封装-2 理解封装是理解面向对象的第一个步骤,40%的程序员对面向对象的理解仅停留在封装这个层次继承继承-1 继承也称为派生,继承关系中,被继承的称为基类,从基类继承而得的被称为派生类或者子类 继承是保持对象差异性的同时共享对象相似性的复用 能够被继承的类总是含有并只含有它所抽象的那一类事务的共同特点 继承

5、提供了实现复用,只要从一个类继承,我们就拥有了这个类的所有行为继承继承-2 理解继承是理解面向对象的第二个步骤,50%的程序员对面向对象的理解仅停留在继承这个层次多态多态-1 多态是“允许用户将父对象设置成为一个或更多的它的子对象相等的技术,赋值后,基类对象就可以根据当前赋值给它的派生类对象的特性以不同的方式运作”多态扩大了对象的适应性,改变了对象单一继承的关系 多态是行为的抽象,它使得同名方法可以有不同的响应方式,可以通过名字调用某一方法而无需知道哪种实现将被执行,甚至无需知道执行这个实现的对象类型多态多态-2 多态是面向对象编程的核心概念,只有理解了多态,才能明白什么是真正的面向对象,才能

6、真正发挥面向对象的最大能力 不过可惜的是,只有极少数程序员能真正理解多态对象之间的关系对象之间的关系 对象之间有两种最基本的关系:继承关系 组合关系继承关系继承关系 继承关系可以分为两种:一种是类对接口的继承,被称为接口继承 另一种是类对类的继承,被称为实现继承 继承关系是一种“泛化/特化”关系,基类代表一般,而派生类代表特殊 组合关系组合关系 组合是由已有的对象组合而成新对象的行为,组合只是重复运用既有程序的功能,而非重用其形式 组合与继承的不同点在于它表示了整体和部分的关系 比如电脑是由CPU、内存、显示器、硬盘等组成的继承继承,组合组合 派生类是基类的一个特殊种类,而不是基类的一个角色

7、语义上的“继承”表示“is-a”(是一种)的关系,派生类“is-a”基类,这是使用继承关系的最基本前提继承继承!?语义上的“继承”表示“是一种(is-a)”的关系。很多人体会到了继承在代码重用方面的优点,而忽视了继承的语义特征 可以使用“里氏替换原则-LSP”来防止滥用继承的情况就发生了里氏替换原则里氏替换原则-LSP 如果类A是类B的基类,那么类B应该可以在任何A出现的地方取代A继承继承 实例实例 客户信息系统客户信息系统1某企业中有一套信息系统,其中有一个“客户(Customer)”基础资料,里边记录了客户的名称、地址、email等信息后来系统要进行升级,增加一个“供应商(Supplier

8、)”基础资料,相比客户只是多了一个“银行帐号”设计人员很自然就把“供应商”设置成“客户”客户的子类 继承继承 实例实例 客户信息系统客户信息系统2 到了年终,老板要求给所有的客户通过Email发送新年祝福 由于“供应商”是一种(is-a)“客户”,所以系统就给“供应商”和“客户”都发送了新年祝福 第二天很多供应商都感动流涕的给老板打电话“谢谢老板呀,我们供应商每次都是求着贵公司买我们的东西,到了年终你们还忘不了我们,真是太感谢了!”老板很茫然,找来开发人员,开发人员这才意识到问题 于是在发送Email的程序里做了判断“如果是供应商则不发送,否则发送”,一切ok了继承继承 实例实例 客户信息系统

9、客户信息系统3 到了年初,老板要求给所有很长时间没有购买他们产品的“客户”,打电话进行问候和意见征集 由于“供应商”是一种(is-a)“客户”,所以第二天电话里不断出现这样的回答:“你们搞错了吧,我们是你们的供应商呀!”老板大发雷霆,开发人员这才意识到问题的严重性 在所有涉及到客户的地方都加了判断“如果是供应商则如果是供应商则”一共修改了60多处,当然由于疏忽遗漏了两处,所以后来又出了一次类似的事故继承继承 实例实例 客户信息系统客户信息系统4 更好的解决方案应该是,从“客户”和“供应商”中抽取一个共同的基类“外部公司”出来:派生类不应大量覆盖基类的行为派生类不应大量覆盖基类的行为 派生类具有

10、扩展基类的责任,而不是具有覆盖(override)基类的责任 如果派生类需要大量的覆盖或者替换掉基类的行为,那么就不应该在两个类之间建立继承关系继承继承 实例实例 库存管理系统库存管理系统1 客户需求:一张入库单一张出库单一张盘点单并且这三张单都有登帐的功能三张单的登帐逻辑都相同:遍历单据中的所有物品记录,然后逐笔登到台帐上去 继承继承 实例实例 库存管理系统库存管理系统2 过了三个月,用户提出了新的需求:盘点单在盘点过程中,如果发现某个货物的盘亏量大于50则停止登帐,并向操作人员报警 所以开发人员在盘点单中重写了“库存业务单据”的“登帐”方法,实现了客户要求的逻辑 又过了半个月,客户要求出库

11、登帐的时候不仅要进行原先的登帐,还要以便登帐一边计算出库成本 所以开发人员在出库单中重写了“库存业务单据”的“登帐”方法,实现了客户要求的逻辑 到了现在“库存业务单据”的“登帐”方法的逻辑只是对“入库单”有用了,因为其他两张单据都“另立门户”了 继承继承 实例实例 库存管理系统库存管理系统3“库存业务单据”的“登帐”方法设置成抽象方法,具体的实现代码由具体子类自己决定:继承继承 实例实例 库存管理系统库存管理系统4 更优的设计:使用组合继承的优点继承的优点 实现新的类非常容易,因为基类的大部分功能都可以通过继承关系自动赋予派生类;修改或者扩展继承来的实现非常容易;只要修改父类,派生的类的行为就

12、同时被修改了 继承的缺点继承的缺点 继承破坏封装性 基类的很多内部细节都是对派生类可见的,因此这种复用是“白箱复用”;如果基类的实现发生改变,那么派生类的实现也将随之改变。这样就导致了子类行为的不可预知性;从基类继承来的实现是无法在运行期动态改变的,因此降低了应用的灵活性 合理使用继承,提倡使用组合合理使用继承,提倡使用组合 使用组合将系统对变化的适应力从静态提升到动态,而且由于组合将已有对象组合到了新对象中,因此新对象可以调用已有对象的功能 由于组合关系中各个对象的内部实现是隐藏的,我们只能通过接口调用,因此我们完全可以在运行期用实现了同样接口的另外一个对象来代替原对象,从而灵活实现运行期的

13、行为控制 而且使用合成关系有助于保持每个类的职责的单一性,这样类的层次体系以及类的规模都不太可能增长为不可控制的庞然大物 优先使用组合而不是继承课间休息课间休息接口的概念接口的概念 接口是一种类型,它定义了能被其他类实现的方法 接口不能被实例化,也不能自己实现其中的方法,只能被支持该接口的其他类来提供实现 接口只是一个标识,标识了对象能做什么,至于怎么做则不在其控制之内,它更像一个契约 任何一个类都可以实现一个接口,这样这个类的实例就可以在任何需要这个接口的地方起作用,这样系统的灵活性就大大增强了 接口编程的实例接口编程的实例 SQL语句在各个不同的数据库之间移植最大的麻烦就是各个数据库支持的

14、语法不尽相同,比如取出表的前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

15、.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 ar

16、gs)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语句,这种增加只能是在编辑

17、源码的时候进行添加,无法在运行时动态添加 再来看一下如果用基于接口的编程方式是如何实现的 面向接口实例:面向接口实例:SQL语句翻译器语句翻译器3 首先,定义接口ISQLTranslator,这个接口定义了所有SQL翻译器的方法,目前只有一个翻译Select top的方法:public interface ISQLTranslator public String translateSelectTop(String tableName,int count);面向接口实例:面向接口实例:SQL语句翻译器语句翻译器4 为各个数据库写不同的翻译器类,这些翻译器类都实现了ISQLTranslator接口

18、: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 +t

19、ableName+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

20、;ISQLTranslator translator=new MSSQLServerTranslator();String sql=translator.translateSelectTop(tableName,count);System.out.println(sql);运行以后,打印出了:select top 10 from T_Table 面向接口实例:面向接口实例:SQL语句翻译器语句翻译器6可以看到,不同的数据库翻译实现由不同的类来承担,这样最大的好处就是可扩展性极强比如也许某一天出现了了支持中文语法的数据库,我要为它做翻译器只需再增加一个类:public class SinoSer

21、verTranslator 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.translateSel

22、ectTop(tableName,count);System.out.println(sql);运行后控制台打印出:读取表T_Table的前10行 面向接口实例:面向接口实例:SQL语句翻译器语句翻译器7 如果要给SQL语句加上验证功能,也就是翻译的时候首先验证一下翻译的结果是否能在数据库中执行,我们就可以采用偷天换日的方式来进行 面向接口实例:面向接口实例:SQL语句翻译器语句翻译器9首先创建一个VerifyTranslator类:public class VerifyTranslator implements ISQLTranslator private ISQLTranslator tr

23、anslator;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);PreparedS

24、tatement 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翻译器来行使翻译的责任了,不过它没有真正执行翻译,它把翻译的任务转发给了通过构造函数传递

25、来的那个翻译器变量: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();St

26、ring sql=translator.translateSelectTop(tableName,count);System.out.println(sql);运行后控制台打印出:wrong sql 面向接口实例:面向接口实例:SQL语句翻译器语句翻译器11 下面这段代码看上去是不是很眼熟呢?ISQLTranslator translator=new VerifyTranslator(new SinoServerTranslator(),getConnection();这段代码和我们经常写的流操作非常类似:InputStream is=new DataInputStream(new FileI

27、nputStream(new File(“c:/boot.ini”);这就是设计模式中经常提到的“装饰者模式”针对接口编程针对接口编程 1 从上面的例子我们可以看出,当代码写到:String sql=translator.translateSelectTop(tableName,count);代码编写者根本不关心translator这个变量到底是哪个类的实例,它只知道它调用了接口约定支持的translateSelectTop方法针对接口编程针对接口编程 2 针对接口编程,而不是针对实现编程是面向对象开发中的一个非常重要的原则,也是设计模式的精髓!违反针对接口编程原则的陋习违反针对接口编程原则的

28、陋习 陋习1 ArrayList list=new ArrayList();for(int i=0;i10;i+)list.add();违反针对接口编程原则的陋习违反针对接口编程原则的陋习 这里使用的是ArrayList的add方法,而add方法是定义在List接口中的,因此没有必要声明list变量为ArrayList类型,修改如下:List list=new ArrayList();for(int i=0;i10;i+)list.add();违反针对接口编程原则的陋习违反针对接口编程原则的陋习 陋习2 public void fooBar(HashMap map)Object obj=map.get(“something”);违反针对接口编程原则的陋习违反针对接口编程原则的陋习 在这个方法中只是调用Map接口的get方法来取数据,所以就不能要求调用者一定要传递一个HashMap类型的变量进来。修改如下:public void fooBar(Map map)Object obj=map.get(“something”);

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

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!