设计模式——结构型


我们常把 23 种经典的设计模式分为三类:创建型、结构型、行为型。
我们知道,设计模式要干的事情就是解耦

  • 创建型模式是将对象的创建和使用代码解耦
  • 结构型模式是将不同功能代码解耦
  • 行为型模式是将不同的行为代码解耦

代理模式

装饰器模式(java IO)

装饰器模式本质是通过组合来替代继承,解决继承关系过于复杂的问题。 它主要的作用是对原始类已有功能进行增强(而代理模式是添加新功能,这是二者的主要区别)

当继承关系过于复杂时,就可以考虑用装饰器模式去替换继承了。

举例理解
Java IO是典型的装饰器模式

InputStream in = new FileInputStream("/user/wangzheng/test.txt");
InputStream bin = new BufferedInputStream(in);
byte[] data = new byte[128];
while (bin.read(data) != -1) {
  //...
}

初看上面的代码,我们会觉得 Java IO 的用法比较麻烦,我在想,Java IO 为什么不设计一个继承 FileInputStream 并且支持缓存的 BufferedFileInputStream 类呢?
这样我们就可以像下面的代码中这样,直接创建一个 BufferedFileInputStream 类对象,打开文件读取数据,用起来岂不是更加简单?


InputStream bin = new BufferedFileInputStream("/user/wangzheng/test.txt");
byte[] data = new byte[128];
while (bin.read(data) != -1) {
  //...
}

如果 InputStream 只有一个子类 FileInputStream 的话,那我们在 FileInputStream 基础之上,再设计一个孙子类 BufferedFileInputStream,也算是可以接受的,毕竟继承结构还算简单。但实际上,继承 InputStream 的子类有很多。我们需要给每一个 InputStream 的子类,再继续派生支持缓存读取的子类,就会使得类继承结构变得无比复杂,代码既不好扩展,也不好维护,而jdk采用组合的方式,虽然代码书写麻烦一点,但是类继承结构简单,便于代码的维护和扩展。

桥接模式

定义:
一个类存在多个独立变化的维度,我们通过组合的方式,让这两个或多个维度可以独立进行扩展。通过组合关系来替代继承,避免继承的层次过多导致混乱。

举例理解:
JDBC 驱动是桥接模式的经典应用


Class.forName("com.mysql.jdbc.Driver");//加载及注册JDBC驱动程序
String url = "jdbc:mysql://localhost:3306/sample_db?user=root&password=your_password";
Connection con = DriverManager.getConnection(url);
Statement stmt = con.createStatement()String query = "select * from test";
ResultSet rs=stmt.executeQuery(query);
while(rs.next()) {
  rs.getString(1);
  rs.getInt(2);
}

如果我们想要把 MySQL 数据库换成 Oracle 数据库,只需要把第一行代码中的 com.mysql.jdbc.Driver 换成 oracle.jdbc.driver.OracleDriver 就可以了

适配器模式

将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作
举例理解:USB 转接头充当适配器

具体开发中常用到适配器模式的场景:

  • 封装有缺陷的接口设计
  • 统一多个类的接口设计
  • 兼容老版本接口

1、封装有缺陷的接口设计
假设我们依赖的外部系统在接口设计方面有缺陷(比如包含大量静态方法),引入之后会影响到我们自身代码的可测试性。为了隔离设计上的缺陷,我们希望对外部系统提供的接口进行二次封装,抽象出更好的接口设计,这个时候就可以使用适配器模式了。

2、统一多个类的接口设计
某个功能的实现依赖多个外部类。通过适配器模式,将它们的接口适配为统一的接口定义,然后我们就可以使用多态的特性来复用代码逻辑。

3、 兼容老版本接口
在做版本升级的时候,对于一些要废弃的接口,我们不直接将其删除,而是暂时保留,并且标注为 deprecated,并将内部实现逻辑委托为新的接口实现。这样做的好处是,让使用它的项目有个过渡期,而不是强制进行代码修改。

门面模式

为了保证接口的通用,我们需要将接口尽量设计得细粒度一点,尽量职责单一。但是如果接口的粒度过小,在接口的使用者开发一个业务功能时,就会导致需要调用很多细粒度的接口才能完成。就很麻烦
相反,如果接口粒度设计得太大,一个接口返回 n 多数据,要做 n 多事情,就会导致接口不够通用。针对不同的调用者的业务需求,我们就需要开发不同的接口来满足,这就会导致系统的接口无限膨胀。

总之,接口的粒度过大过小都不好,怎么办?——门面模式

门面模式定义:为子系统提供一组统一的接口,定义一组高层接口让子系统更易用。
理解:假设有一个系统 A,提供了 a、b、c、d 四个接口。系统 B 完成某个业务功能,需要调用 A 系统的 a、b、d 接口。利用门面模式,我们提供一个封装 a、b、d 接口调用的门面接口 x,给系统 B 使用,系统B只需要调用x接口即可。

适配器 vs 门面

  • 适配器是做接口转换,解决的是原接口和目标接口不匹配的问题。
  • 门面模式做接口整合,解决的是多接口调用带来的问题。

组合模式(此组合非彼组合)

组合模式和“组合关系”(组合两个类,组合优于继承)中的"组合"不一样

组合模式常用来实现支持递归遍历的文件系统目录树结构:
组合模式将一组对象组织成树形结构,将单个对象和组合对象都看做树中的节点,以统一处理逻辑,并且它利用树形结构的特点,递归地处理每个子树,依次简化代码实现。
使用组合模式的前提在于,你的业务场景必须能够表示成树形结构,所以,组合模式的应用场景也比较局限,它并不是一种很常用的设计模式。

享元模式

享元模式的思想本质就是共享,比如java的Integer包装类,在数值小于128时就会采用享元模式返回内存中已存在的Integer对象,以达到节省内存的目的。

注意共享的对象是不可变对象,否则无法共享。

组合优于继承的设计原则

上面多种模式都是利用组合去替换继承,那组合到底比继承好在哪里呢?推荐看下面这篇文章:
组合优于继承

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值