java设计模式期末复习

设计模式概述

定义

  • 一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结
  • 目的:为了可重用代码、让代码更容易被他人理解、提高代码的可靠性
  • 是一种用于对软件系统中不断重现的设计问题的解决方案进行文档化的技术
  • 是一种共享专家设计经验的技术

基本要素

  • 模式名称
  • 问题
  • 解决方案
  • 效果

分类

根据目的(模式是用来做什么的)可分为创建型(Creational),结构型(Structural)和行为型(Behavioral)三类:

  • 创建型模式主要用于创建对象
  • 结构型模式主要用于处理类或对象的组合
  • 行为型模式主要用于描述类或对象如何交互和怎样分配职责

根据范围,即模式主要是处理类之间的关系还是处理对象之间的关系,可分为类模式和对象模式两种:

  • 类模式处理类和子类之间的关系,这些关系通过继承建立,在编译时刻就被确定下来,是一种静态关系
  • 对象模式处理对象间的关系,这些关系在运行时变化,更具动态性

在这里插入图片描述

面向对象设计原则

常用原则汇总

在这里插入图片描述

单一职责原则

一个对象只应包含单一的职责,且该职责完整的封装在一个类中,单一职责用来控制类的粒度大小,是最简单的原则。

开闭原则

软件实体应当对扩展开放,对修改关闭。

即指在尽量不修改源代码的情况下实现功能的扩展。开闭原则实现的关键在于找到系统的可变因素将其抽象化,然后再通过具体类对其进行扩展。

里氏代换原则

所有引用基类的地方都可以透明的使用其子类的对象。

即指在软件中将一个基类对象替换成其子类,程序不会产生错误和异常。但反之则不成立!

根据里氏代换原则,在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型

依赖倒转原则

高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。

即指在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等。

在程序中尽量使用抽象层进行编程,而将具体类写在配置文件中。

接口隔离原则

客户端不应该依赖那些它不需要的接口。

即指当一个接口太大时需要将其分割成更小的接口,用户调用时只提供其需要的方法。

接口仅仅提供客户端需要的行为,客户端不需要的行为则隐藏起来,应当为客户端提供尽可能小的单独的接口,而不要提供大的总接口,每个接口中只包含一个客户端所需的方法

合成复用原则

优先使用对象组合,而不是继承来达到复用的目的。

即指在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分。新对象通过委派调用已有对象的方法达到复用功能的目的,复用时要尽量使用组合/聚合关系(关联关系)少用继承

扩展用继承,复用用关联。

迪米特法则

每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。

即指一个软件实体应当尽可能少地与其他实体发生相互作用

该法则可以降低系统的耦合度

创建型模式

创建型模式对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离,对用户隐藏了类的实例的创建细节

创建型模式描述如何将对象的创建和使用分离,让用户在使用对象时无须关心对象的创建细节,从而降低系统的耦合度,让设计方案更易于修改和扩展

创建型模式一览表

在这里插入图片描述

简单工厂模式

定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。

基本实现流程

  • 具体产品类:将需要创建的各种不同产品对象的相关代码封装到具体产品类中
  • 抽象产品类:将具体产品类公共的代码进行抽象和提取后封装在一个抽象产品类中
  • 工厂类:提供一个工厂类用于创建各种产品,在工厂类中提供一个创建产品的工厂方法,该方法可以根据所传入参数的不同创建不同的具体产品对象
  • 客户端:只需调用工厂类的工厂方法并传入相应的参数即可得到一个产品对象

在这里插入图片描述

例题

在这里插入图片描述
类图:
在这里插入图片描述

优缺点

优点:

  • 实现了对象创建和使用的分离,解决代码重复、创建蔓延的问题
  • 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可
  • 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性

缺点:(不符合开闭原则)

  • 工厂类集中了所有产品的创建逻辑,职责过重,一旦不能正常工作,整个系统都要受到影响
  • 增加系统中类的个数(引入了新的工厂类),增加了系统的复杂度和理解难度
  • 系统扩展困难,一旦添加新产品不得不修改工厂逻辑
  • 由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构,工厂类不能得到很好地扩展

工厂方法模式

定义一个用于创建对象的接口,但是让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。

即指不再提供一个工厂类来统一负责所有产品的创建,而是将具体铲平的创建过程交给专门的工厂子类去完成

满足开闭原则

工厂方法模式结构

在这里插入图片描述

  • Product(抽象产品)
  • ConcreteProduct(具体产品)
  • Factory(抽象工厂)
  • ConcreteFactory(具体工厂)

例题

在这里插入图片描述
类图:
在这里插入图片描述

优缺点

优点:

  • 工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节
  • 能够让工厂自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部
  • 在系统中加入新产品时,完全符合开闭原则
    缺点:
  • 系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,会给系统带来一些额外的开销
  • 增加了系统的抽象性和理解难度

抽象工厂模式

提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。

即指抽象工厂模式中的具体工厂不只是创建一种产品,它负责创建一族产品,在工厂方法模式中,一个每个具体工厂只有一个或者一组重载的工厂方法,只能生产一种产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销,而抽象工厂模式一个工厂可以生产一系列产品(一族产品),极大减少了工厂类的数量。

抽象工厂模式结构

在这里插入图片描述

  • AbstractFactory(抽象工厂)
  • ConcreteFactory(具体工厂)
  • AbstractProduct(抽象产品)
  • ConcreteProduct(具体产品)

例题

在这里插入图片描述
类图:
在这里插入图片描述

优缺点

优点:

  • 隔离了具体类的生成,使得客户端并不需要知道什么被创建
  • 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象
  • 增加新的产品族很方便,无须修改已有系统,符合开闭原则
    缺点:
  • 增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了开闭原则

原型模式

使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象。

即指将一个原型对象传给要发动创建的对象(即客户端对象),这个要发动创建的对象通过请求原型对象复制自己来实现创建过程

原型模式结构

在这里插入图片描述

  • Prototype(抽象原型类)
  • ConcretePrototype(具体原型类)
  • Client(客户类)

浅克隆与深克隆

浅克隆

当原型对象被复制时,只复制它本身和其中包含的值类型的成员变量,而引用类型的成员变量并没有复制

即指复制原型对象,但复制后的对象中的成员变量相同,即复制时复制变量地址而非复制本身

深克隆

除了对象本身被复制外,对象所包含的所有成员变量也将被复制

例题

在这里插入图片描述
浅克隆:(直接使用java中Object类的clone方法)
在这里插入图片描述
深克隆:(通过将复制对象写入序列流中再读取出来实现深克隆,写入流中时会复制成员变量)
在这里插入图片描述

优缺点

优点:

  • 简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率
  • 扩展性较好
  • 提供了简单的创建结构,原型模式中产品的复制是通过封装在原型类中的克隆方法实现的,无须专门的工厂类来创建产品
  • 可以使用深克隆的方式保存对象的状态,以便在需要的时候使用,可辅助实现撤销操作

缺点:

  • 需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时,需要修改源代码,违背了开闭原则
  • 在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来可能会比较麻烦

单例模式

确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例。

即指某个类只能有一个实例

单例模式结构

在这里插入图片描述

例题

在这里插入图片描述
类图
在这里插入图片描述

饿汉式单例

再程序启动时便new出一个实例对象,该方法不需要考虑多线程,但缺点是占用内存大,且程序启动较慢。
在这里插入图片描述

懒汉式单例

懒汉式单例再第一次被引用时才将自己实例化,但是再多线程下会创建多个实例
在这里插入图片描述

懒汉式单例改进(双重检查锁定)

在这里插入图片描述

饿汉式单例与懒汉式单例的比较

  • 饿汉式单例类:无须考虑多个线程同时访问的问题;调用速度和反应时间优于懒汉式单例;资源利用效率不及懒汉式单例;系统加载时间可能会比较长
  • 懒汉式单例类:实现了延迟加载;必须处理好多个线程同时访问的问题;需通过双重检查锁定等机制进行控制,将导致系统性能受到一定影响

优缺点

优点:

  • 提供了对唯一实例的受控访问
  • 可以节约系统资源,提高系统的性能
  • 允许可变数目的实例(多例类)

缺点:

  • 扩展困难(缺少抽象层)
  • 单例类的职责过重
  • 可能会导致共享的单例对象的状态丢失

结构型模式

结构型模式(Structural Pattern)关注如何将现有类或对象组织在一起形成更加强大的结构

类结构型模式

关心类的组合,由多个类组合成一个更大的系统,在类结构型模式中一般只存在继承关系和实现关系

对象结构性模式

关心类与对象的组合,通过关联关系,在一个类中定义另一个类的实例对象,然后通过该对象调用相应的方法

在这里插入图片描述

适配器模式

将一个类的接口转换成客户希望的另一个接口。适配器模式让那些接口不兼容的类可以一起工作。

适配器模式结构(类适配器)

目标类必须是接口,因为一个类只能有一个父类,因此也不能有多个adaptee,且adaptee不能是final类。
在这里插入图片描述

适配器模式结构(对象适配器)

在这里插入图片描述

  • Target(目标抽象类)
  • Adapter(适配器类)
  • Adaptee(适配者类)

例题

在这里插入图片描述
类图:
在这里插入图片描述

优缺点

优点:

  • 目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构
  • 增加了类的透明性和复用性,提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用
  • 灵活性和扩展性非常好
  • 类适配器模式:置换一些适配者的方法很方便
  • 对象适配器模式:可以把多个不同的适配者适配到同一个目标,还可以适配一个适配者的子类

桥接模式

将抽象部分与它的实现部分解耦,使得两者都能够独立变化。

即指将对象间的静态继承关系转化为动态的对象组合关系,使系统更加易于扩展并有效控制了系统中类的个数。用抽象关联取代了传统的多层继承。

桥接模式结构

在这里插入图片描述

  • Abstraction(抽象类)
  • RefinedAbstraction(扩充抽象类)
  • Implementor(实现类接口)
  • ConcreteImplementor(具体实现类)

例题1

假如需要大、中、小三种型号画笔,要能够绘制12种不同颜色,如使用蜡笔需要36只,但是用毛笔则只需提供三种型号的毛笔,请用桥接模式画出毛笔的结构示意图。

在这里插入图片描述

例题2

在这里插入图片描述
在这里插入图片描述

优缺点

优点:

  • 分离抽象接口及其实现部分
  • 可以取代多层继承方案,极大地减少了子类的个数
  • 提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,不需要修改原有系统,符合开闭原则

缺点:

  • 会增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就针对抽象层进行设计与编程
  • 正确识别出系统中两个独立变化的维度并不是一件容易的事情

组合模式

组合多个对象形成树形结构以表示具有部分-整体关系的层次结构。组合模式让客户端可以统一对待单个对象和组合对象。

即指在文件夹中可以包含文件,还可以继续包含子文件夹,在文件中不能再包含子文件或者子文件夹,文件夹就相当于容器,文件相当于叶子,组成一个树形结构。

使用了递归调用的机制来对整个结构进行处理,时的用户再使用时无需对其进行区分。

组合模式结构

注意其中的递归调用。
在这里插入图片描述

  • Component(抽象构件)
  • Leaf(叶子构件)
  • Composite(容器构件)

例题(配和书p155代码进行理解)

在这里插入图片描述
类图:
在这里插入图片描述

优缺点:

优点:

  • 可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,让客户端忽略了层次的差异,方便对整个层次结构进行控制
  • 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码
  • 增加新的容器构件和叶子构件都很方便,符合开闭原则
  • 为树形结构的面向对象实现提供了一种灵活的解决方案

缺点:

  • 在增加新构件时很难对容器中的构件类型进行限制

装饰模式

动态地给一个对象增加一些额外的职责。就扩展功能而言,装饰模式提供了一种比使用子类更加灵活的替代方案。

即指可以在不改变一个对象本身功能的基础上给对象增加额外的新行为(照片+相框  防潮、美观)

装饰模式结构

注意,在Decorator类种并未真正的实现operation方法,只是调用原有Component类种的operation方法,并且将具体的装饰方法交给子类完成。
在这里插入图片描述

  • Component(抽象构件)
  • ConcreteComponent(具体构件)
  • Decorator(抽象装饰类)
  • ConcreteDecorator(具体装饰类)

例题

在这里插入图片描述
类图
在这里插入图片描述

优缺点

优点:

  • 对于扩展一个对象的功能,装饰模式比继承更加灵活,不会导致类的个数急剧增加
  • 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的具体装饰类,从而实现不同的行为
  • 可以对一个对象进行多次装饰
  • 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,且原有类库代码无须改变,符合开闭原则

缺点:

  • 使用装饰模式进行系统设计时将产生很多小对象,大量小对象的产生势必会占用更多的系统资源,在一定程度上影响程序的性能
  • 比继承更加易于出错,排错也更困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐

外观模式

为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得子系统更加容易使用。

即指为复杂的子系统提供一个简答的访问入口。

在这里插入图片描述

外观模式结构

在这里插入图片描述

  • Facade(外观角色)
  • SubSystem(子系统角色)

例题(抽象外观类)

在这里插入图片描述
类图:
在这里插入图片描述
在标准的外观模式结构图中,如果需要增加、删除或更换与外观类交互的子系统类,必须修改外观类或客户端的源代码,这将违背开闭原则,可以通过引入抽象外观类对系统进行改进,在一定程度上解决该问题

抽象外观类类图:
在这里插入图片描述

优缺点

优点:

  • 对客户端屏蔽了子系统组件,减少了客户端所需处理的对象数目,并使得子系统使用起来更加容易
  • 实现了子系统与客户端之间的松耦合关系,这使得子系统的变化不会影响到调用它的客户端,只需要调整外观类即可
  • 子系统的内部变化不会影响到外观对象,一个子系统的修改对其他子系统也没有任何影响

缺点:

  • 不能很好地限制客户端直接使用子系统类,如果对客户端访问子系统类做太多的限制则减少了可变性和灵活性
  • 如果设计不当,增加新的子系统可能需要修改外观类的源代码,违背了开闭原则

代理模式

给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。

代理对象在客户端对象和目标对象种起到中介的作用,他能去掉客户不能看到的内容和添加客户需要的额外的服务

代理模式结构

在这里插入图片描述

  • Subject(抽象主题角色)
  • Proxy(代理主题角色)
  • RealSubject(真实主题角色)

常见代理模式

  • 远程代理(Remote Proxy):为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以在同一台主机中,也可以在另一台主机中,远程代理又称为大使(Ambassador)
  • 虚拟代理(Virtual Proxy):如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。(例如桌面快捷方式
  • 保护代理(Protect Proxy):控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限
  • 缓冲代理(Cache Proxy):为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果
  • 智能引用代理(Smart Reference Proxy):当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数记录下来等

例题

在这里插入图片描述
在这里插入图片描述
类图:
在这里插入图片描述

动态代理(结合书P214代码进行理解)

动态代理(Dynamic Proxy)可以让系统在运行时根据实际需要来动态创建代理类,让同一个代理类能够代理多个不同的真实主题类而且可以代理不同的方法

注意一下几个类种的方法:

Proxy类

  • public static Class<?> getProxyClass(ClassLoader loader, Class<?>… interfaces):该方法用于返回一个Class类型的代理类,在参数中需要提供类加载器并需要指定代理的接口数组(与真实主题类的接口列表一致)
  • public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h):该方法用于返回一个动态创建的代理类的实例,方法中第一个参数loader表示代理类的类加载器,第二个参数interfaces表示代理类所实现的接口列表(与真实主题类的接口列表一致),第三个参数h表示所指派的调用处理程序类

InvocationHandler接口

  • InvocationHandler接口是代理处理程序类的实现接口,该接口作为代理实例的调用处理者的公共父类,每一个代理类的实例都可以提供一个相关的具体调用处理者(InvocationHandler接口的子类)
  • public Object invoke(Object proxy, Method method, Object[] args):该方法用于处理对代理类实例的方法调用并返回相应的结果,当一个代理实例中的业务方法被调用时将自动调用该方法。invoke()方法包含三个参数,其中第一个参数proxy表示代理类的实例,第二个参数method表示需要代理的方法,第三个参数args表示代理方法的参数数组

优缺点

优点:

  • 能够协调调用者和被调用者,在一定程度上降低了系统的耦合度
  • 客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性

缺点:

  • 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢(例如保护代理)
  • 实现代理模式需要额外的工作,而且有些代理模式的实现过程较为复杂(例如远程代理)

各代理模式适用环境

  • 当客户端对象需要访问远程主机中的对象时可以使用远程代理
  • 当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时可以使用虚拟代理
  • 当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓冲代理
  • 当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理
  • 当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理

观察者模式

定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象都得到通知并被自动更新。

即指一个对象的状态或行为的变化将导致其他对象的状态或行为也发生改变,它们之间将产生联动

定义了对象之间一种一对多的依赖关系,让一个对象的改变能够影响其他对象发生改变的对象称为观察目标,被通知的对象称为观察者一个观察目标可以对应多个观察者

观察者模式结构

在这里插入图片描述

  • Subject(目标)
import java.util.*;
public abstract class Subject {
    //定义一个观察者集合用于存储所有观察者对象
    protected ArrayList observers<Observer> = new ArrayList();

    //注册方法,用于向观察者集合中增加一个观察者
    public void attach(Observer observer) {
        observers.add(observer);
    }

    //注销方法,用于在观察者集合中删除一个观察者
    public void detach(Observer observer) {
        observers.remove(observer);
    }

    //声明抽象通知方法
    public abstract void notify();
}
  • ConcreteSubject(具体目标)
  • Observer(观察者)
  • ConcreteObserver(具体观察者)

例题

在这里插入图片描述
分析:联盟为观察目标,玩家为观察者,联盟成员受到攻击 -> 发送通知给盟友 -> 盟友做出响应
在这里插入图片描述

类图:
在这里插入图片描述

优缺点

优点:

  • 可以实现表示层和数据逻辑层的分离
  • 在观察目标和观察者之间建立一个抽象的耦合
  • 支持广播通信,简化了一对多系统设计的难度
  • 符合开闭原则,增加新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便

缺点:

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两个方面封装在独立的对象中使它们可以各自独立地改变和复用
  • 一个对象的改变将导致一个或多个其他对象发生改变,且并不知道具体有多少对象将发生改变,也不知道这些对象是谁
  • 需要在系统中创建一个触发链

策略模式

定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法可以独立于使用它的客户变化。

即指可以定义一些独立的类来封装不同的算法,每一个类封装一种具体的算法

策略模式结构

在这里插入图片描述

  • Context(环境类)
  • Strategy(抽象策略类)
  • ConcreteStrategy(具体策略类)

例题

在这里插入图片描述
类图:
在这里插入图片描述

优缺点

优点:

  • 提供了对开闭原则的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为
  • 提供了管理相关的算法族的办法
  • 提供了一种可以替换继承关系的办法
  • 可以避免多重条件选择语句
  • 提供了一种算法的复用机制,不同的环境类可以方便地复用策略类

缺点:

  • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类
  • 将造成系统产生很多具体策略类
  • 无法同时在客户端使用多个策略类
  • 17
    点赞
  • 99
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值