最近开始学习设计模式,要想快速理解并掌握设计模式画UML图不失为一种行之有效的方法。以下是我所记的笔记。我画UML图所用的软件是一个在线软件点击打开链接。当然他也有离线版本,搜搜就能下到
<font color=#0099ff size=5 face="黑体">黑体</font>
*************************************************************************************************************************
一.类: 分为三层 第一层为类的名称,如果是抽象类,则用斜体表示。
第二层是类的特性,通常就是字段和属性。
第三层是类的操作,通常是方法或行为,前面的符号,+表示public,-表示private,#表示protected.如图
这里是我自己角色类的UML,这里攻击是根据武器类型来计算实现的,所以没必要弄成虚方法。
当然这里我也用斜体表示虚方法。
****************************************************************************************************
二.接口:与类图的区别主要是顶端的<<interface>>显示。第一行是接口名称,第二行是接口方法。
这里为什么要将资源加载设置成为接口呢,因为资源位置有很多位置,有些是在
Resouce上直接加载,有的是在AssetBundle上加载,有些是在其他位置加载。有些在本地,有些在网络上。
如果把他们全写在一起,不符合设计模式中的开闭原则。也就是对修改关闭,对扩展开放。
我们用对应的加载方法类实现这个接口,本地加载写一个类,网络加载写一个类,如果有新需求要在AssetBundle包上加载
那我们只要新建一个实现该接口的类AssetBundleFactory就行。使用的时候:
IAssetFactory asset=new AssetBundleFactory()就可以了,改成其他实现只要把后面的new实现改一下就ok,方便维护
也不用动以前已经跑过没有bug的代码,非常方便。
***********************************************************************************************************
三.继承:继承关系用空心三角形+实现来表示。
这里战士状态的切换使用的是有限状态机,有限状态机是状态模式的一种,使用非常普遍。我在后续的博客中也会重点
介绍,因为他真的非常好用,不仅用在角色状态切换,还可以用在 UI框架上。
也就是说如果一个对象有明显的状态特征,而状态要经常切换,有限状态机是非常好用的设计模式。
***************************************************************************************************************
四.实现接口:用空心三角形+虚线表示。
资源总共分两类,一类是预设GameObject,一类是资源Object,根据类别提取出来两个私有方法。
******************************************************************************************************
上面说的是类与类之间上下级的关系,实现接口和类的继承。
下面说的是类与类之间平级的关系。有四种:关联(Association),聚合(Aggregation),组合(Composition),
依赖(Dependence)
上面说的还漏了个泛化,泛化关系就是通常所说的继承。泛化应该是从下向上的抽象的过程,而继承应该是有上
到下的过程。方向不同,意义相同。苹果继承水果,水果泛化苹果。
******************************************************************************************************
五.关联(Association)
体现的两个类,比如我和我的朋友,这种关系比依赖更强,不存在依赖关系的偶然性,关系也不是临时性的,一般是
长期性的,并且双方的关系一般是平等的,关联可以是单向的,双向的。
企鹅和气候两个类,企鹅需要知道气候的变化,需要了解气候变化。当一个类知道另一个类时,可以用关联。
表现在代码层面,为被关联类B以类属性的形式出现在关联A中,也可以是关联类A引用了一个类型为被关联类B的全局变量。
设计模式中的适配器模式就是类与类之间的关联模式。又扯到设计模式上去了,后面我也会整理出一篇博文。
当然我们可以根据一个适配器模式来画一个UML图。
比如说我有敌人和战士两个角色。但是我想让这个敌人类当战士用,他有战士的一些功能,但是它的预设,使用的武器,它的攻击策略
,技能都是敌人的,这个怎么实现呢。
关联的关系用实线箭头表示:
我们这里的泛化出来的新的角色我们称之为俘兵,它继承自战士,但是我们有一个私有的野蛮人类属性成员。我们在初始化的
时候,可以访问野蛮人类。让俘兵类使用野蛮人的预设,攻击策略,野蛮人的基础属性等等。
这样我们就将产生一个新的战士。虽然使用的是野蛮人信息,但他是一个战士。
适配器模式可以让我们使用在不更改标准接口使用的情况下来使用别的类.
伏兵类就是适配器把一个敌人适配成一个战士
而俘兵和野蛮人之间就是关联关系。
*****************************************************************************************************************
六.聚合(Association)
大雁和雁群两个类,每个大雁都是属于一个雁群,一个雁群可以有多只大雁。它们之间满足聚合关系。
聚合关系是整体和个体的关系。个体是整体类的一部分,即成员对象是整体对象的一部分,但是成员对象可以脱离整体对象
独立存在。
聚合关系也是通过实例变量实现的。例如汽车,发动机,轮胎,一个汽车对象由一个发动机对象,四个轮胎对象组成。
关联关系和聚合关系在语法上是没办法区分的,从语义上才能更好地区分两者的区别。
当我们创建一个工厂管理类来管理多个对象工厂的时候,这个工厂管理类与多个对象就是聚合关系。
聚合关系用空心的菱形+实线箭头来表示。
这里的工厂管理类和诸多工厂就是聚合关系。
看到这里,你可能会有疑问,这可以说成是聚合关系,也可以说成是组合关系。如果真要严格意义上的分隔开聚合和组合的关系
大话设计模式上的雁子和雁群就是严格意义上的聚合关系。雁子离开了雁群也能生存,雁群走丢几只雁子还是雁群。但是我们在用其他的对象来比较:比如说电脑包括键盘,显示器,主机,鼠标。主机可以让其他的键盘,显示器,鼠标组成电脑,而且就算
主机没有其他硬件通上电以后也能正常运行。但是主机内部的处理器,风扇,硬盘,主板,少了谁都不行。它们和主机之间的关系就是组合关系。
更加简单的说:聚合关系:整体和部分可以分开,互不影响。
组合关系:也是整体和部分的关系,但是整体和部分不可以分开。
而我这里所举得例子,如果我把个别创建工厂的类(比如说武器工厂)和管理这些工厂的类分开,也不会影响其他,只不过调用起来比较麻烦而已,就如同雁子和雁群一样,只是为了方便创建把他们统一管理起来
,所以严格意义上这不是组合,就是聚合关系。
那么问题有来了什么是工厂模式:我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建
的对象。
意图:定义一个创建对象的接口,让其子类自己选择实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
主要解决:主要解决接口选择的问题。
何时使用:我们明确的计划不同条件下创建不同实例时。
如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。
关键代码:创建过程在其子类执行。
菜鸟教程上的介绍非常清晰。
之前我所提到的资源加载就是一个典型的工厂模式,如图:
本地加载,Resource加载,网络加载三种实现接口的方法。
在使用的时候直接IAssetFactory mAssetFactory = new ResourcesAssetFactory()当要切换到网络加载的时候直接
IAssetFactory mAssetFactory = new RemoteAssetFactory();改一下new后面要实例的类就能轻松完成
资源加载的路径选择。
**********************************************************************************************************************
七.组合(composition)
组合关系也是关联关系的一种,是比聚合关系更强的关系。组合关系是不能共享的。如人有四肢,头等。
表示类之间整体和部分的关系,组合关系中部分和整体具有统一的生存期。一旦整体对象不存在,部分对象也将不存在。
部分对象与整体对象之间具有共生死的关系。
组合的关系用实心的菱形+实现箭头来表示,合成关系的连线两端还有一个数字1和数字2,这被称之为基数,表示一段的
类可以有几个实例,关联关系和聚合关系也是可以有基数的。
如图:
****************************************************************************************************************************
八.依赖(Dependence)
动物的几大特征,比如新陈代谢,能繁殖。而动物要有生命,需要氧气,水以及食物等。也就是说,动物依赖于氧气和水。
他们之间是依赖关系(Dependency)。
对于两个相对独立的对象,当一个对象负责构造另一个对象的实例,或者依赖另一个对象的服务时,这几个对象之间主要
体现为依赖关系。与关联关系不同的是,依赖关系是以参数变量的形式传入到依赖类中的,依赖是单向的,要避免双向
依赖。一般来说,不应该存在双向依赖。
依赖是一种弱关联,只要一个类用到另一个类,但是和另一个类的关系不是太明显的时候(可以说是"uses"了那个类),就可以
把这种关系看成是依赖。
表现:依赖关系表现在局部变量,方法的参数,以及对静态方法的调用。
依赖关系使用虚线箭头来表示。
*******************************************************************************************************************
到这里我们理清了类之间的关系,以及如何在 UML图中表示。
对于继承/泛化的关系,他们体现的是一种类与类,或者类与接口间的派生关系。我是你爸爸,没我就没你,很好理解。
但是聚合关系,组合关系,依赖关系,关联关系这四大关系体现的是类与类,或者类与接口间的引用,横向关系,
是比较难区分的。但还是有些细微的区别:
1.聚合与组合:
①聚合与组合都是一种结合关系,只是额外具有整体-部分的意涵。
②部件的生命周期不同。
聚合关系中,整件不会拥有部件的声明周期,部件删除时,整件还可以正常运作不会被删除。
组合关系中,整件拥有部件的生命周期,部件删除时,整件就无法运作也会删除。所有整件删除时,部件也会跟着删除。
2.关联和依赖
(1)关联关系中,体现的是两个类,或者类与接口之间语义级别的一种强依赖关系,比如我和我的朋友;这种关系比依赖更强,
不存在依赖关系的偶然性,关系也不是临时的,一般是长期的,而且双方的关系一般是平等的。
(2)依赖关系中,可以简单的理解,就是一个类A使用到了另一个类B,而这种使用关系是具有偶然性的,临时性的,非常弱的,
但是B类的变化会影响到A.
********************************************************************************************************
总的来说有很多事物间的关系要想准确定位是很难的,这几种关系都是语义级别的,所以从代码层面并不能
完全区分各个关系,但总的来说,这6种关系的联系紧密度排行为:
继承/泛化=实现>组合>聚合>关联>依赖。它们在UML中箭头表示依次为:
参考了很多的博客和文章,算是一个笔记和半原创把。其实UML图就是理清类与类之间的关系。搞明白类与类之间
基本的六大关系,画UML图也就容易很多。