面向面试编程:设计模式——结构型模式(下)

151 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第18天,点击查看活动详情

面试官:说说你了解的设计模式

Facade 外观模式

定义

定义了一个高层、统一的接口,外部与通过这个统一的接口对子系统中的一群接口进行访问。

作用

  • 实现客户类与子系统类的松耦合。
  • 降低原有系统的复杂度。
  • 提高了客户端使用的便捷性,使得客户端无须关心子系统的工作细节,通过外观角色即可调用相关功能。

优缺点

优点

  • 降低了客户类与子系统类的耦合度,实现了子系统与客户之间的松耦合关系。
  • 外观模式对客户屏蔽了子系统组件,从而简化了接口,减少了客户处理的对象数目并使子系统的使用更加简单。
  • 降低原有系统的复杂度和系统中的编译依赖性,并简化了系统在不同平台之间的移植过程。

缺点

  • 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。
  • 不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性。

应用场景

  • 要为一个复杂的子系统对外提供一个简单的接口。
  • 提供子系统的独立性。
  • 客户程序与多个子系统之间存在很大的依赖性。
  • 在层次化结构中,可以使用外观模式定义系统中每一层的入口。

Flyweight 享元模式

定义

运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。

角色

  • Flyweight: 享元接口,通过这个接口传入外部状态并作用于外部状态。
  • ConcreteFlyweight: 具体的享元实现对象,必须是可共享的,需要封装享元对象的内部状态。
  • UnsharedConcreteFlyweight: 非共享的享元实现对象,并不是所有的享元对象都可以共享,非共享的享元对象通常是享元对象的组合对象。
  • FlyweightFactory: 享元工厂,主要用来创建并管理共享的享元对象,并对外提供访问共享享元的接口。

优缺点

优点

  • 它可以极大减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份。
  • 享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。

缺点

  • 享元模式使得系统更加复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。
  • 为了使对象可以共享,享元模式需要将享元对象的状态外部化,而读取外部状态使得运行时间变长。

应用场景

  • 一个系统有大量相同或者相似的对象,由于这类对象的大量使用,造成内存的大量耗费。
  • 对象的大部分状态都可以外部化,可以将这些外部状态传入对象中(细粒度对象)。
  • 使用享元模式需要维护一个存储享元对象的享元池,而这需要耗费资源,因此,应当在多次重复使用享元对象时才值得使用享元模式。
  • 广义上讲,在JDK类库中定义的String类也是使用享元模式的典型。

Proxy 代理模式

定义

给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用。

作用

通过引入代理对象的方式来间接访问目标对象。

静态代理

优缺点

优点

  • 协调调用者和被调用者,降低了系统的耦合度。
  • 代理对象作为客户端和目标对象之间的中介,起到了保护目标对象的作用。

缺点

  • 由于在客户端和真实主题之间增加了代理对象,因此会造成请求的处理速度变慢。
  • 实现代理模式需要额外的工作(有些代理模式的实现非常复杂),从而增加了系统实现的复杂度。

应用场景

  • 当需要为一个对象在不同的地址空间提供局部的代表时,此时的代理模式称为远程代理:为一个对象在不同的地址空间提供局部代表。
  • 当需要创建开销非常大的对象时,此时的代理模式称为虚拟代理:通过使用过一个小的对象代理一个大对象。
  • 当需要控制对原始对象的访问时,此时的代理模式称为保护代理:控制目标对象的访问,给不同用户提供不同的访问权限。
  • 当需要在访问对象时附加额外操作时,此时的代理模式称为智能引用代理,额外操作包括耗时操作、计算访问次数等等。
  • 防火墙代理:保护目标不让恶意用户靠近。
  • Cache代理:为结果提供临时的存储空间,以便其他客户端调用。

动态代理

背景

  • 1个静态代理只服务1种类型的目标对象。
  • 若要服务多类型的目标对象,则需要为每种目标对象都实现一个静态代理对象。
  • 在目标对象较多的情况下,若采用静态代理,则会出现 静态代理对象量多、代码量大,从而导致代码复杂的问题。

实现原理

  • 设计动态代理类时,不需要显式实现与目标对象类相同的接口,而是将这种实现推迟到程序运行时由 JVM来实现。
  • 通过Java 反射机制的method.invoke(),通过调用动态代理类对象方法,从而自动调用目标对象的方法。

优缺点

优点

  • 只需要1个动态代理类就可以解决创建多个静态代理的问题,避免重复、多余代码。
  • 更强的灵活性。

缺点

  • 效率低,相比静态代理中 直接调用目标对象方法,动态代理则需要先通过Java反射机制从而间接调用目标对象方法。
  • 应用场景局限,因为Java的单继承特性(每个代理类都继承了Proxy类),即JDK只能针对接口创建代理类,不能针对类创建代理类。

应用场景

  • 基于静态代理应用场景下,需要代理对象数量较多的情况下使用动态代理。
  • AOP 领域。

补充知识

  • JDK动态代理只能针对接口创建代理类。
  • CGlib的动态代理可以针对类创建代理类。
  • 上述两种动态代理底层都是ASM实现的,ASM可以直接操作字节码文件。