对着月亮唱歌的幸福生活

即便是莲藕内心真空之所,也有根根柔丝穿过。
  博客园  :: 首页  :: 联系 :: 管理

Object-Oriented:继承(Inheritance)

Posted on 2008-07-21 11:46  对月而歌  阅读(315)  评论(0)    收藏  举报

 

《Beginning c# Objects》读书笔记系列

1.: 继承(Inheritance)

2.: 多态(Polymorphism)

3.: 抽象类(Abstract Class)

4.: 接口(Interface)

5.:静态特征(Static Feature)

 

     假定已经正确而完整地通过Student类对"学生"的基本要素进行了建模,且已经用C#编写了该类.Student类的简板代码如下:

 

 1using System;
 2piblic class Student
 3{
 4 private string name;
 5 private string studentId;
 6 // 
 7
 8public string Name{
 9 get {
10   return name;
11 }

12 set {
13 name = value;
14 }

15}

16public string StudentId{
17 get 
18   return studentId;
19 }

20 set {
21 studentId = value;
22 }

23}

24// 
25}

    再进一步假设Student了的代码经过严格的检测,没有任何缺陷,而且已经用到一些应用程序中:例如,学生选课系统、学生交费系统、和校友录等等.

    对研究生建模是一种新的需求.研究生和我们已对之建模的普通学生之间的区别在于

* 在进入研究生阶段前,该生获得过什么本科学位;

* 在何处获得这个学位.

   用于描述研究生的其他信息--姓名、编号、存取这些信息的attribute和描述学生行为的方法等----与已经编好的代码的Student类没有区别,因为研究生到底也是学生.

 继承手法:

   使用面向对象编程语言,我们就能利用继承的好处来解决问题.继承是一种强大的机制,它通过指出新旧类之间的不同之处,在旧类基础上定义一个新类.使用继承手法,我们就能声明一个继承Student类所有特性的GraduateStudeng类.GraduateStudent类只需要照管俩个与研究生相关的attribute----undergraduateDegree和undergraduateInstituion.在C#类声明中,用冒号后面跟基类的名称来标示继承关系.

  

pbulic class GraduateStudent : Student{
//声明俩个和Student类不同的attribute
private string undergraduateDegree;
private string undergraduateInstitution;//Institution 机构
// 还有这俩个attribute的proerty
public string UndergraduateDegree{
 
get{
  
return undergraduateDegree;
 }

 
set{
  undergraduateDetree 
= value ;
 }

}

public string UndergraduateInstitution{
 
get{
  
return undergraduateInstitution;
 }

 
set{
  undergraduateInstitution 
= value ;
 }

}

}


继承的妙处:

*派生类要比非继承的类更为简洁.

*通过继承的方式,我们能够重用和扩展已彻底测试过的代码,且无须改之;

*最妙的是,即使我们没有一个类的代码,也可以从中派生出新类!只要有编译好的类,继承机智便可管用;扩展一个类时并不需要其源代码.这是面向对象语言带来的生产力显著提升的途径之一:

*最后,如前面所示,分类是人类组织信息的自然方式;所以,惟有按照这种方式组织软件,并使其更为直观,放可达到易于维护和扩展的目的.

覆载:
覆载可以不通过修改方法截面而改变一个方法或一个pripertu的内部工作机理.例如,假设我们为Student了日定义了一个Print方法,用来打印一个学生对象的所有attribute值.

 

public class Student 
{
 
//Attributes.
 private string name;
 
private string studentId;
 
private string majorField;
 
private double gpa;
 
//等等
 
//这些attribute的property也已经提供,此处略
public void print(){
 
//打印Student类的所有attribute的值
 
//注意get访问器的用法
  Console.WriteLin("Student Name:"+Name+"\n"+
                   
"Student No.:"+StudentId+"\n"+
                   
"Major Field:"+MajorField+"\n"+
                   
"GPA:"+Gpa);
 }
}

    上例中的Print方法假设Student类的所有attribute已覆值.Property访问器用来存取相关的attribute值,而不是直接存取attribute.

    Student类的所有派生类都会继承这个方法,但是,还有一个问题:我们给GraduateStudent派生增添俩个新attribute----undergraduateDetree 和undergraduateInstitution.如果只是采取让GraduateStudent类原封不动的继承Student类Print方法这种懒方法,则在调用GraduateStudent的Pring方法时,总会答应从Student类继承的四个attribute----name、studentId、major和gpa因为Print方法的代码只能打印这些attribute值。然而,我们期望的是GraduateStudent的Print方法不但能打印这四个attribute值,还能打印underagraduateDetree和undergrateInstitution俩个增添的attribute值。

    使用面向对象语言,我们能覆载,或者说替换GraduateSutdent类从Student类继承下来的Pring方法。要覆盖一个方法,必须先在所属类的基类中,使用virtual关键字把该方法声明为虚方法。声明一个虚方法,表示该方法可能(而非必须)被派生类所覆载。

    派生类可以在方法声明中使用override关键字来重新实现基类虚方法。派生类中的覆载方法必须声明为何基类方法拥有同样的作用域、返回值、名称和参数列表。

    我们来看看GraduateStudent类会怎么覆载Student类的Pring方法:

   


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);
 }

}

public class GraduateStudent : Student 
{
 
string undergraduateDegree;
 
string undergraduateInstitution;
 
//property已赋值,此处从略
 
//现在覆载Student类的Pring方法;
 
//注意override 关键字
 public override void pring(){
   
//打印GraduateStudent类知道的attribute值;
   
//即从Student类继承的和自己声明的.
    Console.WriteLie("Student Name:"+Name+"\n"+
                     
"Student No.:"+StudentId+"\n"+
                     
"Major Field:"+MajorField+"\n"+
                     
"GPA:"+Gpa+"\n"+
                     
"Undergrad.Deg.:"+UndergraduateDegree+"\n"+
                     
"Undergrade.Inst.:"+UndergraduateInstitution);
 }

}

    GraduateStudent类的Print方法,就这样覆载
/替代了送Student类继承的版本.
    

    上例代码不太完美,因为GraduateStudent类Print方法的前四行重复了Student类Print方法的代码。应该避免代码冗余,因为他会让维护变成一场噩梦:如果想修改一处代码,就要拼命记得修改不知道什么地方的相同代码,最郁闷的是,如果忘记修改别处的相同代码,就会引起代码逻辑矛盾。我们应避免代码冗余,但却鼓励尽可能的重用代码。所以GraduateStudent类的Pring方法其实应该这样写:

 

public class GraduateStudent : Student
{
 
//细节从略 
 public overide void print(){
 
//调用基类的Pring方法,重用代码 
 base.Print();
 
//然后继续打印派生类特有的attribute 
 Console.WriteLine("Undergrad.Deg.:"+UndergraduateDegree+"\n"+
                   
"Undergrad.Inst.:"+UndergraduateInstitution);
  }

}

  我们使用C#关键字base做为方法调用的指示符:

  base.method Name(arguments);

  注意:在所有情况下,存取的作用范围、返回值和方法前必须保持一致----public void Pring()---这样覆载才能起作用。