《Beginning c# Objects》读书笔记系列
1.多态
多态指2个或2个不同类的对象,对同一个消息(方法调用)做出不同响应的能力.
例如,如果我们要三种人----外科医生、外科医生和演员----"cut" ,则
*外科医生会在病人身体上割开一个小口(cut = 切开)
*发型师会开始一剪头发(cut = 剪)
*演员会停止表演,等待导演下一步指令(cut = 停止拍摄)
可以把这三种不同职业的人安康内做属于不同类的对象。每个对西昂都得到同一个消息----"cut" ----但是他们知道对自己来说,这个命令以为着什么,因为他们都清楚自己的职业(类)特征。
回到SRS的例子,假定我们定义了基类Student,以及2个派生派生类,GraduateStudent和UndergraduateStudent.
我们在第5章讨论过,Print方法被设计为打印Student类的所有attribute值,这对于GraduateStudent等派类可能不够用,为Student类编写的代码不可能知道派生类中天家的attribute3.所以我们要负载Student类的Print方法,为每个类创建特制的版本。现在来回顾当时我们介绍过的代码:我们添加了UndergraduateStudent类的代码,也对另外俩个类的Print方法做了少许增强处理.
public class Student
{
//Attributes.
private string name;
private string studentId;
private string majorField;
private double gpa;
//等等
//这些attribute的property也已经提供,此处略
//Student 类的Print方法被声明为虚方法,
//可以被派生类覆载
public virtual void print(){
//打印Student类知道的attribute的值,注意get访问器的用法
//注意get访问器的用法
Console.WriteLin("Student Name:"+Name+"\n"+
"Student No.:"+StudentId+"\n"+
"Major Field:"+MajorField+"\n"+
"GPA:"+Gpa);
}
{
//增加一些attribute
private string undergraduateDegree;
private string undergraduateInstitution;
//也提供了公共的attribute(细节从略)

//覆载Pring方法.
public override void print(){
//通过print 方法重用student代码

base.print();
//
然后打印派生类的特有attribute值.Console.writeLine("Undergrad.Deg.:"+UndergraduateDegree+"\n"+"Undergrad.Inst.:"+
UndergraduateInstitution+"\n"+"THIS IS A GRADUATE STUDENT
"};}
}
{
//增加一个attribute
private string highSchool;
//也提供了公共property(细节从略)


//覆载Print方法.
public override void Print(){
//重用Student的代码


base.Pring();
//

然后打印派生类特有的attribute值.Console.WriteLine("High School Attended:"+HighSchool+"\n"+"THIS IS AN
UNDERGRADUATE STUDENT
");}
}
在SRS应用程序中,我们声明了一个名为studentBody的数组,用来保存对Student类对象的引用。跟着我们用一些Student对象引用----研究生和本科生,随机组合--填充该数组:
Student[] studentBody = new Student[20];
// Instantiate various types of Student object.
UndergraduateStudent u1 = new UndergraduateStudent();
UndergraduateStudent u2 = new UndergraduateStudent();
GraduateStudent g1 = new GraduateStudent();
GraduateStudent g2 = new GraduateStudent();
//等等
//按随机顺序倒入数组.
studentBody[0] = u1;
studentBody[1] = g1;
studentBody[2] = g2;
studentBody[3] = u2;
//等等
因为需要在数组中存放GraduateStudent 和UndergraduateStudent对象,所以得声名数组的类型为Studengt,即数组存放的所有对象的基类.依据继承的"is a "特性,这样编译器不会在对 数组中同时存在两种类型的对象产生抱怨.
或许我们想打印在studentBody数组中所有Student对象的attribute值。我们希望数组中的每个Student对象--无论是研究生还是本科生----准确的调用自己所属类的Print对象,下面代码能很好的做到这一点:
//遍历数组(群集)......
for (int i = 0 ; i <20;i++){
//... ...调用第i个Student对象。
studentBody[i].Print();
}
在我们遍历集群中的Student对象时,根据他们对于自身类型的认识,每个对象都字的知道应该调用哪个版本的Print方法。执行后会的到类似下面的结果,粗字体标出的代码行强调了GraduateStudent和UndergraduateStudent版本的Pring方法的不同之处:
//本科
Student Name: John Smith
Student No.: 12345
Major Field: Biolgy
GPA: 2.7
High School Attenden: Rocky Mountain High
THIS IS AN UNDERGRADUATE STUDENT ...
//研究生
Student Name: Paula Green
Student No.: 34567
Major Fild: Edncation
GPA: 3.6
Undergrad. Deg.: B.S.English
Undergrad. Inst..: UCLA
THIS IS A GRADUATE STUDENT...
...
...
术语"多态(polymorphism[pɔli'mɔ:fizəm ])"在<<韦氏大词典>>中被定义为"可以呈现不同形式的能力或状态".
代码行
studentBody[i].Print();
是多态的,因为响应消息的方法代码可以根据对象的类标识,呈现多种样式.
当然,这种遍历群集、逐个要求对象执行自身类某种特定操作的方式,只有在群集所有对象明白发出的消息时才成立。也就是说,studentBody数组中所有对象都必须定义了拥有这个签名的方法:pring().当然,我们保证studentBody数组中所有对象都会有这个方法。
*首先,我们声明该数组可容纳类型为Student(或其派生类)的对象。
*其次,我们在基类Student中包括了灭有参数的Print方法(否则编译器就回拒绝通过下面的代码
studentBody[i].Print();
因为它会检查Student类,寻找Print方法)
*再次,由于继承机智的缘故,Student类的所有派生类都会继承Student类版本的Print方法,或者覆载该方法.因为派生类没办法"不继承"其任何一个祖先类中定义的方法。所以底线是所有声明为Student类型的对象,都得保证懂得打印"Print"!
稍稍琢磨一下,你就会发现,自己早已学到过所有关于C#如何实现多态的知识,即继承加上覆载。继承和覆载结合起来实现了多态。
2.多态简化了代码维护
为了更明白多态的妙处,我们来看看怎么在不支持多态的语言中应付同一个挑战----用不同的类型指定方式维护不同对象。
没有了多态机制,通常我们只能用一系列if测试来判断Student对象类型:
for(int i = 0 ;i<20;I++){
//处理第i个Student对象.
//伪代码
if (studentBody[i] is an underguaduate studern)
studentBody[i].PrintAsUndergraduateStudent();
else if (studentBody[i] is a graduate student)
studentBody[i].PrintAsGraduateStudent();
else if......
}
在情况越来越复杂时,就会导致一大堆象意大利面般缠夹不清的代码!而且要知道,这类if测试代码会在整个应用程序中无数地方出现。维护这样的代码简直如同噩梦。
对照多态方式:
//编历(群集)... ...
for(int i = 0;i<20;i++){
//... ...调用第i个Student对象的Print方法。
studentBody[i].Print();
}
由于可户端程序可以操作不同对象,而无须考虑对象具体子类型,所以不需要做什么修改。例如在SRS程序编码和测试之后,我们又从GraduateStudent类派生出新类PhDStudent和MastersStudent,他们都覆写了Print方法
我们可以随便把MastersStudent和PhDStudent对象塞到数组中,同GraduateStudent和UndergraduateStudent对象放在一起,而根本不用修改我们的多态的遍历代码!
//声明和初使化数组
Student[] sutdentBody = new Student[20];
//初始化不同类型的Student对象。
//现在处理4中派生类
UndergraduateStudent u1 = new UndergraduateStudent();
phDStudent p1 = new phDStudent();
GraduateStudent g1 = new GraduateStudent();
MastersStudent m1 = new MastersStudent();
//等等
//乱插入数组
studentBody[0] = u1;
studentBody[1] = p1;
studentBody[2] = g1;
studentBody[3] = m1;
//等等,
//然后,在后面的程序中
..
//和前面看到的代码一模一样!
//遍历数组(群集)

for (int i = 0 ;i<studentBody.size;i++){
//
调用其中每个Student对象的print方法。
//因为C#的多态特性,下面的代码无须做任何改变!
studentBody[i].Print();
这是因为,新的派生类----MastersStudent和phDStudent----是Student类的扩展,这就保证了它们能理解同一个Print消息(通过集成和可选的覆写)。
这和我们之前战士的非多态例子全然不同。那个版本必须为适应新的Student类型而修改客户代码;更有甚者,我们还得在应用程序中四处追捕,找到所有涉及Student类型的地方加以修改,加入更多if测试,让它变得越来越复杂:
//处理第i个对象
//伪代码。
if(studentBody[i] is an undergraduate student)
studentBody[i].PrintAsUndergraduateStudent();
else if (studentBody[i] is amasters student);
studentBody[i].PrintAsprintAsMastersStudent();
else if (studentBody[i] is aphD student)
studentBody[i].PrintAsphDStudent();
else if (studentBody[i] is ageneric graduate student)
studentBody[i].PrintAsGraduateStudent();
elsse if

}
意大利面条越堆越高拉。
如我们在前面讨论封装和信息隐藏时所看到的那样,多态是oo语言中另一有力的特征,它降低了在应用程序奋发后,需求发生不可避免的改变时已存在应用程序的连锁反应。

浙公网安备 33010602011771号