1.2 理解面向对象
对象以域(Field,也称为属性)的形式表达数据或状态,以方法(Method)的形式表达过程或行为;对象间可以相互访问或修改域,也可以调用行为;对象具有一定的生命周期(从初始化到最终消亡);所有对象一起建立协作关系,向外部提供软件服务。如今,面向对象的编程语言已经成为应用最广的软件开发语言,如Java、C#等。
1.2.1 面向对象的特征
在面向对象的概念中,对象具有状态变化,一般使用类(Class)定义对象的类型(Type)。类是对象的泛化和抽象,是静态的,可以通过面向对象编程语言进行描述。类经实例化生成具体的对象。面向对象的编程语言具有封装(Encapsulation)、继承(Inheritance)和多态(Polymorphism)等特征,用于实现软件系统业务模型时,具有天然优势。
1. 封装
封装是信息隐藏的一种形式。如果某个类将域或方法定义为私有(Private),则能够避免外部程序的干扰或错误访问。封装也能让程序员将业务相关性较强的数据或行为定义在一个类中,形成内聚度较强的代码单元,为软件解耦或复用提供便利。
下面的Java示例代码定义了Employee作为COS系统的员工类,该类将用户名(userName)、密码(password)域定义为受保护的(protected),登录行为(login())定义为公共(public)方法,验证密码行为(verifyPassword())定义为私有方法。Employee类的封装避免了外部程序直接访问Employee类对象的用户名、密码等受保护的域和验证密码等私有行为,在一定程度上保护了数据和业务实现的安全;同时,登录行为和密码验证行为是具有强内聚特性的业务逻辑,Employee类将其封装为一个独立的代码单元,减少了软件耦合,并增加了代码的复用便利。
Employee类示例代码如下。
public class Employee{ protected String userName;//用户名 protected String password;//密码 // 其他域或行为 / ** * 登录行为 * / public boolean login() { // 登录逻辑 //return 语句 } / ** * 验证密码行为 * / private boolean verifyPassword() { //验证逻辑 //return 语句 } }
2. 继承
继承是面向对象的重要特征之一,允许类以层次结构实现代码定义和复用。同时,继承也是物理世界中对象间关系的一种形式,能够使软件开发人员很容易地将目标领域的业务模型映射为技术模型。在继承关系中,被继承的类为父类,继承类为子类;子类可以继承父类的属性、行为和关系。
如下Java示例代码中定义了Patron作为COS系统的客户类,并且Patron类继承Employee类。程序员虽然没有在Patron类中定义登录行为,但Patron继承和复用了Employee类实现的登录行为,在一定程度上减少了代码开发成本。此外,这种复用方式还会为代码维护提供便利。如,当登录逻辑需求变更时,只需要修改父类Employee的login()业务逻辑,即能实现所有Employee子类对象的登录行为修复。
Patron类示例代码如下。
public class Patron extends Employee{ private String name;// 客户名称 private PatronLevel level;//客户等级 // 其他域或行为 // 支付订单行为 public void pay(){ //支付订单逻辑 //return 语句 } }
3. 多态
多态允许将父类对象的引用指向不同的子类对象,从而使得父类对象依据指向的子类对象执行不同的行为。多态也是一种抽象编程形式,可以向客户端屏蔽子类对象的差异,统一客户端对多态对象行为调用的形式,以达到客户端程序灵活适应需求变化的目的。
如下Java代码示例中的PayOrder类是COS系统的支付类,定义了支付订单行为(check ())、抽象的支付费用行为(pay())等。PayCard、PayRollDeduction继承PayOrder父类,实现了卡和工资抵扣的支付费用行为pay()。当客户端使用PayOrder类对象进行支付行为调用时,如果PayOrder对象引用指向PayCard类的对象,支付费用则为卡支付方式;如果PayOrder对象引用PayRollDeduction类的对象,支付费用即为工资抵扣支付方式。可见,对于调用PayOrder对象的客户端来说,支付行为表现为多态特性。
PayOrder类示例代码如下。
public abstract class PayOrder { private Date payDate;//支付日期 private float payAmount;//支付金额 / ** * 支付订单行为 * / public PayBill check(MealOrder o){ // 支付订单逻辑 //return 语句 } / ** * 支付费用行为 * / protected abstract boolean pay(MealOrder o); }
PayCard类示例代码如下。
public class PayCard extends PayOrder { / ** * 卡支付费用行为 * / @Override protected boolean pay(MealOrder o) { //卡支付费用逻辑 // return 语句 } }
PayRollDeduction类示例代码如下。
public class PayRollDeduction extends PayOrder { / ** * 工资抵扣支付费用行为 * / @Override protected boolean pay(MealOrder o) { // 工资抵扣支付费用逻辑 // return 语句 } }
假设,COS系统升级时需要添加新的支付方式,则开发人员只需要在源码中增加PayOrder子类实现新支付方式的逻辑即可,而不用更改依赖于PayOrder支付行为的客户端代码,达到了灵活适应需求变化的目的。
4. 其他特征
当然,除了封装、继承和多态外,面向对象还具有组合(Composition)、委托(Delegation)、包装(Wrapping)等特征,能够向软件开发人员提供很好的业务支持。
1.2.2 使用面向对象
在20世纪90年代,美国软件工程专家(如Grady Booch、Ivar Jacobson等人)较早提出了面向对象软件开发。早期的面向对象软件开发方法包括Booch方法(Booch Method)、OMT (Object-Modeling Technique,对象建模技术)、OOSE(Object-Oriented Software Engineering,面向对象软件工程)等。
面向对象软件开发方法分为面向对象分析(Object-Oriented Analysis,OOA)和面向对象设计(Object-Oriented Design,OOD)。面向对象分析的目标是建立业务领域的概念模型;而面向对象设计则是利用抽象或结构优化机制解决业务模型实现的问题。尽管软件分析和设计的目标不同,但面向对象分析与面向对象设计之间的边界并不明确。
面向对象分析方法有很多,举例如下。
(1)行为分析(Behavior Analysis):主要通过分析系统功能和动态行为抽取目标类或对象。
(2)领域分析(Domain Analysis):通过咨询领域专家,抽取重要的领域类或对象以及它们之间的关联。
(3)用例分析(Use-Case Analysis):以用例为中心,通过情景建模,抽取软件系统的类或对象。
面向对象设计是基于软硬件约束、性能需求、软件开发成本限制等条件,对软件系统概念模型进行架构设计、类或对象设计等活动的统称。面向对象设计的目标是建立软件业务逻辑的实现模型。
当前,面向对象软件开发方法主要使用UML进行软件概念模型、设计模型和物理模型的可视化表达,通过面向对象编程语言(如Java、C++等)实施软件逻辑编码。