大话设计模式 | 0 面向对象基础

《大话设计模式》是作者「程杰」通过趣味的场景设置,以诙谐的表达来解读和剖析「面向对象」编程思维和「设计模式」。书中的示例代码是以 .NET 的 C# 语言编写而成。
本文是我对《大话设计模式》的学习系列笔记的第一篇,面向对象基础。
1. 对象与类
首先,一切事物皆为对象。对象是一个自包含的实体,用一组可识别的特性和行为来标识。面向对象编程是指针对对象来进行编程,英文名为 Object-Oriented Programming。
类是具有相同的属性和功能的对象的抽象的集合。
举例来说,「猫」是一个类,「耶啵」这只猫是一个真实的对象,从类创建对象的过程叫做实例化。
在 C# 中,类的定义是通过 class 关键字来定义的,类名的首字母要大写,且对外公开的方法需要使用 public 修饰符。对象这个实例,是通过 new 关键字来创建的(实例化)。
2. 构造方法
构造方法,又叫构造函数,是为了对类进行初始化。构造函数与类同名,无返回值,也不需要 void,在 new 实例化时候调用。
所有的类都有构造方法,如果没有定义,则系统会默认生成空的构造方法,如上例中的 Cat()。若有定义,则默认的构造方法会失效。
在构造方法中,可以使用方法重载,以创建同名的多个方法的能力。在使用方法重载时,方法名必须相同,参数类型或个数必须不同。
3. 属性
属性包含 get 和 set 两个方法,get 访问器返回与声明的属性相同的数据类型,表示的意思是调用时可以得到内部字段的值或引用;set 访问器没有显示设置参数,但它有一个隐式参数,用关键字 value 表示,它的作用是调用属性时可以给内部的字段或引用赋值。
在调用属性的代码来说,属性也可以看作是一个字段。下面通过一些代码段来看下。
ShouNum 属性是 public 的,其 get 方法表示外界调用时可以得到 shoutNum 的值,set 方法有一个隐式的参数,用关键字 value 表示,作用是外界调用属性时可以给内部的字段 shoutNum 赋值。
这里,为什么不直接把 shoutNum 字段的修饰符改为 public 以实现既读又写呢?因为我们期望对那些对外公开的数据做更多的控制,不能让调用者任意的读取或写入。就好比一个房间,其门和窗是 public 的,但房间内部的东西希望是 private 的。
面向对象的三大特征分别是封装、继承和多态,下面来依次讲解。
4. 封装
每个对象都包含它能进行操作所需要的所有信息,而不必依赖其他对象来完成自己的操作,这个特性称为封装。这样,方法和属性都包装在类中,通过类的实例来实现。
封装的好处包括:
- 良好的封装能减少耦合 
- 类内部的实现可以自由地修改 
- 类具有清晰的对外接口,如 - ShoutNum属性和- Shout方法。
5. 继承
对象的继承代表了一种 is-a 的关系,如果两个对象A和B可以表述为B是A,那么表明B可以继承A,比如猫(B)是哺乳动物(A)就是一种继承和被继承的关系。继承者(子类、派生类)可以看作是被继承者(父类、基类)的特殊化,除了具备被继承者的一些特性外,还具备自己独有的个性。
- 子类拥有父类的非 - private的属性和功能
- 子类具有自己的属性和功能(可扩展) 
- 子类可以以自己的方式实现父类的功能(方法重写) 
比如,对猫和狗,都可以抽象为动物,将其共有的名字、叫的次数这个功能放到动物这个类中,通过继承以避免重复的代码。
继承的缺点就是,当父类发生变化时,子类必须也不得不变。也会破坏了包装,将父类的细节暴露给子类,增大了两个类之间的耦合性。
当类之间是 is-a 的关系时,可以考虑使用继承。但当两个类之间时 has-a 关系时,比如人有两只手,则不能使用继承了。
6. 多态
多态表示不同的对象可以执行相同的动作,但是要通过它们自己的实现代码来执行。需要注意以下三点:
- 子类以父类的身份出现 
- 子类在工作时以自己的方式来实现 
- 子类以父类的身份出现时,子类特有的属性和方法不可以使用 
为了让子类的实例完全接替来自父类的类成员,父类必须将该成员声明为虚拟的(virtual 关键字),子类通过使用 override 关键字来重写父类的实现。
比如,猫和狗都会叫,但是叫声不同,此时可以让动物有一个 Shout 的虚方法,然后猫和狗去重写这个Shout 方法以实现各自的叫声,以实现多态。
7. 重构
当我们在原来的基础上,好需要增加动物的种类来实现不同的叫声时,会发现在不同的动物的 Shout 方法中,除了叫声不同外,其他都是相同的,此时就需要对我们之前的代码进行改造了,来消除一些重复。
比如把 Shout 方法变为普通的方法放到 Animal 中,同时增加一个另一个方法 getShoutSound 来获取不同动物的叫声。这样,只需在不同的动物的实现中重写 getShoutSound 即可,将所有的重复都转移到父类的 Animal 中了。
8. 抽象类
C# 允许把类和方法声明为 abstract`,及抽象类和抽象方法。抽象类通常代表一个抽象概念,它提供一个继承的出发点,当设计一个新的抽象类时,一定是用来继承的。所以,在一个以继承关系形成的等级结构里面,树叶节点应该是具体类,而树枝节点均应该是抽象类。
抽象类有以下几点需要注意:
- 抽象类不能实例化 
- 抽象方法是必须要被子类重写的方法 
- 如果类中包含抽象方法,那么类就必须定义为抽象类 
前面的 Animal 类其实就是一个抽象的概念,是不可能实例化的,所以可以将其改造为抽象类:
9. 接口
接口是把隐式公共方法和属性组合起来,以封装特定功能的一个集合。声明接口在语法上与声明抽象类相同,但是不允许提供接口中任何成员的执行方式。一旦类实现了接口,类就可以支持接口所指定的所有属性和成员。实现接口的类,就必须要实现接口中的所有方法和属性。一个类可以支持多个接口,多个类也可以支持相同的接口。
接口通过 Interface 来声明,接口名称前需要加I,接口中的方法或属性前不能有修饰符、方法没有方法体。
比如对前面的猫,如果希望某些猫有特异功能,可以变出一些东西。但是特异功能并不是普通的猫有的能力,所以不能作为猫的一个方法。此时就需要借助接口了。
通过定义一个「变东西」的接口,然后让某些猫,比如机器猫,来实现定义好的「变东西」的接口。
抽象类与接口的不同点:
- 类是对对象的抽象,抽象类是对类的抽象,接口是对行为的抽象 
- 如果行为跨越不同类的对象,可使用接口;对于一些相似的类对象,用继承抽象化 
- 抽象类是从子类中发现公共的东西,然后泛化处父类,然后子类继承父类;而接口根本就不知道子类的存在,方法如何实现还不确认,预先定义。 
10. 集合
介绍集合前,先讲讲数组。数组在内存中是连续的,可以快速地从头到尾遍历元素并对元素进行修改;但数组在创建时必须要指定数组变量的大小,且在两个元素之间添加元素较为困难。
.NET Framework 针对这一问题提供了用于数据存储和检索的专用类,这些类统称为集合。这些类提供对堆栈、队列、列表和哈希表的支持,其中,ArrayList 是最为常用的一种。
ArrayList 是命名空间 System.Collection 下的一部分,它是使用大小可按需动态增加的数组实现 IList 接口。ArrayList 的默认初识容量为0,随着元素的添加,容量会根据需要重新分配自动增加,可通过整数索引访问集合中的元素。
ArrayList 是以 Object 类型来存储所有元素的,不管是字符串类型还是整型都能接受,但是在遍历运行时,会导致类型不匹配而出错,这说明 ArrayList 不是类型安全的。
由于 ArrayList 都是需要将值类型「装箱」为 Obejct 对象,在使用集合的元素时,还需要执行「拆箱」操作,这会带来很大的性能损耗(相对于简单的赋值,装箱和拆箱过程需要大量的计算,对值类型进行装箱时,必须分配并构造一个全新的对象)。
- 装箱,即把值类型打包到 - Obejct引用类型的一个实例中,比如整型变了- i被装箱并赋值给对象- o:
- 拆箱时指从对象中提取值类型,比如对象 - o拆箱并将其赋值给整型变量- i:
11. 泛型
泛型是具有占位符(类型参数)的类、结构、接口和方法,这些占位符是类、结构、接口和方法所存储或使用的一个或多个类型的占位符。泛型集合类可以将类型参数用作它所存储的对象的类型的占位符。
泛型集合需要使用System.Collections.Generic的命名空间,List 类是 ArrayList 类的泛型等效类,该类使用大小可按需动态增加的数组实现 IList 泛型接口。用法是在 IList 和 List 后面加 <T>,其中 T 是需要指定的集合的数据或对象类型。
泛型集合相对非泛型集合有两个优点:
- 类型安全 
- 不需要对元素进行装箱操作,如果集合元素都是值类型,性能会更优 
12. 委托与事件
委托是对函数的封装,可以当作给方法的特征指定一个名称。委托是一种引用方法的类型,一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托用关键字 delegate 来声明。
事件是委托的一种特殊形式,当发生有意义的事情时,事件对象处理通知过程。事件是说在发生其他类或对象关注的事情时,类或对象可通过事件通知它们。事件用关键字 event 来声明。
完整的代码实现可以参考:https://github.com/puran1218/DesignPatternWithCSharp
版权声明: 本文为 InfoQ 作者【Puran】的原创文章。
原文链接:【http://xie.infoq.cn/article/024954aa2b9c56df999bfd5d4】。文章转载请联系作者。












 
    
评论