河源市seo网站设计,什么叫软件外包公司,ps外包网站,北京有名的装修公司目录
1.引言
2.有一个关系
3.是一个关系(继承)
4.“有一个”与“是一个”的区别
5.not-a关系
6.层次结构
7.多重继承
8.混入类 1.引言 作为程序员#xff0c;必然会遇到这样的情况#xff1a;不同的类具有共同的特征#xff0c;至少看起来彼…目录
1.引言
2.有一个关系
3.是一个关系(继承)
4.“有一个”与“是一个”的区别
5.not-a关系
6.层次结构
7.多重继承
8.混入类 1.引言 作为程序员必然会遇到这样的情况不同的类具有共同的特征至少看起来彼此有联系。面向对象的语言提供了许多机制来处理类之间的这种关系。最棘手的问题是理解这些关系的实质。类之间的关系主要有两类有一个has a关系和是一个(is a)关系。
2.有一个关系 有一个关系模式是A有一个B,或者A包含一个B。在此类关系中可认为某个类是另一个类的一部分。前面定义的组件通常代表有一个关系因为组件表示组成其他类的类。 动物园和猴子就是这种关系的一个示例。可以说动物园里有一只猴子或者说动物园包含一只猴子。在代码中用Zoo对象模拟动物园这个对象有一个Monkey组件。 考虑用户界面通常有助于理解类之间的关系。尽管并非所有的UI都是虽然现在大多数都是以OOP方式实现的屏幕上的视觉元素也能很好地转换为类。UI关于“有一个”关系的类比就是窗口中包含一个按钮按钮和窗口是两个明显不同的类但又明显有某种关系。由于按钮在窗口中因此窗口中有一个按钮。 下图显示了实际的“有一个”关系和用户界面的“有一个”关系。 有一个关系有如下两种类型
1聚合通过聚合当聚合器被销毁时聚合对象组件可以继续存在。例如假如动物园对象包含一组动物对象。当动物园对象因为破产而被销毁时动物对象(理想情况下) 不会被销毁他们被转移到另外一个动物园。
2组合对于组合如果由其他对象组成的对象被销毁那么这些其他对象也会被销毁。例如如果包含按钮的窗口对象被销毁则这些按钮对象也将被销毁。
3.是一个关系(继承) “是一个”关系是面向对象编程中非常基本的概念。因此有许多名称包括派生(deriving)、子类subclass、扩展(extending)和继承(inheriting)。类模拟了显示世界包含具有属性和行为的对象这一事实继承模拟了这些对象通常以层次方式来组织这一事实。“是一个”说明了这种层次关系。 基本上继承的模式是A是一个B,或者A实际上与B非常相似-----这可能比较棘手。再次以简单的动物园为例但假定动物园里除了猴子之外还有其他动物。这句话本身已经建立了关系-----猴子是一种动物。同样长颈鹿也是一种动物袋鼠是一种动物企鹅也是一种动物。那又怎么样意识到猴子、长颈鹿、袋鼠和企鹅具有某种共性时继承的魔力就开始显现了。这些共性就是动物的一般特征。 对于程序员的启示就是可定义Animal类用于封装所有动物的属性大小、生活区域、食物等和行为走动、进食、睡觉。特定的动物例如猴子成为Animal的子类因为猴子包含动物的所有特征。记住猴子是动物并且它还有与众不同的其他特征。动物的继承关系如下图所示 就像猴子与长颈鹿是不同类型的动物一样用户界面中通常也有不同类型的按钮。例如复选框是一个按钮按钮只是一个可被单击并执行操作的UI元素Checkbox类通过添加状态相应的框是否被选中扩展了Button类。 当类之间具有“是一个”关系时目标之一就是将通用功能放入基类base class其它了可扩展基类。如果所有子类都有相似或完全相同的代码就应该考虑将一些代码或全部代码放入基类。这样可在一个地方完成所需的改动将来的子类可“免费”获取这些共享的功能。 1.继承技术 前面的示例非正式地讲述了继承中使用的一些技术。当生成子类时程序员有多种方法将某个类与其父类(也称为基类或超类)区分开。可使用多种方法生成子类生成子类实际上就是完成语句A is a B that...的过程。 添加功能 派生类可在基类的基础上添加功能。例如猴子是一种可以在树间荡秋千的动物。除了具有动物的所有行为以外Monkey类还有swingFromTrees0方法这个行为只存在于Monkey 类中。 替换功能 派生类可完全替换或重写父类的行为。例如大多数动物都步行因此 Animal类可能拥有模拟步行的 move 行为。但袋鼠是一种通过跳跃而不是步行移动的动物Animal 基类的其他属性和行为仍然适用Kangaroo 派生类只需要改变 move 行为的运行方式。当然如果对基类的所有功能都进行替换就可能意味着采用继承的方式根本就不正确除非基类是一个抽象基类。抽象基类会强制每个子类实现未在抽象基类中实现的所有方法。 添加属性 除了从基类继承属性以外派生类还可添加新属性。企鹅具有动物的所有属性此外还有 beaksize(鸟喙大小)属性。 替换属性 与重写方法类似C提供了重写属性的方法。然而这么做通常是不合适的因为这会隐藏基类的属性。也就是说基类可为具有特定名称的属性指定一个值而派生类可用同一个名字为另一个属性指定另一个值。有关“隐藏”的内容会在后面讲解。不要把替换属性的概念与子类具有不同属性值的概念混淆。例如所有动物都具有表明它们吃什么的 diet 属性猴子吃香蕉企鹅吃鱼二者都没有替换 diet 属性--只是赋给属性的值不同而已。 2.多态性 多态性(Polymorphism)指遵循一套标准属性和方法的对象可互换使用。类定义就像对象和与之交互的代码之间的契约。根据定义任意一个Monkey 对象必须支持 Monkey 类的属性和行为。 这个概念也可推广到基类、由于所有猴子都是动物因此所有Mokey对象都支转属性和行为。 多态性是面向对象编程的亮点因为多态性真正利用了继承所提供的能力、在模拟动物园时可通过编程遍历动物园的所有动物让每个动物都移动一次。由于所有动物都是Animal类的成员因此它们都知道如何移动。某些动物重写了移动行为但这正是亮点所在一一代码只是告诉每个动物移动而不知道也不关心是哪种动物。所有动物都按自己的方式移动。
4.“有一个”与“是一个”的区别 在现实中区分对象之间的“有一个”与“是一个”关系相当容易。没有人会说橘子有一个水果-----橘子是一种水果。在代码中有时并不会那么明显。 下面举个例子来说明这个问题。 假设我们有一个名为AssociativeArray的类用于高效地将键映射到值。例如保险公司可以使用AssociativeArray类来将会员ID映射到名字。同时我们还想要一个可以允许一个键对应多个值的数据结构。这个需求导致了另一个类MultiAssociativeArray的产生。 MultiAssociativeArray类在功能上与AssociativeArray非常相似除了它允许多值与单一键关联。这引出了一个设计问题MultiAssociativeArray应该是AssociativeArray的派生类Is-a关系还是应该包含一个AssociativeArray对象Has-a关系 Is-a关系继承的代码示例
// 基类 AssociativeArray
class AssociativeArray {
public:void insert(int key, std::string value);std::string get(int key);// ... 其他方法
};// 派生类 MultiAssociativeArray
class MultiAssociativeArray : public AssociativeArray {
public:void insert(int key, std::string value) override;std::string get(int key) override;std::vectorstd::string getAll(int key);// ... 其他方法
}; Has-a关系组合的代码示例
// 基类 AssociativeArray
class AssociativeArray {
public:void insert(int key, std::string value);std::string get(int key);// ... 其他方法
};// 使用组合的 MultiAssociativeArray
class MultiAssociativeArray {
private:AssociativeArray internalArray; // 内部对象
public:void insert(int key, std::string value);std::vectorstd::string getAll(int key);// ... 其他方法
}; 关系类型优点缺点Is-a继承- 能够复用AssociativeArray的所有功能和代码 - 可以用AssociativeArray对象的地方也能使用MultiAssociativeArray对象- get()方法需要重写以返回一个特定的值这可能会引入复杂性 - 如果AssociativeArray类的实现发生变化可能需要更改MultiAssociativeArrayHas-a组合- 更灵活因为可以更容易地修改或扩展MultiAssociativeArray的功能 - 不必担心AssociativeArray的任何功能或方法“渗透”到MultiAssociativeArray- 可能需要编写更多的代码来调用内部AssociativeArray对象的方法 LSP建议派生类对象应该能够替换其基类对象而不改变程序的正确性。在这个案例中由于MultiAssociativeArray的insert()方法与AssociativeArray的行为不同不会删除具有相同键的早期值因此更倾向于Has-a关系。 基于上述分析推荐使用Has-a关系。这样MultiAssociativeArray可以有自己独立的接口和实现同时还可以灵活地应对未来需求的变化。
5.not-a关系 当考虑类之间的关系时应该考虑类之间是否真的存在关系。不要把对面向对象设计的热情全部转换为许多不必要的类/子类关系。 当实际事物之间存在明显关系而代码中没有实际关系时问题就出现了。OO(面向对象)层次结构需要模拟功能关系而不是人为的制造关系。下图显示的关系作为概念集或层次结构是有意义的但是代码中并不能代表有意义的关系。 避免不必要继承的最好方法是首先给出大概得设计。为每个类和派生类写出计划设置的属性和方法。如果发现某个类没有自己特定的属性或方法或者某个类的所有属性和方法都被派生类重写只要这个类不是前面提到的抽象基类就应该重新考虑设计。
6.层次结构 正如类A可以是类B的基类一样B也可以是C的基类。面向对象层次结构可模拟类似的多层关系。一个具有多种动物的动物园模拟程序可能会将每种动物作为 Animal 类的子类如下图所示。 当编写每个派生类的代码时许多代码可能是相似的。此时应该考虑让它们拥有共同的父类。Lionhe Panther的移动方式和食物相同说明可使用BigCat类。还可进一步将 Animal类细分以包括WaterAnimal和Marsupial下图展示了利用这种共性的更趋层次化的设计。 生物学家看到这个层次结构可能会失望----海豚Dolphin和企鹅(Ppngui)并不属于同一科。然而这强调了一个要点----在代码中需要平衡现实关系和共享功能关系。即使现实中两种事物紧密联系在代码中也可能没有任何关系因此它们没有共享功能。可简单地把动物分为哺乳动物和鱼类但是这会使基类没有任何共同因素。 另一个要点是可用其他方法创建这个层次结构。前面的设计基本上根据动物的移动方式创建的。如果根据动物吃的食物或身高创建层次结构可能会大不相同。总之关键在于如果使用类需求决定对层次结构的设计。 优秀的面向对象层次结构能做到以下几点: 1使类之间存在有意义的功能联系。 2将共同的功能放入基类从而支持代码重用。 3避免子类过多地重写父类的功能除非父类是一个抽象类。
7.多重继承 到目前为止所有示例都只有单一的继承链。换句话说给定的类最多只有一个直接的父类。事物并非一直如此在多重继承中一个类可以有多个基类。 下图给出了一种多重继承设计。在此任然有一个基类Animal, 根据大小对这个类进行细分。此外根据食物划分了一个独立的层次类别根据移动方式又划分了一个层次类别所有类型的动物都是这三个类的子类。 考虑用户界面环境假定用户可单击某张图片。这个对象好像既是按钮又是图片因此其实现同时继承了 Image 类和 Button 类如下图所示。 某些情况下多重继承可能很有用但必须记住它也有很多缺点。许多程序员不喜欢多重继承,C明确支持这种关系而Java语言根本不予支持除非通过多个接口来继承(抽象基类)。多重继承的批评者一般有以下几个原因。 首先用图形表示多重维承十分复杂如上图所示当存在多重维承和交叉线时即使简单的类图也会变得非常复系。类层次结构旨在让程序员更方便地理解代码之间的关系。而多种继承中类可有多个彼此没有关系的父类。将这么多的类加入对象的代码中能跟踪发生了什么吗 其次多重维承会破坏清晰的层次结构。在动物示例中使用多重继承方法意味着Animal基类作用降低因为描述动物的代码现在分成三个独立的层次。尽管上图的设计显示了三个清晰的层次但不难想象它们会变得如何凌乱。例如如果发现所有的Jumper不仅以同样的方式移动还吃同样的食物该怎么办?由于层次是独立的因此无法在不添加其他派生类的情况下加入移动和食物的概念。 最后多重维承的实现很复杂、如果两个基类以不同方式实现了相同的行为该怎么办?两个基类本身是同一个基类的子类可以这样吗?这种可能让实现变得复杂因为在代码中建立这样复杂的关系会给作者和读者带来挑战。 其他语言取消多重继承的原因是通常可以避免使用多重继承。在设计某个项目时重新考虑层次结构通常可避免引入多重继承。
8.混入类 混入(mixin)类代表类之间的另一种关系。在 C中混入类的语法类似于多重继承但语义完全不同混入类回答“这个类还可以做什么”这个问题答案经常以“-able”结尾。使用混入类可向类中添加功能而不需要保证是完全的“是一个”关系。可将它当作一种分享(share-with)关系。 回到动物园示例假定想引入某些动物可以“被抚摸”这一概念。也就是说动物园的游客可以抚模一些动物大概率不会被咬伤或伤害。所有可以被抚摸的动物支持“被抚摸”行为。由于可以被抚模的动物没有其他的共性且不想破坏已经设计好的层次结构因此Pettable 就是很好的混入类。 混入类经常在用户界面中使用。可以说 Image 能够点击(Clickable)而不需要说 PictureButton 类既是 lmage 又是 Button。桌面上的文件夹图标可以是一张可拖动(Draggable)、可点击(Clickable)的图片(lmage)。软件开发人员总是喜欢弄一大堆有趣的形容词。 混入类和基类之间的区别更多地取决于对类的看法而不是代码的区别。因为范围有限混入类通常比多重层次结构容易理解。Pettable混入类只是在已有类中添加了一个行为Clickable混入类或许仅添加了“按下鼠标”和“释放鼠标”行为。此外混入类很少会有庞大的层次结构因此不会出现功能的交叉混乱。 推荐阅读 组合优于继承:什么情况下可以使用继承?