面向对象编程(Object Oriented Programming)概念总结及延伸(一)
1.介绍
笔者的梦想是成为一个架构师,但是要成为一个合格的架构师是相当不易的,它既需要丰富的项目经验也需要不断地吸取新的知识,而且在这过程中我们也要不断巩固基础知识。我也注意到了,现在主流的文章大都集中到了新技术新的框架的学习,大家对于最新的技术都怀有无比好奇的学习态度,这点是好的,可是只是一味站在高层学习而忽略了最本质的知识,这是不好的,所以笔者在吸取了很多教训之后决定写此篇文章总结自己对面向对象知识的学习。
另外,需要提出的一点是,现在的开发大多基于框架或者现有类库,因为开发人员要在很短的时间内完成开发任务,而且对于开发人员的培训来说,课题也主要集中在了框架的学习上而忽略了基础,我觉得这点不是很好,尤其是对想成为出色架构师的人而言。而如果当他们过渡到了软件架构师的职位后,虽然职称变了,以前遗留下来对于基础知识以及架构知识的真空将成为架构高质量软件的瓶颈。
笔者本人是也无例外的成为主流趋势下的受害者,荒废了多年的基础,今天要痛定思过总结面向对象的基础知识,希望大家能帮助我完善,在这先谢谢大家了。
2.背景
我们大家或多或少都应该有这么个经验,作为新手时我们常常挣扎于理解一个新概念的准确定义,而对于有相关经验的人他们则能够很好地了解这些概念的意思,它的用途是什么。作为曾经不明白它意义的人之一,曾经的煎熬历历在目,其中的痛苦真是不言而喻。上面的道理到了软件架构也无一例外的相似,例如:当你作为架构师开始设计自己第一个系统的时候,各方面的盲点一下子涌现了出来,这其中的痛苦真的令人很沮丧,你将应用所有你学过的知识去尽力完善设计,例如:以前的经验告诉你每个类都要为其定义接口。总之,这只能是一个痛苦的过程。还有可能有人会嘲笑你设计的系统是错误的,然后你根据建议又进入了再学习的过程。在这过程中你思考了很多读了很多资料,在不断的挫折中为以前基础的荒废而还债。我希望本文能作为一个启发式的文章,让大家加深对基础的学习,为以后的工作开个好头。
3.主要内容
3.1 什么是软件架构
软件架构可以被定义为以下准则:
(1)将问题和系统划分为相互独立的不同部分
(2)创建上述独立部分之间的接口的技术
(3)管理全局的结构和流程的技术
(4)为系统和运行环境定义接口的技术
(5)能合理的使用开发和产品发布的工具、技术和方法
3.2 为什么架构这么重要?
软件架构的主要目的是为系统和环境定义非功能需求。详细设计是在系统功能按照架构准则划分后才开始的。架构很重要,因为好的架构具备以下特点:
(1)控制复杂度
(2)强调使用最佳实践
(3)要求系统的一致性和统一性
(4)为项目增加可预见性
(5)强调重用性
3.3 什么是面向对象编程(OOP)?
OOP是一个设计哲学。它代表面向对象编程(Object Oriented Programming)。OOP使用与过程编程语言(C语言、Pascal等)不同类型的编程语言。OOP中的单位是对象(Object),通过使用面向对象的编程理念我们获得了可重用性。
为了清晰地了解面向对象的概念,让我们举个简单的例子把,例如你的手,手就是一个对象。你拥有两中类型的手:左手和右手,它们的主要功能是由从你肩部的电信号进行管理和控制的,在这里肩部就是一个接口,身体通过这个接口和你的手进行交互。手就是一个类,通过轻微改动类中的属性(左和右)就可以达到了手这个类的重用。
3.4 什么是对象(Object)?
一个对象可以被认为是一个事物,它可以展开一些列相关的活动。对象可以展开的活动定义了对象的行为。例如:手可以抓东西,学生可以给出自己的姓名和年龄。
在纯面向对象中,一个对象被定义为一个类的实例。
3.5 什么是类(Class)?
一个类可以简单的代表一种类型的对象。它是一个描述对象细节的蓝本/模板,而对象是创建类的蓝本/模板。类包含三个部分:类名、属性和操作,如下图所示:
类在代码中的编写可以是这样:
public class Student
{
//属性和操作
}
在下面给出的代码中,我们可以说对象oStudent是由类Student创建的。
Student oStudent = new Student();
在现实世界中,你会发现很多单独的对象其实都是一类的。例如:世界上有很多的自行车,但它们都是同样的制造和模型。每个自行车都是由同一个蓝本创建的。在面向对向中,我们认为自行车是自行车类的实例。
在软件世界中,虽然你可能没有意识到,但是你已经在使用类了。例如,TextBox控件就是产生于TextBox这个类,它定义了外观和功能。每次你通过VS将TextBox拖拽到设计器中你就已经在创建一个TextBox的实例了。
3.6 怎样识别和设计类?
这是一门艺术。每个设计师都用不同的技术识别类。然而通过面向对象设计原则,有5个原则应该在设计一个类的过程中参考:
(1)SRP-The Single Responsibility Principle(单独职责原则)-
一个类应该有且只有一个理由去改变。
(2)OCP-The Open Closed Principle(开闭原则)-
一个类应该可以在不修改的前提下扩展类的行为。
(3)LSP-The LisKov Substitutable Principle(LisKov可替换原则)-
子类型必须能够替换掉它们的基类型,即子类需要具备父类所有的属性和行为
(4)DIP-The Dependency Inversion Principle-
应该依赖与抽象而不是具体。
(5)ISP-The Interface Segregation Principle-
为客户端定义合适粒度的接口
同时,为了正确地识别出类,你需要将系统功能分解为一个树状,对于这棵树,你需要识别出达到不可再分的叶子级别功能,然后你可以将各个功能进行分组,分组后就形成了类,记住,类必须将包含地是相同类型的功能和操作。然而一个定义良好的类必须是个有意义的功能集合俄日全额应该指出可重用性,尤其是对系统要求可维护性和可扩展性很高的情况下。
在软件世界中,有个概念叫做分治(dividing and conquering)经常被推荐,如果你在初始分析一个系统,你将发现很难去组织,所以最好的方式是首先将系统进行模块化划分,然后对每个模块单独进行深层分析,进而识别出类,这就是分治的核心思想。
一个软件系统可能由很多类组成。但对于任何情形,对于这些类都应该进行良好的组织。设想一下一个很大的机构,它们的员工有上千人(在这里我们假设一个员工就是一个类),为了有效地管理劳动力,你需要指定合理的管理策略。对于软件,管理策略也是必须的,为了管理软件系统的类,降低复杂性,软件设计者将这些策略分为以下四种:封装(Encapsulation)、抽象(Abstraction)、继承(Inheritance)和多态(Polymorphism),这四个概念是面向对象编程的主要准则。
3.7 什么是封装(或者信息隐藏)?
封装的含义是是隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别。对于一个封装的程序对象而言,它包含了实现自身功能的所有资源(数据和方法)。在OOP中,类通过暴露公有(public)方法或者属性实现封装的,这个类就像一个容器或者胶囊,它封装了方法和属性的细节,外部只用知道方法和属性的接口即可,通过封装我们可以对类内部的实现细节进行修改而会影像整个系统。总而言之,封装就是隐藏了怎么做,调用者只要知道它能做什么就行。
为了可以模块化/定义一个类的功能,这个类可以使用其它的类暴露的方法和属性,调用的方式可以有很多种形式,在面向对象编程中,有一些技术对于类之间互相连接,它们的名字是:关联(Association)、聚合(Aggregation)和组合(Composition)。
封装还有很多其它用途,例如我们经常使用的接口,接口可以用来隐藏一个类的实现信息。其实这也是种封装,如下所示:
IStudent oStudent = new LocalStudent();
IStudent oStudent = new ForeignStudent();
根据上边的例子(我们假设LoaclStudent和ForeignStudent实现了IStudent这个接口),我们可以看到接口隐藏了它们的实现细节。
3.8 什么是关联(Association)?
关联对于两个类而言是一种“a”的关系,简单说,当一个对象通过对另一个对象的引用去使用另一个对象的服务或者操作时,两个对象就互相关联了。关联是两个类关系中很普遍的定义,聚合和组合相对来说比较特殊。
public class StudentRegisterar
{
public StudentRegisterar()
{
new RecordManager().Initialize();
}
}
如上边的例子,我们可以说类StudentRegisterar和类RecordManager互相关联,也可以说有一个从StudentRegisterar到RecordManager的方向关联以及StudentRegisterar使用(“use”)了RecordManager。既然一个方向已经显式地指定了,在上例中StudentRegisterar是控制类。
对于初学者而言,关联是一个容易混淆的概念。造成这种混淆的主要原因是另两个OOP的概念,它们是组合和聚合。当每个人都懂得了关联的概念后,但在学习了聚合和组合后就变得混乱了。聚合和组合不能分开进行理解。如果你明白了聚合的概念,那它将会损坏你对关联的概念,如果你理解了组合的概念,很可能它会破坏你对聚合的理解,所以这三者要一起来学习和理解,通过对比找出其中的异同。所以下面让我们来了解以下我来了解下这三者之间的区别把。
3.9 关联、聚合和组合三者之间的区别
关联是两个类之间的一个“use”的关系,一个类使用了另一个类的方法。聚合是一种特殊的关联,对于两个类,它们是“has a”的关系。当类的一个对象拥有(has a)另一个对象,也就是一个对象成为另外一个对象的组成部分时,我们说它们之间有种聚合关系,与关联不同的是,聚合总是强调方向性。如下面的代码和UML图所示:
public class University
{
//一个大学总会有个法律顾问
private Chancellor universityChancellor = new Chancellor();
}
在上面的例子中我们可以说University聚合了Chancellor,或者说University有(has a)Chancellor。但是如果没有法律顾问(Chancellor)大学也能存在。但是一个大学如果没有院系(Faculties)就不可能存在,可以说University的生命周期依附于Faculty的声明周期。如果Faculty调用了dispose方法那么University就不能存在了。对于这种情形我们可以说Univesity和Faculty是一中组合的关系。所以组合可以说是一种特殊形式的聚合。
我们再来看看另一个例子,.NET和Java中其实就是使用了组合关系定义集合的,而且组合还被用到了很多别的方面中,只是大多数人忽略了生命周期这个概念,生命周期对于组合的两个对象而言是互相绑定的,它们之间互相依赖。所以如果你想将两个对象绑定为组合关系,那么最好的方式就是你直接定义被组合的类为另一个类的内部类,访问级别设为私有(private)或者保护(protected)。
所以简言之,我们可以说聚合是一种特殊的关联,而组合是一种特殊的聚合,而且关联->聚合->组合。
由于精力有限,今天就写到这里了,因为内容比较多所以难免存在脑子混乱和打字错误的可能,希望大家能帮助我修正,OOP的其它内容将在下篇文章中继续总结,谢谢。
posted on 2010-04-27 17:03 building7 阅读(1822) 评论(0) 编辑 收藏 举报