持续创作,加速成长!这是我参与「掘金日新计划 · 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可以直接操作字节码文件。