1、继承关系(泛化关系)
【说明】:继承关系是子类(派生类)继承父类(基类),或者子接口继承父接口的关系。即子类对象“is a” 父类对象,比如鸟是动物。
【UML图】:
图解:Animal为父类,Bird类、Fish类、Dog类分别继承了Animal类,它们不仅继承了Animal的公用方法Breath(),同时也根据自己的实际需要拓展了相关方法(Fly()方法、Swim()方法、Run()方法)。
【对应代码】:
<span style="font-size:18px;">//Animal类(父类): class Animal { public void Breath() { } } //Bird类、Fish类和Dog类(子类): class Bird : Animal { public void Fly() { } } class Fish : Animal { public void Swim() { } } class Dog : Animal { public void Run() { } }</span>
【画龙点睛】:
★说明子类可以继承所有父类非private的属性和方法,并且可以根据实际情况进行拓展(增加属性或方法),如上例中的Fly()方法就是鸟类对动物类的拓展;
★类只可以有单继承(一个子类只可以继承一个父类,一个父类可以由多个子类继承),接口可以有多继承(一个子接口可以继承多个父接口)。
2、实现关系
【说明】:实现关系就是类实现接口的关系。以下引用《大话设计模式》中的例子来说明。
【UML图】:
图解:机器猫、孙悟空、猪八戒都可以变出东西来,但又因为其他动物并不像他们三个一样具有这种超能力,因此并不能直接把这个方法加到父类Animal中去,而需要把“变出东西”这种方法抽象出一个名为“变东西”的接口,到时候让具有这种超能力的动物们(比如机器猫、孙悟空、猪八戒)直接去实现这个接口。
【对应代码】:
<span style="font-size: 18px;"> //Animal类、Cat类、Monkey类、Pig类、叮当猫类的代码省略 interface IChange { string ChangeThing(string thing); } class SunWukong:Monkey,IChange//孙悟空继承与猴类,并实现IChange接口 { public string ChangeThing(string thing) //此处为实现接口的方法,注意不能加override修饰符 { return "我老孙有七十二般变化,可以变出" + thing; } } class ZhuBajie : Pig,IChange//猪八戒继承与猪类,并实现IChange接口 { public string ChangeThing(string thing) //此处为实现接口的方法,注意不能加override修饰符 { return "我老猪有三十六般变化,可以变出" + thing; } }</span>
【画龙点睛】:
★类可以实现多重接口,即一个类实现多个接口;
★因为接口中的方法用来定义对象之间通信的契约,如果指定接口中的方法为私有或保护就没有意义了,所以它们默认为公有方法,即接口不能用new、public、protected、internal、private等修饰符。
★为了区分类和接口,接口名称一般都以“I”作为首字母(当然不这样声明也可以)。
3、依赖关系
【说明】:如果一个类A需要用到另一个类B,或者说一个类A负责构造类B的实例时,则称类A依赖类B,即A类对象“use a” B类对象。比如人写字需要用到笔,那这个关系就是人类依赖笔类;又比如机器造零件,那机器类就依赖零件类(本例参考简单工厂模式)。
【UML图】:
人写字的UML图:
机器造零件的UML图:
【对应代码】:
例1:人写字的代码(此处体现依赖的方式为:Pen是People中一个方法中的变量)
<span style="font-size:18px;"> class People { public void Write() { Pen pen=new Pen(); pen.Write(); } } class Pen { public void Write(){} public void Draw(){} }</span>
例2:机器造零件的代码(此处体现依赖的方式为:Part是Machine中一个方法中返回值)
class Part //零件类 { private string type; } class PartA : Part //零件A类(继承零件类) { } class PartB : Part //零件B类(继承零件类) { } class Machine //机器类(职责:根据要求选择性地生产零件A或零件B) { public static Part CreatePart(string type) { Part part = null; switch (type) { case "A": part = new PartA(); break; case "B": part = new PartB(); break; } return part; } }
【画龙点睛】:
★假如A依赖B,依赖关系代码表现形式:
1、B是A中方法的变量;
2、B是A中方法的返回值;
3、B是A中方法的参数。
4、关联关系
【说明】:关联关系简单来说就是一个类A“知道”另一个类B的时候,则类A关联类B,UML图表现为实线箭头由类A指向类B。比如许多动物都可以预知地震、海啸等自然灾害,狗在自然灾害前会狂吠不止、老鼠在震前会出窝乱窜……
【UML图】:
【对应代码】:
class Disaster { private string name; public Disaster(string name) { this.name = name; } } class Mouse { private Disaster disaster; public Disaster TheDisaster { get{return disaster;} set{this.disaster = value;} } public void Run(){} } class Dog { private Disaster disaster; public Disaster TheDisaster { get{return disaster;} set{this.disaster = value;} } public void Bark(){} } //客户端代码 static void Main(string[] args) { Disaster disaster=new Disaster("Typhoon"); Mouse mouse=new Mouse(); Dog dog=new Dog(); mouse.TheDisaster=disaster; dog.TheDisaster=disaster; if(mouse.TheDisaster!=null) mouse.Run(); if(dog.TheDisaster!=null) dog.Bark(); }
【画龙点睛】:
★关联关系有单向关联、双向关联、自身关联、多维关联等等。代码体现为,哪个类关联另一个类就直接在类中调用那个类即可;
★关联和依赖的区别:
(1)从类的属性是否增加的角度看:
发生依赖关系的两个类都不会增加属性。其中的一个类作为另一个类的方法的参数或者返回值,或者是某个方法的变量而已。
发生关联关系的两个类,其中的一个类成为另一个类的属性,而属性是一种更为紧密的耦合,更为长久的持有关系。
(2)从关系的生命周期来看:
依赖关系是仅当类的方法被调用时而产生,伴随着方法的结束而结束了。
关联关系是当类实例化的时候即产生,当类销毁的时候,关系结束。相比依赖讲,关联关系的生存期更长。
5、聚合关系:
【说明】:如果A对象可以包含B对象,但是B对象不一定要成为A对象的组成部分,那么A对象与B对象之间的关系为聚合关系,即A对象“contain a” B对象,比如一辆自行车包含两个车轮,但这两个车轮不一定要安装在这两自行车上。
【UML图】:
【对应代码】:
class Wheel{ } class Bicycle { private Wheel BicycleWheel; public Wheel BicycleWheel { set{BicycleWheel=value;} get{return BicycleWheel;} } }
【画龙点睛】:
★普通关联关系的两个类处于同一层次上,而聚合关系的两个类处于不同的层次,一个是整体,一个是部分,是一种弱的“拥有”关系。
★A对象可以包含B对象,但B对象不是A对象的组成部分。具体表现为,如果A由B聚合成,表现为A包含有B的全局对象,但是B对象可以不在A创建的时刻创建。
6、组合关系
【说明】:如果A类对象包含B类对象,而且B类对象一定要是A类对象的组成部分,那么A类对象与B类对象之间的关系为聚合关系,即A类对象“contain a” B类对象,比如一个健康的人有两条胳膊和两条腿,而且这两条胳膊和两条腿一定只属于这个人。即A对象“has a” B对象。
【UML图】:
【对应代码】:
<span style="font-size:18px;"> class Arm { } class Leg { } class People { private Arm myArm; private Leg myLeg; public People() { myArm=new Arm(); myLeg=new Leg(); } }</span>
【画龙点睛】:
★组合是比聚合关系强的关系。它要求普通的聚合关系中代表整体的对象负责代表部分的对象的生命周期。组合关系是一种强的“拥有”关系,体现了严格的部分和整体的关系,部分和整体的生命周期一致。如果A由B组成,表现为A包含有B的全局对象,并且B对象在A创建的时刻创建。
★聚合和组合的区别:请参见浅谈UML中的聚合与组合
在这几种关系中,它们的耦合强度由强到弱为:泛化=实现>组合>聚合>关联>依赖。设计模式就如同搭积木,这几种关系就如同积木,设计模式的作用无非就是为了将这几种关系组合,最大化降低程序耦合度,使代码结构达到最优,便于应对以后的需求变更。
需要注意的是,其中的关联关系、聚合关系、组合关系的代码体现有一个相似点,就是都是一有个类作为另一个类的成员。这一点需要用心理解清楚,不要混淆。
大家如果有什么好的说明例子或者建议请您提出宝贵的意见,谢谢。
【注:本文代码均用C#来实现,与其他语言可能会有差异(如java中子类继承父类使用“class 子类 extends 父类”的结构;java中类实现接口使用“class 类 implements 接口”的结构等】