重新认识面向对象.ppt

上传人:sccc 文档编号:5814186 上传时间:2023-08-22 格式:PPT 页数:54 大小:223.54KB
返回 下载 相关 举报
重新认识面向对象.ppt_第1页
第1页 / 共54页
重新认识面向对象.ppt_第2页
第2页 / 共54页
重新认识面向对象.ppt_第3页
第3页 / 共54页
重新认识面向对象.ppt_第4页
第4页 / 共54页
重新认识面向对象.ppt_第5页
第5页 / 共54页
点击查看更多>>
资源描述

《重新认识面向对象.ppt》由会员分享,可在线阅读,更多相关《重新认识面向对象.ppt(54页珍藏版)》请在三一办公上搜索。

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

2、个子类,然后重写了它的保存到数据库的方法,然后把数据通过Socket发送到了远程客户端了,而调用者根本不知道,哈哈!,覆盖父类的方法就可以瞒天过海了吗?,场景4,A:我推荐你用的Java不错吧?B:真是不错,面向对象就是好,JDK里边也有好多好多的类可以用,不用像在VB里边那样要去查API文档了 A:但是我听说现在又出了个面向方面编程,咱们看来又落伍了呀,看来做编程真的不是长久之计。,VB中也有类,它是面向对象吗?,类与对象-1,从程序设计语言的角度来分析“类”是用户自定义的具有一定行为的数据类型“对象”则是“类”这种数据类型的变量,类与对象-2,以生活化的语言说明“类”是具有相同或相似行为的

3、事物的抽象“对象”是“类”的实例,是是一组具有相关性的代码和数据的组合体,是有一定责任的实体,类与对象-3,类本身还可以进一步抽象为类型,类型是一种更高层次上的抽象,它只用来描述接口比如抽象类和接口就是一种类型,面向对象编程的特性,面向对象编程有三个特性:封装,继承,多态一种语言只有同时具备这三种特性才能被称为面向对象的语言VB中也有类,它的类也支持封装和简单的继承,但是它不支持所有的继承语义和多态,因此VB只能被称为基于对象的语言,封装-1,将程序按照一定的逻辑分成多个互相协作的部分并将对外界有用的稳定的部分暴露出来,而将会发生的改变隐藏起来外界只能通过暴露的部分向这个对象发送操作请求从而享

4、受对象提供的服务而不必管对象内部是如何运行的,这就是封装,封装-2,理解封装是理解面向对象的第一个步骤,40%的程序员对面向对象的理解仅停留在封装这个层次,继承-1,继承也称为派生,继承关系中,被继承的称为基类,从基类继承而得的被称为派生类或者子类继承是保持对象差异性的同时共享对象相似性的复用能够被继承的类总是含有并只含有它所抽象的那一类事务的共同特点继承提供了实现复用,只要从一个类继承,我们就拥有了这个类的所有行为,继承-2,理解继承是理解面向对象的第二个步骤,50%的程序员对面向对象的理解仅停留在继承这个层次,多态-1,多态是“允许用户将父对象设置成为一个或更多的它的子对象相等的技术,赋值

5、后,基类对象就可以根据当前赋值给它的派生类对象的特性以不同的方式运作”多态扩大了对象的适应性,改变了对象单一继承的关系多态是行为的抽象,它使得同名方法可以有不同的响应方式,可以通过名字调用某一方法而无需知道哪种实现将被执行,甚至无需知道执行这个实现的对象类型,多态-2,多态是面向对象编程的核心概念,只有理解了多态,才能明白什么是真正的面向对象,才能真正发挥面向对象的最大能力不过可惜的是,只有极少数程序员能真正理解多态,对象之间的关系,对象之间有两种最基本的关系:继承关系组合关系,继承关系,继承关系可以分为两种:一种是类对接口的继承,被称为接口继承另一种是类对类的继承,被称为实现继承继承关系是一

6、种“泛化/特化”关系,基类代表一般,而派生类代表特殊,组合关系,组合是由已有的对象组合而成新对象的行为,组合只是重复运用既有程序的功能,而非重用其形式组合与继承的不同点在于它表示了整体和部分的关系比如电脑是由CPU、内存、显示器、硬盘等组成的,继承,组合,派生类是基类的一个特殊种类,而不是基类的一个角色语义上的“继承”表示“is-a”(是一种)的关系,派生类“is-a”基类,这是使用继承关系的最基本前提,继承!?,语义上的“继承”表示“是一种(is-a)”的关系。很多人体会到了继承在代码重用方面的优点,而忽视了继承的语义特征可以使用“里氏替换原则-LSP”来防止滥用继承的情况就发生了,里氏替换

7、原则-LSP,如果类A是类B的基类,那么类B应该可以在任何A出现的地方取代A,继承 实例 客户信息系统1,某企业中有一套信息系统,其中有一个“客户(Customer)”基础资料,里边记录了客户的名称、地址、email等信息后来系统要进行升级,增加一个“供应商(Supplier)”基础资料,相比客户只是多了一个“银行帐号”设计人员很自然就把“供应商”设置成“客户”客户的子类,继承 实例 客户信息系统2,到了年终,老板要求给所有的客户通过Email发送新年祝福由于“供应商”是一种(is-a)“客户”,所以系统就给“供应商”和“客户”都发送了新年祝福第二天很多供应商都感动流涕的给老板打电话“谢谢老板

8、呀,我们供应商每次都是求着贵公司买我们的东西,到了年终你们还忘不了我们,真是太感谢了!”老板很茫然,找来开发人员,开发人员这才意识到问题于是在发送Email的程序里做了判断“如果是供应商则不发送,否则发送”,一切ok了,继承 实例 客户信息系统3,到了年初,老板要求给所有很长时间没有购买他们产品的“客户”,打电话进行问候和意见征集由于“供应商”是一种(is-a)“客户”,所以第二天电话里不断出现这样的回答:“你们搞错了吧,我们是你们的供应商呀!”老板大发雷霆,开发人员这才意识到问题的严重性在所有涉及到客户的地方都加了判断“如果是供应商则”一共修改了60多处,当然由于疏忽遗漏了两处,所以后来又出

9、了一次类似的事故,继承 实例 客户信息系统4,更好的解决方案应该是,从“客户”和“供应商”中抽取一个共同的基类“外部公司”出来:,派生类不应大量覆盖基类的行为,派生类具有扩展基类的责任,而不是具有覆盖(override)基类的责任如果派生类需要大量的覆盖或者替换掉基类的行为,那么就不应该在两个类之间建立继承关系,继承 实例 库存管理系统1,客户需求:一张入库单一张出库单一张盘点单并且这三张单都有登帐的功能三张单的登帐逻辑都相同:遍历单据中的所有物品记录,然后逐笔登到台帐上去,继承 实例 库存管理系统2,过了三个月,用户提出了新的需求:盘点单在盘点过程中,如果发现某个货物的盘亏量大于50则停止登

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

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

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

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

14、Type;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 tab

15、leName=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语句,这种增加只能是在编辑源码的时候进行添加,无法在运行时动态添加再来看一下

16、如果用基于接口的编程方式是如何实现的,面向接口实例:SQL语句翻译器3,首先,定义接口ISQLTranslator,这个接口定义了所有SQL翻译器的方法,目前只有一个翻译Select top的方法:public interface ISQLTranslator public String translateSelectTop(String tableName,int count);,面向接口实例:SQL语句翻译器4,为各个数据库写不同的翻译器类,这些翻译器类都实现了ISQLTranslator接口:public class MSSQLServerTranslator implements IS

17、QLTranslator 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 OracleSQLTransl

18、ator 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 sq

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

20、ring 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行,面

21、向接口实例:SQL语句翻译器7,如果要给SQL语句加上验证功能,也就是翻译的时候首先验证一下翻译的结果是否能在数据库中执行,我们就可以采用偷天换日的方式来进行,面向接口实例:SQL语句翻译器9,首先创建一个VerifyTranslator类:public class VerifyTranslator implements ISQLTranslator private ISQLTranslator translator;private Connection connection;public VerifyTranslator(ISQLTranslator translator,Connectio

22、n 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

23、.close(ps);return wrong sql;return sql;,面向接口实例:SQL语句翻译器10,这个类接受一个实现了ISQLTranslator 接口的变量和数据库连接做为构造参数,最重要的是这个类本身也实现了ISQLTranslator 接口,这样它就完全能“伪装”成SQL翻译器来行使翻译的责任了,不过它没有真正执行翻译,它把翻译的任务转发给了通过构造函数传递来的那个翻译器变量:String sql=translator.translateSelectTop(tableName,count);它自己的真正任务则是进行SQL语句的验证:ps=connection.prepa

24、reStatement(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,面向接口实例:S

25、QL语句翻译器11,下面这段代码看上去是不是很眼熟呢?ISQLTranslator translator=new VerifyTranslator(new SinoServerTranslator(),getConnection();这段代码和我们经常写的流操作非常类似:InputStream is=new DataInputStream(new FileInputStream(new File(“c:/boot.ini”);这就是设计模式中经常提到的“装饰者模式”,针对接口编程 1,从上面的例子我们可以看出,当代码写到:String sql=translator.translateSelec

26、tTop(tableName,count);代码编写者根本不关心translator这个变量到底是哪个类的实例,它只知道它调用了接口约定支持的translateSelectTop方法,针对接口编程 2,针对接口编程,而不是针对实现编程是面向对象开发中的一个非常重要的原则,也是设计模式的精髓!,违反针对接口编程原则的陋习,陋习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”);,

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 建筑/施工/环境 > 农业报告


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号