结构型模式 有更新!
结构型模式描述如何将类或对象按某种布局组成更大的结构,它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者采用组合或者聚合来组合对象
对象结构型模式比类结构型模式具有更大的灵活性
结构型模式分类
- 代理(Proxy) 模式:为某个对象提供一个代理以控制对该对象的访问,即客户端通过代理间接的访问,从而限制、增强、或修改该对象的一些特性
- 适配器(Adapter) 模式:将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作
- 桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化,它是用组合关系来代替继承关系来实现的,从而降低了抽象和实现这两个可变维度的耦合度
- 装饰(Decorator)模式:动态的给对象增加一些职责,即增加其额外的功能。
- 外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问
- 享元(Flyweight)模式:运用共享技术来有效的支持大量细粒度对象的复用
- 组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致性的访问
适配器模式分为类结构型模式和对象结构型模式,其他全部都属于对象结构型模式
代理模式
定义和特点
定义:
由于某些原因需要给对象提供一个代理以控制改对象的访问,这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象的中介
优点:
- 中介作用和保护目标对象作用
- 扩展目标对象的功能
- 客户端和目标对象分离,降低系统的耦合度
缺点:
- 客户端和目标对象增加一个代理对象,会造成请求速度变慢
- 增加系统的复杂度
结构和实现
主要角色:
- 抽象主题(Subject)类:通过接口和抽象类声明真实主题和代理对象实现业务方法
- 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,最终要引用的对象
- 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或者扩展真实主题功能
模式的实现:
package com.swjsj.design.pattern;
public class ProxyTest {
public static void main(String[] args) {
Proxy proxy = new Proxy();
proxy.reqeust();
}
}
interface Subject{
void reqeust();
}
class RealSubject implements Subject {
@Override
public void reqeust() {
System.out.println("访问真实主题的方法!!");
}
}
class Proxy implements Subject {
private Subject subject;
@Override
public void reqeust() {
if (subject == null) {
subject = new RealSubject();
}
preRequest();
subject.reqeust();
postRequest();
}
private void preRequest() {
System.out.println("访问真实主题之前执行方法!!!");
}
private void postRequest() {
System.out.println("访问真实主题之后执行方法!!!");
}
}
应用场景
- 远程代理:隐藏目标对象存在于不同地址空间的事实, 方便客户端访问
- 虚拟代理:通常用于要创建的目标对象开销很大
- 安全代理:控制不同种类的客户对真实对象的访问权限
- 智能指引:附加一些额外的功能
- 延迟加载:提高系统性能,延迟对目标对象的加载:如hibernate对属性的延迟加载和关联表的演示加载
扩展
缺点:
- 真实主题与代理主题一一对应,增加真实主题也要增加代理
- 设计代理以前真实主题必须事先存在, 不太灵活。可以采用动态代理模式解决以上问题。如springaop
适配器模式
定义和特点
将一个接口转换成另外一个接口,使原本由于接口不兼容而不能一起工作的类能在一起工作
分为类结构型模式和对象结构型模式, 类结构型模式耦合度比对象结构型模式高,需要了解现有组件库中的相关组件内部结构,所以应用比较少
优点:
- 客户端通过适配器可以透明的调用目标接口
- 复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类
- 将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题
缺点:
- 对类适配器来说,更换适配器的实现过程比较复杂
结构和实现
类适配器模式可采用多重继承方式实现,java不支持多重继承,但可以定义一个适配器类实现当前系统业务接口,同时又继承现有组件库中已经存在的组件
主要角色:
- 目标(Target) 接口:当前系统业务所期待的接口,它可以是抽象类或者接口
- 适配者(Adaptee)类:它是被访问和适配现有组件接口
- 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者
类适配器模式
package com.swjsj.design.pattern;
//类适配器模式
public class ClassAdapterTest {
public static void main(String[] args) {
Target classApdapter = new ClassApdapter();
classApdapter.request();
}
}
interface Target {
public void request();
}
class Adaptee{
public void specificRequest() {
System.out.println("适配者中的业务代码被调用!!!");
}
}
//适配器类
class ClassApdapter extends Adaptee implements Target {
@Override
public void request() {
System.out.println("类适配器模式测试:");
specificRequest();
}
}
对象适配器模式
package com.swjsj.design.pattern;
//对象适配器模式
public class ObjectAdapterTest {
public static void main(String[] args) {
System.out.println("对象适配器模式测试:");
Adaptee adaptee = new Adaptee();
Target objectAdapter = new ObjectAdapter(adaptee);
objectAdapter.request();
}
}
class ObjectAdapter implements Target {
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
应用场景
- 以前开发的系统满足新系统功能需求的类,但其接口同新系统的接口不一致
- 使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同
扩展
双向适配器模式,即可以把适配者接口转换成目标接口,也可以把目标接口转换成适配器接口
package com.swjsj.design.pattern;
public class TwoWayAdapterTest {
public static void main(String[] args) {
System.out.println("目标通过双向适配器访问适配者:");
TwoWayAdaptee twoWayAdaptee = new AdapteeRealize();
TwoWayAdapter twoWayAdapter = new TwoWayAdapter(twoWayAdaptee);
twoWayAdapter.request();
System.out.println("适配者通过双向适配器访问目标:");
TwoWayTarget twoWayTarget = new TargetRealize();
twoWayAdapter = new TwoWayAdapter(twoWayTarget);
twoWayAdapter.specificRequest();
}
}
interface TwoWayTarget {
public void request();
}
interface TwoWayAdaptee{
public void specificRequest();
}
//目标实现
class TargetRealize implements TwoWayTarget {
@Override
public void request() {
System.out.println("目标代码被调用!!");
}
}
class AdapteeRealize implements TwoWayAdaptee {
@Override
public void specificRequest() {
System.out.println("适配者代码被调用!!");
}
}
class TwoWayAdapter implements TwoWayAdaptee, TwoWayTarget {
private TwoWayAdaptee twoWayAdaptee;
private TwoWayTarget twoWayTarget;
public TwoWayAdapter(TwoWayAdaptee twoWayAdaptee) {
this.twoWayAdaptee = twoWayAdaptee;
}
public TwoWayAdapter(TwoWayTarget twoWayTarget) {
this.twoWayTarget = twoWayTarget;
}
@Override
public void request() {
this.twoWayAdaptee.specificRequest();
}
@Override
public void specificRequest() {
this.twoWayTarget.request();
}
}
桥接模式
定义和特点
将抽象和实现分离,使他们独立变化。它是用组合关系代理继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度
优点:
- 抽象和实现分离,扩展能力强
- 实现细节对客户透明
缺点:
- 聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,这增加了系统的理解与设计难度
结构和实现
主要角色
- 抽象化(Abstraction)角色:定义抽象类,包含一个对实现化对象的引用
- 扩展抽象化(Refined Abstraction)角色, 是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法
- 实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象话角色调用
- 具体实现化(Concrete Implementor) 角色:给出实现化角色接口的具体实现
应用实例:
package com.swjsj.design.pattern;
public class BridgeTest {
public static void main(String[] args) {
Implementor implementor = new ConcreteImplement();
Abastraction abastraction = new RefinedAbstraction(implementor);
abastraction.operation();
}
}
interface Implementor{
public void operationImpl();
}
class ConcreteImplement implements Implementor {
@Override
public void operationImpl() {
System.out.println("具体实现化角色(ConcreteImplement)被访问!!");
}
}
abstract class Abastraction{
protected Implementor implementor;
protected Abastraction(Implementor implementor) {
this.implementor = implementor;
}
public abstract void operation();
}
class RefinedAbstraction extends Abastraction {
protected RefinedAbstraction(Implementor implementor) {
super(implementor);
}
@Override
public void operation() {
System.out.println("扩展抽象化(Refined Abastrction) 角色被访问");
implementor.operationImpl();
}
}
应用场景
- 当一个类存在两个独立变化的维度,且这两个维度都需要扩展的
- 当一个系统不希望使用继承或者多重继承导致类的个数急剧增加时
- 当一个系统需要构建抽象化角色和具体化角色之间增加更多的灵活性时
扩展
与适配器联合使用,桥接模式的实现化角色接口与现有类的接口不一致,可以在两者中间定义一个适配器将两者连接起来
装饰模式
定义和特点
不改变现有对象结构的情况下,动态的给这些对象增加一些职责的模式,属于对象结构型模式
优点:
- 采用装饰模式扩展对象功能比采用继承方式更加灵活
- 可以设计出多个不同的具体装饰类,创造多个不同行为的组合
缺点:
- 增加了很多子类, 如果过度使用会使程序变的很复杂
结构和实现
主要角色:
- 抽象构建角色(Component),定义一个抽象接口以规范准备接收复杂责任的对象
- 具体构建的角色(Concrete Component): 实现抽象构建,通过装饰角色为其添加一些职责
- 抽象装饰角色(Decorator):继承抽象构建,并包含具体构建的实例,可以通过子类扩展具体构建的功能
- 具体装饰角色(ConcreteDecorator):实现抽象装饰的相关方法,并给具体构建对象添加附加的责任
模式的实现:
package com.swjsj.design.pattern;
public class DecoratorPattern {
public static void main(String[] args) {
Component component = new ConcreteComponent();
component.operation();
System.out.println("----------------");
Component component1 = new ConcreteDecorator1(component);
component1.operation();
}
}
interface Component {
public void operation();
}
class ConcreteComponent implements Component {
public ConcreteComponent() {
System.out.println("创建具体构件角色");
}
@Override
public void operation() {
System.out.println("具体构件操作方法调用!!!");
}
}
class Decorator implements Component{
private Component component;
public Decorator(Component component) {
this.component = component;
}
public void operation(){
this.component.operation();
}
}
class ConcreteDecorator1 extends Decorator {
public ConcreteDecorator1(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedFun();
}
private void addedFun() {
System.out.println("为具体构件角色增加额外的功能addedFun()");
}
}
class ConcreteDecorator2 extends Decorator {
public ConcreteDecorator2(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedFun();
}
private void addedFun() {
System.out.println("为具体构件角色增加额外的功能addedFun()");
}
}
应用场景
- 需要给一个现有的类添加功能,而又不能采用生成子类的方法进行扩充的时候 如:类被隐藏,终极类,继承方式会产生大量的子类
- 对现有一组基本功能进行排列组合而产生非常多的功能时,采用继承关系很难实现,而采用装饰模式却很好的实现
- 对象的功能要求可以动态添加,动态撤销时
装饰模式在著名的应用是java i/o 标准库的设计
扩展
4个角色不是任何时候都需要存在的,有些可以简化 如:java i/o 的相关类, 都做了简化操作
- 如果只有一个具体构建而没有抽象构建时,可以让抽象装饰继承具体构建
- 如果只有一个具体装饰时,可以将具体装饰和抽象装饰合并