CSDN博客

img iltaek

日泽IT笔记系列1-Beginning C# Objects 从概念到代码-2

发表于2008/10/3 12:55:00  743人阅读

 

5 Relationships Between Objects

行为关系behavioral relationship):两个对象之间因互换信息而存在的一种短暂关系。

结构关系structural relationship):因为对象需要通过attribute的形式来持久地维护相关对象的句柄,才能保持这样的关系。

5.1 关联(association)与链接(link

类与类之间结构关系的正式名称是“关联”。关联是某个类/类型的对象间可能存在的关系。

链接”则是指两个特定对象(实体)间的结构关系。链接是这些特定类型的对象间实际存在的关系。链接是填充了成员对象的关联实体。

二元关联binary association):两个不同类之间的关联关系。

反身关联unary/reflexive association):相同类的两个实体间的关系。如:一门课是另一门课的先修课程;一位教授管理其他教授。

结对关系multiplicity)指可能和类型B一个实体相关联的类型A的对象数目。存在一对一,一对多,多对多结对关系。

结对关系的概念只适用于关联,不适用于链接。结对关系从本质上规定了一个对象的某种关联类型可以产生多少个链接。

聚合aggregation)是一种特殊形式的关联,它指明“包括”、“由……构成”或“有……”的关系。与关联不同的是,我们可以用聚合表述更多种关系:某对象属于类A,类A就是聚合类aggregate class),类A包括一些类型为B组件对象component object)。

当你倾向于用“包括”、“由……构成”等短语来命名类间关系,则该关系极可能是一种聚合关系。聚合和关联之间的微妙区别的确会影响到抽象模型在代码中如何被实现。

5.2 继承(Inheritance

继承是一种强大的机制,它通过指出新旧类之间的不同之处,在旧类的基础上定义一个新类。

C#类声明中,用冒号后面跟基类base class)的名称来标示继承关系。新类被称作派生类derived class),我们说派生类扩展extend)了基类。

继承有可能是OO语言中最强有力和最独特的方面之一,通过继承的方式,我们能够重用和扩展业已彻底测试过的代码,且无须修改之,最妙的是,即便我们没有一个类的代码,也可以从中派生出新类,这是面向对象语言带来的生产力显著提升的途径之一。

C#语言中,Object类(属于System命名空间)是所有其他类型的最顶基类,无论是用户自定义类型还是语言内嵌类型。

继承真的是一种类间关系吗?关联、聚合和继承都被称作是类间关系。继承和关联、聚合在对象层级上有所区别:继承是描述单个对象特征的一种方式。在继承的情形中,对象同时是派生类和该派生类的所有基类的实体。

覆载Override)可以不通过修改方法界面而改变一个方法或一个property的内部工作机理。

要覆载一个方法,必须现在所属类的基类中,使用virtual关键字把该方法声明为虚方法,如:public virtual void Print( ) { }。声明一个虚方法,表示该方法可能(而非必需)被派生类覆载。

派生类可以在方法声明中使用override关键字来重新实现基类虚方法,如:

public override void Print( ) { },或避免代码冗余,使用关键字base作为调用方法的指示符:

public override void Print( ) {  base.Print( );   }

派生类不可为:

1.  不可以改变特征的语义—即方法的目的、表达的意义;

2.  不可移除特征;

3.  不可修改property类型;

4.  不可试图改变方法头。

多载Overload)这种语言机制既为C那样的非OO语言支持,也为C#这样的OO语言所支持。

多载允许同一个类中两个或多个不同方法拥有相同方法名和不同的参数签名。这里只关心参数类型和顺序,而与参数名和方法返回类型无关,因为后者无法在消息中标定。

构造器是一种特殊的函数成员,用来实体化对象,构造器常被多载。

6 Collections of Objects

群集对象能够维系(包括)多个到其他类型对象的引用。能在对象被创建时将它们收集在一起,作为一个群组来管理和进行整体操作,并且在需要的时候还能单独引用它们中的一个。

对象本身在群集之外存在,但可以通过它们存储于群集中的引用来定位。

为了在特定情况下选择合适的群集类型,和有效地使用群集,只需要知道群集的公共特征—特别是对象方法头和property

6.1 作为简单群集的数组

System名称空间的Array是所有C#数组的基础。

声明数组:int[ ] x;

由于C#数组也是对象,必须用new操作符来实体化,在初次实体化数组时,也需要指明数组能容纳的子项数量,或其所包括空格的数量,称作尺寸

通过索引为数组中的单个元素赋值:names[0]=kimiltaek;

使用完整的值集合来初始化数组:string[ ] names={ kim, il, taek };

如果没有提供初始值,则数组中的元素将自动初始化为该种数据类型中与0相当的值。

C#关键字null在对象引用中是0的等价物,避开空单元“地雷”的代码如下:

If ( student[i] != null ) {  }

关于数组的其他问题:

1.  数组尺寸一旦被声明,就不能改变;

2.  在同一数组中不能混用不同数据类型。

多维数组: 矩形(rectangular)数组和锯齿状(jagged)数组

double[ , ] data = new double[2, 3];

仅指定数组第一维度的尺寸,下面的代码实体化一个三行的二维锯齿状string数组:

string [ ] [ ] names = new string [3], [ ];

6.2 更复杂的群集类型

有序列表Ordered lists)存储的项是以某种特定顺序放置的,无需在首次创建时指定其大小,当新项被插入时,有序列表能自动增加长度。当有项被移除时,留下的“空洞”会被自动填补。

C# ArrayList 类是实现有序列表的一个例子。

分类有序列表sorted ordered lists)是一种特殊的有序列表,把新项插入列表中适当的位置。为此,我们必须定义对象的分类排序依据,即,定义一个分类键sorted key)。

C# SortedList 类是实现分类有序列表的一个例子。

集合Sets)是一种未排序的群集。

字典(Dictionaries)提供一种手段,让对象引用被存储时,同时存储一个唯一的查询键,查询键通常源自对象的一个或多个attribute值。

C# Hashtable类是实现字典的一个例子。

创造自己的群集类型:

  1. 设计全的群集类型;

  2. 扩展一种预定义的群集类;

  3. 创建一个封装某种内建群集类型的“包装”类,用来隐藏与操作群集相关的一些信息。

如果把方法的返回类型定义为群集类型,则可以向使用该方法的客户端代码返回任意长度的对象引用群集。

再论组合类:通过将attribute定义为群集,从而创建相当复杂的组合类。

7 Some Final Object Concepts

多态是指两个或多个属于不同类的对象,对同一个消息(方法调用)做出不同响应的能力。

继承和覆载合起来实现了多态。   好处: 当新的子类被创建时,不会在客户代码中导致连锁反应,从而戏剧性地降低了代码维护成本。

7.1 抽象类(Abstract Class

把两个或多个类的共享特征—attribute和行为—集中到一个公共基类,这种手法称作概括

抽象类用来列举一个类所需要的行为,但不明确提供每个行为的具体实现方法。只要一个类包括一个或多个抽象方法,该类就必须是抽象类。

仅有方法头的方法称作抽象方法。抽象方法头部没有闭合的参数列表括号,而是直接跟了一个分号(;)

public abstract class Course

{   ………

   public abstract void EstablishCourseSchedule ( string startDate, string endDate );

}

抽象类不能被实体化,但可以声明到一个抽象类的引用变量。如: Course x;

覆载抽象方法:

public class LectureCourse : Course

{   ………

   public override void EstablishCourseSchedule ( string startDate, string endDate ) { …… }

}

从一个抽象类派生出的某个类,如果希望能够实体化该派生类型的对象,必须实现其所有祖先类的所有抽象方法。

7.2 接口(Interface

如果我们只想指定公共行为,而不想费心去声明attribute,则可以声明为接口:

public interface ITeacher {

bool AgreeToTeach( Course c );

Syllabus DesignateTextbook( TextBook b, Course c );

}

接口中的方法都隐含地是公共及抽象方法。

抽象类VS.接口:

1.  接口只指定抽象行为,抽象类经常会指定“具体的”数据结构(attribute),以及一些抽象行为和具体行为的混合体。

2.  派生类的方法头一定会包括override关键字;而实现接口时,是初次描绘方法轮廓。

3.  如果一个或多个抽象方法没有被覆载,则该派生类也是一个抽象类,实现一个接口,是一种“要么全做,要么不做”的事情。

4.  一个类只能从一个基类派生,而一个类却可以实现多个接口。

实现多个接口:

如果一个类实现的两个或多个接口中的方法拥有同样的方法签名,则在实现类中只需要实现其中一个,该方法将同时满足所有接口的实现要求。

一个类可能会同时扩展单个基类,和一个或多个接口。其中基类名称应放在最前面。

接口不能被实体化,因为接口中没有构造器。但能把引用变量声明为一个接口类型:

ITeacher t = new Professor( );

只要有可能,就应该把类的公共部分设计为接口,这样就可以让方法在多个方面拥有更多灵活性。诸如:方法的形参,方法的返回值。 如:

常用的一个C#预定义接口是Ilist接口,该接口被数种C#预定义群集类型所实现,包括Array类和ArrayList类等。如果我们编写一个操作群集的方法,该方法接受一个普遍Ilist引用,而不是特定的群集类型的引用,则这样的方法更为通用;客户代码可以传入任意类型的群集。

public void SomeMethod(Ilist list) { ……}

7.3 静态特征(Static Feature

直到现在,我们讨论的所有方法,attributeproperty都与一个类的实体相关联,每个对象都有这些特征的一份拷贝,并且能独立于其他对象操作它们。不过,有时我们也会需要一个对于类的所有实体都通用的特征,即有一个其值为给定类所有对象所共享的attribute

静态特征与作为一个整体的类相关联,而不是与单独的对象相关联。

静态方法的一些重要限制:

1. 它们不能访问类的非静态attribute。在非静态attribute被赋值之前,类还是一个空模板,直至我们实体化一个对象。

2. 不能被派生类覆载,所有不能给静态方法加上virtual关键字。

3. 静态方法也不能被声明为抽象方法。

实体变量:表示一个非静态attribute因为这样的attribute拥有一个实体或一个对象的值。

静态变量:表示一个静态attribute

本地变量:表示一个在方法内部声明的变量。

用具类:不必实体化相关对象,即可调用此类的静态方法。如Math类,它完全由静态方法和公共静态attribute组成。

常量:是一种给定初始值后就不会变化的变量。用const关键字来声明常量:

public const double FahrenheitFreezing = 32.0;

常量隐含地是静态的,所以不应给常量的声明加上static关键字。

在声明常量时必须给定一个值,给常量赋的值必须是在编译时可计算的表达式。

常量的类型必须是预定义数值类型(如char,int,double,byte等等)或string类型。

0 0

相关博文

我的热门文章

img
取 消
img