这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。
定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
ConcreteFactory代表产品族,AbstractProduct代表产品等级,Product就是具体产品(指定的产品族和指定的产品等级)
也可以把产品族看成厂家,产品等级看成商品,具体产品就是不同厂家生产的各种商品,厂家数和商品种类的数目都是固定的,二者数量相乘就是生产出来的具体产品数量
如下图,3*3=9种具体产品
或者把产品族想象成软件版本,不同版本的软件具有的功能数量都一样,但是实现该功能的方法会变。当我们想要改变某个功能的实现方法时,就需要升级版本,而非再去改动当前版本的功能实现方法
抽象工厂与工厂方法的区别:
抽象工厂:一开始就知道产品的种类数,但是后期需要改动产品
工厂方法:只生产具体的一类产品,后期也会改动产品
确保一个类只有一个实例,并提供全局访问点。
该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
例题:打印池:在操作系统中,打印池(Print Spooler)是一个用于管理打印任务的应用程序,通过打印池用户可以删除、中止或者改变打印任务的优先级,在一个系统中只允许运行一个打印池对象,如果重复创建打印池则抛出异常。现使用单例模式来模拟实现打印池的设计,设计类图,实现代码。
使用多个简单的对象一步一步构建成一个复杂的对象。一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
建造者模式的优点:
product和builder部分一般不变,但是当有新的要求,Director部分可以通过变更满足这些要求。这种将业务逻辑封装在导演类(Director)中的方式,对整体而言会有更好的稳定性。
工厂与建造者(生成器)模式的区别:
工厂主要变更的地方是产品的变更,而builder 模式主要变更的地方则是director,就是组装的变更。工厂更倾向基本组件的生产,而builder是这些基本组件的组装。
把Product分为多个部分,由抽象类Builder分别构造(通过Product的set方法),如果有很多种产品,则对应多个ConcreteBuilder,在Builder中声明抽象方法,在每个子类(ConcreteBuilder)中进行不同的重写。最后Director通过构造器或者set方法注入Builder,就可以选择不同的ConcreteBuilder,再使用conduct方法指挥产品的构建,同时返回产品类。
用于创建重复的对象,同时又能保证性能。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
一个原型类,只需要实现Cloneable接口,覆写clone方法,
此处clone方法可以改成任意的名称,因为Cloneable接口是个空接口,
你可以任意定义实现类的方法名,如cloneA或者cloneB,
因为此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法,
在Object类中,clone()是native的。
深浅复制的区别:
将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。分为类结构型模式和对象结构型模式两种,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。
适配器模式(Adapter)包含以下主要角色。
目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
例题:设计一个可以模拟各种动物行为的机器人,在机器人中定义了一系列方法,如机器人叫喊方法cry()、机器人移动方法move()等。如果希望在不修改已有代码的基础上使得机器人能够像狗一样叫bark(),像狗一样跑run(),使用适配器模式设计类图和代码
桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化。
桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化,像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。
一种将对象组合成树状的层次结构的模式,用来表示“整体-部分”的关系,使用户对单个对象和组合对象具有一致的访问性
(1) 透明方式
在该方式中,由于抽象构件声明了所有子类中的全部方法,所以客户端无须区别树叶对象和树枝对象,对客户端来说是透明的。但其缺点是:树叶构件本来没有 Add()、Remove() 及 GetChild() 方法,却要实现它们(空实现或抛异常),这样会带来一些安全性问题。
(2) 安全方式
在该方式中,将管理子构件的方法移到树枝构件中,抽象构件和树叶构件没有对子对象的管理方法,这样就避免了上一种方式的安全性问题,但由于叶子和分支有不同的接口,客户端在调用时要知道树叶对象和树枝对象的存在,所以失去了透明性。
例题:用组合模式实现算术表达式:一个算术表达式包括操作数、操作符和另一个操作数,其中,一个操作符也可以是操作数、操作符(最基本的操作符是加减乘除这4个)和另一个操作数。设计类图,编写代码
在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)
例题:变形金刚在变形之前是一辆汽车,它可以在陆地上移动。当它变成机器人之后除了能够在陆地上移动之外,还可以说话;如果需要,它还可以变成飞机,除了在陆地上移动还可以在天空中飞翔。使用装饰模式设计与实现变形金刚的功能。
隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。它向现有的系统添加一个接口,来隐藏系统的复杂性。
这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。
运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
内部状态指对象共享出来的信息,存储在享元信息内部,并且不回随环境的改变而改变;
外部状态指对象得以依赖的一个标记,随环境的改变而改变,不可共享。
比如,连接池中的连接对象,保存在连接对象中的用户名、密码、连接URL等信息,在创建对象的时候就设置好了,不会随环境的改变而改变,这些为内部状态。而当每个连接要被回收利用时,我们需要将它标记为可用状态,这些为外部状态。
例题:
为其他对象提供一种代理以控制对这个对象的访问。
在代码中,一般代理会被理解为代码增强,实际上就是在原代码逻辑前后增加一些代码逻辑,而使调用者无感知。
根据代理的创建时期,代理模式分为静态代理和动态代理。
例题:在一个论坛中已注册用户和游客的权限不同,已注册的用户拥有发帖、修改自己的注册信息、修改自己的帖子等功能;而游客只能看到别人发的帖子,没有其他权限。使用代理模式来设计该权限管理模块。设计类图,写代码。
这些设计模式特别关注对象之间的通信
给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
例题:某个楼盘构建一片别墅区,区内别墅设计了3种户型:1) 地面为实木地板,墙面为木制墙,大门是木制门,窗户朝北朝南,花园设计为欧式风格;2)地面为大理石,墙面为花岗岩,大门是铁门,窗户朝东朝南,花园设计为中式风格;3)地面为水泥地,墙面为玻璃墙,大门是防盗门,窗户朝东朝西,花园设计为美式风格。该楼盘拥有户型1的别墅20套,户型2的别墅30套,户型3的别墅15套
请用模板模式实现,设计类图,实现代码
使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。但是发出者并不清楚到底最终那个对象会处理该请求,所以,责任链模式可以实现,在隐瞒客户端的情况下,对系统进行动态的调整。
例题:用户可以到银行存钱,有客户要存取人民币,也有客户亚要存取美元、日元、欧元这三种外币,未来也有可能该银行会支持更多币种的存取,或者不支持现有某一种币种的存取,请建立职责链来完成银行存取款功能。
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。
例题:用命令模式实现:用户(User )通过智能电器遥控(Controller),控制各种家电,包括电灯(Light),冰箱(Fridge)和电视机(TV),发送命令(Command),不同的电器接收到后执行命令(excuteCommand),设计类图,编写代码。
提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。
例题:电视机遥控器就是一个迭代器的实例,通过它可以实现对电视机频道集合的遍历操作,本实例我们将模拟电视机遥控器的实现。
定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。
例题:某论坛系统欲增加一个虚拟聊天室,允许论坛会员通过该聊天室进行信息交流,普通会员(CommonMember)可以给其他会员发送文本信息,钻石会员(DiamondMember)既可以给其他会员发送文本信息,还可以发送图片信息。该聊天室可以对不雅字符进行过滤,如“日”等字符;还可以对发送的图片大小进行控制。用中介者模式设计该虚拟聊天室。设计类图(必做),实现代码(选做)
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
备忘录模式使用三个类 Memento、Originator 和 CareTaker。Memento 包含了要被恢复的对象的状态。Originator 创建并在 Memento 对象中存储状态。Caretaker 对象负责从 Memento 中恢复对象的状态。
例题:某系统提供了用户信息操作模块,用户可以修改自己的各项信息(account、password、phoneNo)。为了使操作过程更加人性化,现使用备忘录模式对系统进行改进,使得用户在进行了错误操作之后可以恢复到操作之前的状态。设计类图,实现代码。
指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式。
对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。
例题:某系统提供了一个用于对数组数据进行操作的类,该类封装了对数组的常见操作,如查找数组元素、对数组元素进行排序等。现以排序操作为例,使用策略模式设计该数组操作类,使得客户端可以动态地更换排序算法,可以根据需要选择冒泡排序或选择排序或插入排序,也能够灵活地增加新的排序算法。画出类图,实现代码(可以不把具体的排序代码写进去,只需要在屏幕输出一句话:“使用XX排序法”即可)
将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。
例题:顾客在超市中将选择的商品,如苹果、图书等放在购物车中,然后到收银员处付款。在购物过程中,顾客需要对这些商品进行访问,以便确认这些商品的质量,之后收银员计算价格时也需要访问购物车内顾客所选择的商品。此时,购物车作为一个ObjectStructure(对象结构)用于存储各种类型的商品,而顾客和收银员作为访问这些商品的访问者,他们需要对商品进行检查和计价。不同类型的商品其访问形式也可能不同,如苹果需要过秤之后再计价,而图书不需要。使用访问者模式来设计该购物过程。设计类图(必做),实现代码(选做)