一. UML类图介绍
1. UML类的表示
2. 类间的关系
① 泛化:类的继承关系
用空心三角和实线,空心三角指向父类。
如 狗和猫都是一种动物。
② 实现:类与接口的关系,表示类是接口所有特征和行为的实现。
用空心三角和虚线,空心三角指向接口。
如 狗和猫实现了动物的“eat()”和”run()”的接口。
③ 单向关联:关联是一种拥有关系(has),一个类可以调用另一个类的公有的属性和方法。在类中以成员变量的方式表示。比如老师有自己的学生,知道学生的姓名学 成绩;学生有自己的老师,也知道老师的姓名和所教的科目。关联分为单向关联、双向关联和自关联。
带箭头的直线表示。
如 老师(Teacher类)有(has)自己的地址(Address类)
④ 双向关联:指双方都知道对方的存在,都可以调用对方的公共属性和方法。
用一条直线连接两个类,也可以用双向箭头。
如 老师(Teacher类)有自己的学生(Student类),学生也有自己的老师。
⑤ 自关联:自己引用自己
带箭头的直线,指向自己。
如 二叉树结构体(刷题时经常碰见吧)。
⑥ 聚合: 整体与部分的关系,部分离开整体后可以单独存在。常用于类的成员变量。
带空心菱形和箭头的直线(或没有箭头),菱形挨着整体,箭头指向部分。
例 汽车和轮胎,轮胎是汽车的一部分,但轮胎也可以单独存在。
⑦ 组合: 整体与部分的关系,部分离开整体后不可以单独存在,代表整体的对象负责代表部分的对象的生命周期。常用于类的成员变量。
带实心菱形和箭头的直线(或没有箭头),菱形挨着整体,箭头指向部分。
如 公司和部门,部门是公司的一部分,但部门不可以单独存在。
二. 设计模型
1.简单工厂模式
结构:
工厂(Factory):根据客户提供的具体产品类的参数,创建具体产品实例;
抽象产品(AbstractProduct):具体产品类的基类,包含创建产品的公共方法;
具体产品(ConcreteProduct):抽象产品的派生类,包含具体产品特有的实现方法,是简单工厂模式的创建目标。
简单工厂模式UML类图如下:
举例:
Jungle想要进行户外运动,它可以选择打篮球、踢足球或者玩排球。它需要凭票去体育保管室拿,票上写着一个具体球类运动的名字,比如“篮球”。体育保管室负责人根据票上的字提供相应的体育用品。然后Jungle就可以愉快地玩耍了。
解析:
首先,体育保管室是工厂,篮球、足球和排球是具体的产品,而抽象产品可以定义为“运动球类产品SportProduct”.Jungle作为客户只需要提供具体产品名字,工厂就可“生产”出对应产品。
继承使用错解
继承使用正解
本题解答
总结:
简单工厂模式的优点在于:
工厂类提供创建具体产品的方法,并包含一定判断逻辑,客户不必参与产品的创建过程;
客户只需要知道对应产品的参数即可,参数一般简单好记,如数字、字符或者字符串等。
当然,简单工厂模式存在明显的不足(想想我们之前介绍的面向对象设计原则)。假设有一天Jungle想玩棒球了,该怎么办呢定会说,这还不容易吗抽象产品类派生出一个Baseball类,并在工厂类的getSportProduct方法中增加“productName == “Baseball”的条件分支即可。的确如此,但是这明显违背了开闭原则(对扩展开放,对修改关闭),即在扩展功能时修改了既有的代码。另一方面,简单工厂模式所有的判断逻辑都在工厂类中实现,一旦工厂类设计故障,则整个系统都受之影响!
2.工厂模式
简单工厂模式中,每新增一个具体产品,就需要修改工厂类内部的判断逻辑。为了不修改工厂类,遵循开闭原则,工厂方法模式中不再使用工厂类统一创建所有的具体产品,而是针对不同的产品设计了不同的工厂,每一个工厂只生产特定的产品。
定义一个用于创建对象的接口,但是让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
结构:
抽象工厂(AbstractFactory):所有生产具体产品的工厂类的基类,提供工厂类的公共方法
具体工厂(ConcreteFactory):生产具体的产品
抽象产品(AbstractProduct):所有产品的基类,提供产品类的公共方法
具体产品(ConcreteProduct):具体的产品类
举例:
Jungle想要进行户外运动,它可以选择打篮球、踢足球或者玩排球。和上一次的体育保管室不同,这次分别由篮球保管室、足球保管室和排球保管室,Jungle只需直接去相应的保管室就可以拿到对应的球!然后Jungle就可以愉快地玩耍了。
总结:
如果Jungle想玩棒球(Baseball),只需要增加一个棒球工厂(BaseballFacory),然后在客户端代码中修改具体工厂类的类名,而原有的类的代码无需修改。由此可看到,相较简单工厂模式,工厂方法模式更加符合开闭原则。工厂方法是使用频率最高的设计模式之一,是很多开源框架和API类库的核心模式。
优点:
工厂方法用于创建客户所需产品,同时向客户隐藏某个具体产品类将被实例化的细节,用户只需关心所需产品对应的工厂;
工厂自主决定创建何种产品,并且创建过程封装在具体工厂对象内部,多态性设计是工厂方法模式的关键;
新加入产品时,无需修改原有代码,增强了系统的可扩展性,符合开闭原则。
缺点:
添加新产品时需要同时添加新的产品工厂,系统中类的数量成对增加,增加了系统的复杂度,更多的类需要编译和运行,增加了系统的额外开销;
工厂和产品都引入了抽象层,客户端代码中均使用的抽象层(AbstractFactory和AbstractSportProduct ),增加了系统的抽象层次和理解难度。
适用环境:
客户端不需要知道它所需要创建的对象的类;
抽象工厂类通过其子类来指定创建哪个对象(运用多态性设计和里氏代换原则)
3.抽象工厂模式
回顾之前的设计模式,简单工厂模式所有逻辑都封装在工厂类中,工厂根据客户提供的产品名字创建对应产品的对象实例;工厂方法模式将产品的创建过程放到了具体工厂类中,每一个工厂可以创建一个具体产品,由此可能会创建许多工厂类。很多时候,一个工厂不只是生产一种产品,而是生产一类产品,比如一个体育用品工厂,可以生产篮球、足球、排球等多种产品。此时我们可以把这些相关的产品归纳为一个“产品族”,由同一个工厂来生产,即抽象工厂模式。
抽象工厂模式:
提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。简言之,一个工厂可以提供创建多种相关产品的接口,而无需像工厂方法一样,为每一个产品都提供一个具体工厂。
结构:
抽象工厂(AbstractFactory) :所有生产具体产品的工厂类的基类,提供工厂类的公共方法
具体工厂(ConcreteFactory) :生产具体的产品
抽象产品(AbstractProduct) :所有产品的基类,提供产品类的公共方法
具体产品(ConcreteProduct):具体的产品类
举例:
Jungle想要进行户外运动,它可以选择打篮球和踢足球。但这次Jungle不想弄脏原本穿的T恤,所以Jungle还需要穿球衣,打篮球就穿篮球衣,踢足球就穿足球衣。篮球保管室可以提供篮球和篮球衣,足球保管室可以提供足球和足球衣。Jungle只要根据心情去某个保管室,就可以换上球衣、拿上球,然后就可以愉快地玩耍了。
总结:
抽象工厂模式中,如果需要新增加一个系列的产品,比如足球系列,只需增加一族新的具体产品类(抽象和具体)并提供一个对应的工厂类即可。但是,如果要在已有的产品族里增加另一个产品,比如Jungle打篮球,除了需要篮球和篮球衣外,Jungle还想换双篮球鞋,这时候该怎么办呢是要去修改BasketballFactory呢,Jungle总结了抽象工厂模式的特点:
优点:工厂方法用于创建客户所需产品,同时向客户隐藏某个具体产品类将被实例化的细节,用户只需关心所需产品对应的工厂;
新加入产品系列时,无需修改原有系统,增强了系统的可扩展性,符合开闭原则。
缺点:在已有产品系列中添加新产品时需要修改抽象层代码,对原有系统改动较大,违背开闭原则
适用环境:
一系列/一族产品需要被同时使用时,适合使用抽象工厂模式;
产品结构稳定,设计完成之后不会向系统中新增或剔除某个产品。
4.建造者模式
建造者模式将客户端与包含多个部件的复杂对象的创建过程分离,客户端不必知道复杂对象的内部组成方式与装配方式,只需知道所需建造者的类型即可。
建造者模式:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
“同样的构建过程可以创建不同的表示”句话是什么意思呢一下,建造一栋房子,建造过程无非都是打地基、筑墙、安装门窗等过程,但不同的客户可能希望不同的风格或者过程,最终建造出来的房子当然就呈现不同的风格啦!
结构:
抽象建造者(AbstractBuilder):创建一个Product对象的各个部件指定的抽象接口;
具体建造者(ConcreteBuilder):实现AbstractBuilder的接口,实现各个部件的具体构造方法和装配方法,并返回创建结果;
产品(Product):具体的产品对象;
指挥者(Director): 构建一个使用Builder接口的对象,安排复杂对象的构建过程,客户端一般只需要与Director交互,指定建造者类型,然后通过构造函数或者setter方法将具体建造者对象传入Director。它主要作用是:隔离客户与对象的生产过程,并负责控制产品对象的生产过程。
举例:
Jungle想要建造一栋简易的房子(地板、墙和天花板),两个工程师带着各自的方案找上门来,直接给Jungle看方案和效果图。犹豫再三,Jungle最终选定了一位工程师……交房之日,Jungle满意的看着建好的房子,开始思考:这房子究竟是怎么建成的呢板、墙和天花板是怎么建造的呢师笑着说:“It’s none of your business”
总结:
从客户端代码可以看到,客户端只需指定具体建造者,并作为参数传递给指挥者,通过指挥者即可得到结果。客户端无需关心House的建造方法和具体流程。如果要更换建造风格,只需更换具体建造者即可,不同建造者之间并无任何关联,方便替换。从代码优化角度来看,其实可以不需要指挥者Director的角色,而直接把construct方法放入具体建造者当中。
优点:
建造者模式中,客户端不需要知道产品内部组成细节,将产品本身和产品的创建过程分离,使同样的创建过程可以创建不同的产品对象;
不同建造者相互独立,并无任何挂链,方便替换。
缺点:
建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大
适用环境:
需要生成的产品对象有复杂的内部结构(通常包含多个成员变量);
产品对象内部属性有一定的生成顺序;
同一个创建流程适用于多种不同的产品。
5.原型模式
原型模式通过复制一个已有对象来获取更多相同或者相似的对象。
原型模式:
使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象。
原型模式的工作原理是将一个原型对象传给要发动创建的对象(即客户端对象),这个要发动创建的对象通过请求原型对象复制自己来实现创建过程。从工厂方法角度而言,创建新对象的工厂就是原型类自己。软件系统中有些对象的创建过程比较复杂,且有时需要频繁创建,原型模式通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象,这就是原型模式的意图所在。
结构:
抽象原型类(AbstractPrototype):声明克隆clone自身的接口;
具体原型类(ConcretePrototype):实现clone接口;
客户端(Client):客户端中声明一个抽象原型类,根据客户需求clone具体原型类对象实例;
原型模式可以说是“复制”,即克隆,但这个复制不是代码的复制,而是将对象包含的所有属性都创建一份拷贝。但不同的复制操作,可能会产生两种不同的拷贝,即浅拷贝和深拷贝:
在浅拷贝中,如果原型对象的成员变量是值类型(如int、double、char等基本数据类型),将复制一份给拷贝对象;如果原型对象的成员变量是引用/指针,则将引用/指针指向的地址拷贝一份给拷贝对象,即原型对象和拷贝对象中的成员变量指向同一个地址;
在深拷贝中,无论原型对象中的成员变量是值类型还是指针/引用类型,都将复制一份给拷贝对象。注意,深拷贝中,指针/引用对象也会被拷贝一份给拷贝对象。
举例:
明天就是周一了,Jungle又陷入了苦恼中,因为作业还没完成。于是Jungle想拿着哥哥Single的作业来抄一份。虽然抄袭作业并不好,但是边抄边学借鉴一下也是可以的。于是乎,Jungle开始动起手来……(作业包含几个部分:姓名(name)、学 (idNum)、模型(workModel))
显然,这不是我们想要的结果。接下来我们使用clone方法。
总结:
优点:
当创建新的对象实例较为复杂时,原型模式可以简化创建过程,提高创建对象的效率;
可扩展:模式中提供了抽象原型类,具体原型类可适当扩展;
创建结构简单:创建工厂即为原型对象本身
缺点:
深克隆代码较为复杂;
每一个类都得配备一个clone方法,且该方法位于类的内部,修改时违背开闭原则;
适用环境:
当创建新的对象实例较为复杂时,原型模式可以简化创建过程;
结合优点第3条,需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少几个的组合状态,通过复制原型对象得到新实例,比通过使用构造函数创建一个新实例会更加方便。
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!