容错的处理是保障分布式环境下相应系统的高可用或者健壮性,一个典型的案例就是对于缓存穿透 问题的解决方案。我们来具体看一下这个例子,如图所示
问题描述:
我们在项目中使用缓存通常都是先检查缓存中是否存在,如果存在直接返回缓存内容,如果不存在就直接查询数据库然后再缓存查询结果返回。这个时候如果我们查询的某一个数据在缓存中一直不存在,就会造成每一次请求都查询DB ,这样缓存就失去了意义,在流量大时,或者有人恶意攻击
如频繁发起为id 为 “-1” 的条件进行查询,可能 DB 就挂掉了。
那这种问题有什么好办法解决呢?
1. 临时存放 null 值
2. 使用布隆过滤器
4.4 负载均衡
负载均衡:其关键在于使用多台集群服务器共同分担计算任务,把网络请求及计算分配到集群可用的不同服务器节点上,从而达到高可用性及较好的用户操作体验。
负载均衡器有硬件解决方案,也有软件解决方案。硬件解决方案有著名的 F5 ,软件有 LVS、 HAProxy 、Nginx等。
以 Nginx 为例,负载均衡有以下 6 种策略:
5. 分布式架构服务调用
5.1 服务调用
和传统的单体架构相比,分布式多了一个远程服务之间的通信,不管是 soa 还是微服务,他们本质上都是对于业务服务的提炼和复用。那么远程服务之间的调用才是实现分布式的关键因素
5.2 实现方式
1. HttpURLConnection
java 原生 HttpURLConnection 是基于 http 协议的,支持 get , post , put , delete 等各种请求方式,最常用的就是get 和 post
2. Apache Common HttpClient
HttpClient 是 Apache Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持
HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本。
实现了所有 HTTP 的方法( GET,POST,PUT,HEAD 等)
支持 HTTPS 协议
支持代理服务器等
3. OKhttp3
OKHttp 是一个当前主流的网络请求的开源框架 , 用于替代 HttpUrlConnection 和 Apache HttpClient
支持 http2.0 ,对一台机器的请求共享一个 socket 。
采用连接池技术,可以有效的减少 Http 连接数量。
无缝集成 GZIP 压缩技术。
支持 Response Cache ,避免重复请求
域名多 IP 支持
4. RestTemplate
Spring RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端, RestTemplate 提供了多种便捷访问远程Http 服务的方法,能够大大提高客户端的编写效率,所以很多客户端比如 Android或第三方服务商都是使用 RestTemplate 请求 restful 服务。
面向 URL 组件,必须依赖于主机 + 端口 + URI
RestTemplate 不依赖于服务接口,仅关注 REST 响应内容
Spring Cloud Feign
RPC 框架
RPC 全称为 remote procedure call ,即远程过程调用。借助 RPC 可以做到像本地调用一样调用远程服务,是一种进程间的通信方式. 。常见的 RPC 框架有一下几种 .
1. Java RMI
Java RMI ( Romote Method Invocation )是一种基于 Java 的远程方法调用技术,是 Java 特有的一种RPC 实现。
2. Hessian
Hessian 是一个轻量级的 remoting onhttp 工具,使用简单的方法提供了 RMI 的功能 . 相比WebService, Hessian 更简单、快捷。采用的是二进制 RPC 协议,因为采用的是二进制协议,所以它很适合于发送二进制数据。
3. Dubbo
Dubbo 是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和Spring 框架无缝集成。 Dubbo 是一款高性能、轻量级的开源 Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
4. gRPC
gRPC 是由 Google 公司开源的一款高性能的远程过程调用 (RPC) 框架,可以在任何环境下运行。该框架提供了负载均衡,跟踪,智能监控,身份验证等功能,可以实现系统间的高效连接。
5.3 跨域调用
跨域
在分布式系统中 , 会有调用其他业务系统 , 导致出现跨域问题 ,跨域实质上是浏览器的一种保护处理。如果产生了跨域,服务器在返回结果时就会被浏览器拦截( 注意:此时请求是可以正常发起的,只是浏览器对其进行了拦截) ,导致响应的内容不可用 . 产生跨域的几种情况有一下 :
常见的解决方案
1. 使用 jsonp 解决网站跨域
缺点:不支持 post 请求,代码书写比较复杂
2. 使用 HttpClient 内部转发
3. 使用设置响应头允许跨域
response.setHeader(“Access-Control-Allow-Origin”, “*”); 设置响应头允许跨域 .
4. 基于 Nginx 搭建企业级 API 接口网关
5. 使用 Zuul 搭建微服务 API 接口网关
Zuul 是 spring cloud 中的微服务网关。网关: 是一个网络整体系统中的前置门户入口。请求首先通过网关,进行路径的路由,定位到具体的服务节点上。可以使用zuul 的过滤器的请求转发去解决跨域问题
6.1 服务协调
分布式协调技术主要用来解决分布式环境当中多个进程之间的同步控制,让他们有序的去访问某种临界资源,防止造成" 脏数据 " 的后果。
分布式锁也就是我们 分布式协调技术 实现的核心内容。
分布式锁两种实现方式:
1. 基于缓存( Redis 等)实现分布式锁
获取锁的时候,使用 setnx 加锁,并使用 expire 命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value 值为一个随机生成的 UUID, 释放锁的时候进行判断。
获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。
释放锁的时候,通过 UUID 判断是不是该锁,若是该锁,则执行 delete 进行锁释放。
SETNX : set 一个 key 为 value 的字符串,返回 1 ;若 key 存在,则什么都不做,返回 0。
expire: 为 key 设置一个超时时间,单位为 second ,超过这个时间锁会自动释放,避免死锁。
delete :删除 key
2. ZooKeeper 是一个为分布式应用提供一致性服务的开源组件,它内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一文件名, 基于 ZooKeeper 实现分布式锁的步骤如下:
创建一个目录 mylock
线程 A 想获取锁就在 mylock 目录下创建临时顺序节点
获取 mylock 目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁
线程 B 获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点
线程 A 处理完,删除自己的节点,线程 B 监听到变更事件,判断自己是不是最小的节点,如果是则获得锁
6.2 服务削峰
为什么要削峰
主要是还是来自于互联网的业务场景,例如,春节火车票抢购,大量的用户需要同一时间去抢购;以及大家熟知的阿里双11 秒杀, 短时间上亿的用户涌入,瞬间流量巨大(高并发)
流量削峰方案
削峰从本质上来说就是更多地延缓用户请求,以及层层过滤用户的访问需求,遵从“最后落地到数据库的请求数要尽量少”的原则。
1. 消息队列解决削峰
要对流量进行削峰,最容易想到的解决方案就是用消息队列来缓冲瞬时流量,把同步的直接调用转换成异步的间接推送,中间通过一个队列在一端承接瞬时的流量洪峰,在另一端平滑地将消息推送出去。
消息队列中间件主要解决应用耦合,异步消息, 流量削锋等问题。常用消息队列系统:目前在生产环境,使用较多的消息队列有 ActiveMQ 、 RabbitMQ 、 ZeroMQ 、 Kafka 、 RocketMQ 等。
在这里,消息队列就像“水库”一样,拦截上游的洪水,削减进入下游河道的洪峰流量,从而达到减免洪水灾害的目的。
2. 流量削峰漏斗:层层削峰
分层过滤其实就是采用 “ 漏斗 ” 式设计来处理请求的,这样就像漏斗一样,尽量把数据量和请求量一层一层地过滤和减少了。如下图所示:
分层过滤的核心思想
通过在不同的层次尽可能地过滤掉无效请求。
通过 CDN 过滤掉大量的图片,静态资源的请求。
再通过类似 Redis 这样的分布式缓存过滤请求
分层过滤的基本原则
对写数据进行基于时间的合理分片,过滤掉过期的失效请求。
对写请求做限流保护,将超出系统承载能力的请求过滤掉。
涉及到的读数据不做强一致性校验,减少因为一致性校验产生瓶颈的问题。
对写数据进行强一致性校验,只保留最后有效的数据。
6.3 服务降级
什么是服务降级
当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证核心服务正常运作或高效运作
整个架构整体的负载超出了预设的上限阈值或即将到来的流量预计将会超过预设的阈值时,为了保证重要或基本的服务能正常运行,我们可以将一些 不重要 或 不紧急 的服务或任务进行服务的延迟使用 或 暂停使用
降级策略
当触发服务降级后,新的交易再次到达时,我们该如何来处理这些请求呢?从分布式 , 微服务架构全局的视角来看,降级处理方案:
页面降级 —— 可视化界面禁用点击按钮、调整静态页面
延迟服务 —— 如定时任务延迟处理、消息入 MQ 后延迟处理
写降级 —— 直接禁止相关写操作的服务请求
读降级 —— 直接禁止相关读的服务请求
缓存降级 —— 使用缓存方式来降级部分读频繁的服务接口
针对后端代码层面的降级处理策略,则我们通常使用以下几种处理措施进行降级处理:
抛异常
返回 NULL
调用 Mock 数据
调用 Fallback 处理逻辑
分级降级
结合服务能否降级的优先原则,并根据台风预警(都属于风暴预警)的等级进行参考设计,可将分布式服务架构的所有服务进行故障风暴等级划分为以下四种:
6.4 服务限流
什么是服务限流
限流的目的是通过对并发访问请求进行限速或者一个时间窗口内的的请求数量进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待
多维度限流
在请求到达目标服务接口的时候 , 可以使用多维度的限流策略 , 这样就可以让系统平稳度过瞬间来临的并发
限流算法
限流算法 - 计数器 ( 固定窗口 )
计数器限制每一分钟或者每一秒钟内请求不能超过一定的次数,在下一秒钟计数器清零重新计算
存在问题:
客户端在第一分钟的 59 秒请求 100 次,在第二分钟的第 1 秒又请求了 100 次 , 2 秒内后端会受到 200次请求的压力,形成了流量突刺
2. 限流算法 - 计数器 ( 滑动窗口 )
滑动窗口其实是细分后的计数器,它将每个时间窗口又细分成若干个时间片段,每过一个时间片段,整个时间窗口就会往右移动一格
时间窗口向右滑动一格,这时这个时间窗口其实已经打满了 100 次,客户端将被拒绝访问 , 时间窗口 划分的越细,滑动窗口的滚动就越平滑,限流的效果就会越精确
3. 限流算法-漏桶
漏桶算法类似一个限制出水速度的水桶,通过一个固定大小 FIFO 队列 + 定时取队列元素的方式实现,请求进入队列后会被匀速的取出处理(桶底部开口匀速出水),当队列被占满后后来的请求会直接拒绝(水倒的太快从桶中溢出来)
优点是可以削峰填谷,不论请求多大多快,都只会匀速发给后端,不会出现突刺现象,保证下游服务正常运行 , 缺点就是在桶队列中的请求会排队,响应时间拉长
4. 限流算法-令牌桶
令牌桶算法是以一个 恒定的速度往桶里放置令牌(如果桶里的令牌满了就废弃),每进来一个请求 去桶里找令牌,有的话就拿走令牌继续处理,没有就拒绝请求
令牌桶的优点是可以应对突发流量,当桶里有令牌时请求可以快速的响应,也不会产生漏桶队列中的等待时间, 缺点就是相对漏桶一定程度上减小了对下游服务的保护
6.5 服务熔断
什么是服务熔断
【熔断】 , 熔断这一概念来源于电子工程中的断路器( Circuit Breaker )。在互联网系统中,当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。这种牺牲局部,保全整体的措施就叫做熔断。
如果不采取熔断措施,我们的系统会怎样呢?
举例说明:
当前系统中有 A , B , C 三个服务,服务 A 是上游,服务 B 是中游,服务 C 是下游 . 它们的调用链如下:
一旦下游服务 C 因某些原因变得不可用,积压了大量请求,服务 B 的请求线程也随之阻塞。线程资源逐渐耗尽,使得服务B 也变得不可用。紧接着,服务 A 也变为不可用,整个调用链路被拖垮。
像这种调用链路的连锁故障,叫做雪崩。
熔断机制
在这种时候,就需要我们的熔断机制来挽救整个系统。
1. 开启熔断
在固定时间窗口内,接口调用超时比率达到一个阈值,会开启熔断。
进入熔断状态后,后续对该服务接口的调用不再经过网络,直接执行本地的默认方法,达到服务降级的效果。
2. 熔断恢复
熔断不可能是永久的。当经过了规定时间之后,服务将从熔断状态回复过来,再次接受调用方的远程调用。
熔断机制实现
1. Spring Cloud Hystrix
Spring Cloud Hystrix 是基于 Netflix 的开源框架 Hystrix 实现,该框架实现了服务熔断、线程隔离等一系列服务保护功能。
对于熔断机制的实现, Hystrix 设计了三种状态:
熔断关闭状态(Closed ):服务没有故障时,熔断器所处的状态,对调用方的调用不做任何限制。
熔断开启状态(Open): 在固定时间内(Hystrix 默认是 10 秒),接口调用出错比率达到一个阈值( Hystrix 默认为50%),会进入熔断开启状态。进入熔断状态后, 后续对该服务接口的调用不再经过网络,直接执行本地的fallback 方法。
半熔断状态(Half-Open): 在进入熔断开启状态一段时间之后(Hystrix 默认是 5 秒),熔断器会进入半熔断状态。所谓半熔断就是尝试恢复服务调用,允许有限的流量调用该服务,并监控调用成功率。如果成功率达到预期,则说明服务已恢复,进入熔断关闭状态;如果成功率仍旧很低,则重新进入熔断开启状态。
三个状态的转化关系如下图:
2. Sentinel
Sentinel 和 Hystrix 的原则是一致的 : 当调用链路中某个资源出现不稳定,例如,表现为timeout,异常比例升高的时候,则对这个资源的调用进行限制,并让请求快速失败,防止避免影响到其它的资源,最终产生雪崩的效果。
Sentinel 熔断手段:
通过并发线程数进行限制
通过响应时间对资源进行降级
系统负载保护
6.6 服务链路追踪
什么是链路追踪
分布式微服务架构上通过业务来划分服务的,通过 REST 调用对外暴露的一个接口,可能需要很多个服务协同才能完成这个接口功能,如果链路上任何一个服务出现问题或者网络超时,都会形成导致接口调用失败。随着业务的不断扩张,服务之间互相调用会越来越复杂。
随着服务的越来越多,对调用链的分析会越来越复杂。它们之间的调用关系也许如下:
分布式链路追踪( Distributed Tracing ),也叫 分布式链路跟踪,分布式跟踪,分布式追踪 等等。 其实就是将一次分布式请求还原成调用链路。显示的在后端查看一次分布式请求的调用情况,比如各个节点上的耗时、请求具体打到了哪台机器上、每个服务节点的请求状态等等。
链路跟踪具备的功能
1. 故障快速定位
通过调用链跟踪,一次请求的逻辑轨迹可以用完整清晰的展示出来。开发中可以在业务日志中添加调用链ID ,可以通过调用链结合业务日志快速定位错误信息。
2. 各个调用环节的性能分析
在调用链的各个环节分别添加调用时延,可以分析系统的性能瓶颈,可以进行针对性的优化。通过分析各个环节的平均时延,QPS 等信息,可以找到系统的薄弱环节,对一些模块做调整。
3. 数据分析
调用链绑定业务后查看具体每条业务数据对应的链路问题,可以得到用户的行为路径,经过了哪些服务器上的哪个服务,汇总分析应用在很多业务场景。
4. 生成服务调用拓扑图
通过可视化分布式系统的模块和他们之间的相互联系来理解系统拓扑。点击某个节点会展示这个模块的详情,比如它当前的状态和请求数量。
链路跟踪设计原则
1. 设计目标
低侵入性,应用透明
低损耗
大范围部署,扩展性
2. 埋点和生成日志
埋点即系统在当前节点的上下文信息,可以分为客户端埋点、服务端埋点,以及客户端和服务端双向型埋点。埋点日志通常要包含以下内容:
TraceId 、 RPCId 、调用的开始时间,调用类型,协议类型,调用方 ip 和端口,请求的服务名等信息;调用耗时,调用结果,异常信息,消息报文等
3. 抓取和存储日志
日志的采集和存储有许多开源的工具可以选择,一般来说,会使用离线 + 实时的方式去存储日志,主要是分布式日志采集的方式。典型的解决方案如Flume 结合 Kafka 。
4. 分析和统计调用链数据
一条调用链的日志散落在调用经过的各个服务器上,首先需要按 TraceId 汇总日志,然后按照RpcId 对调用链进行顺序整理。调用链数据不要求百分之百准确,可以允许中间的部分日志丢失。
5. 计算和展示
汇总得到各个应用节点的调用链日志后,可以针对性的对各个业务线进行分析。需要对具体日志进行整理,进一步储存在HBase 或者关系型数据库中,可以进行可视化的查询。
链路跟踪Trace模型
Trace 调用模型,主要有以下概念:
Client && Server :对于跨服务的一次调用,请求发起方为 client ,服务提供方为 Server 各术语在一次分布式调用中,关系如下图所示
链路跟踪系统实现:
大的互联网公司都有自己的分布式跟踪系统,比如 Google 的 Dapper , Twitter 的 zipkin ,淘宝的鹰眼,新浪的Watchman ,京东的 Hydra 等等
7.架构设计基本原则
架构最重要的就是编程思想:
1. 利于开发者
2. 利于公司
3. 利于客户
7.1 开闭原则
开闭原则的定义
开闭原则:软件实体应当对扩展开放,对修改关闭,这就是开闭原则的经典定义。
这里的软件实体包括以下几个部分:
1. 项目中划分出的模块
2. 类与接口
3. 方法
开闭原则的含义是:当应用的需求改变时,在不修改软件实体的源代码或者二进制代码的前提下,可以扩展模块的功能,使其满足新的需求。
开闭原则的作用
开闭原则是面向对象程序设计的终极目标,它使软件实体拥有一定的适应性和灵活性的同时具备稳定性和延续性。具体来说,其作用如下。
1. 对软件测试的影响
2. 可以提高代码的可复用性
3. 可以提高软件的可维护性
开闭原则的实现方法
可以通过 “ 抽象约束、封装变化 ” 来实现开闭原则,即通过接口或者抽象类为软件实体定义一个相对稳定的抽象层,而将相同的可变因素封装在相同的具体实现类中。
7.2 单一职责原则
单一职责原则的定义
单一职责原则又称单一功能原则,这里的职责是指类变化的原因,单一职责原则规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分。
该原则提出对象不应该承担太多职责,如果一个对象承担了太多的职责,至少存在以下两个缺点:
1. 一个职责的变化可能会削弱或者抑制这个类实现其他职责的能力;
2. 当客户端需要该对象的某一个职责时,不得不将其他不需要的职责全都包含进来,从而造成冗余代码或代码的浪费。
单一职责原则的优点
单一职责原则的核心就是控制类的粒度大小、将对象解耦、提高其内聚性。如果遵循单一职责原则将有以下优点。
1. 降低类的复杂度
2. 提高类的可读性
3. 提高系统的可维护性
4. 变更引起的风险降低
7.3 接口隔离原则
接口隔离原则的定义
接口隔离原则要求程序员尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客户感兴趣的方法。
接口隔离原则和单一职责都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想,但两者是不同的:
1. 单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离。
2. 单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。
接口隔离原则的优点
接口隔离原则是为了约束接口、降低类对接口的依赖性,遵循接口隔离原则有以下 5 个优点。
1. 提高系统的灵活性和可维护性
2. 降低系统的耦合性。
3. 保证系统的稳定性
4. 使用多个专门的接口还能够体现对象的层次,因为可以通过接口的继承,实现对总接口的定义。
5. 能减少项目工程中的代码冗余
接口隔离原则的实现方法
在具体应用接口隔离原则时,应该根据以下几个规则来衡量。
1. 接口尽量小,但是要有限度。一个接口只服务于一个子模块或业务逻辑。
2. 为依赖接口的类定制服务。只提供调用者需要的方法,屏蔽不需要的方法。
3. 了解环境,拒绝盲从。每个项目或产品都有选定的环境因素,环境不同,接口拆分的标准就不同深入了解业务逻辑。
4. 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。
7.4 里氏替换原则
里氏替换原则的定义
里氏替换原则主要阐述了有关继承的一些原则。里氏替换原则是继承复用的基础,它反映了基类与子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范。总结: 子类可以扩展父类的 功能,但不能改变父类原有的功能
里氏替换原则的作用
里氏替换原则的主要作用如下。
1. 里氏替换原则是实现开闭原则的重要方式之一。
2. 它克服了继承中重写父类造成的可复用性变差的缺点。
3. 它是动作正确性的保证。即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性。
4. 加强程序的健壮性,同时变更时可以做到非常好的兼容性,提高程序的维护性、可扩展性,降低需求变更时引入的风险。
里氏替换原则的实现方法
根据上述理解,对里氏替换原则的定义可以总结如下:
1. 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
2. 子类中可以增加自己特有的方法
7.5 依赖倒置原则
依赖倒置原则定义
依赖倒置原则的原始定义为:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。其核心思想是:要面向接口编程,不要面向实现编程 。
依赖倒置原则的作用
依赖倒置原则的主要作用如下。
1. 可以降低类间的耦合性。
2. 可以提高系统的稳定性。
3. 可以减少并行开发引起的风险。
4. 可以提高代码的可读性和可维护性。
7.6 迪米特法则
迪米特法则的定义
迪米特法则又叫作最少知识原则 , 迪米特法则的定义是:只与你的直接朋友交谈,不跟 “ 陌生人 ”说 话。其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。
迪米特法则的优点
迪米特法则要求限制软件实体之间通信的宽度和深度,正确使用迪米特法则将有以下两个优点。
1. 降低了类之间的耦合度,提高了模块的相对独立性。
2. 由于亲合度降低,从而提高了类的可复用性和系统的扩展性。
迪米特法则的实现方法
从迪米特法则的定义和特点可知,它强调以下两点:
1. 从依赖者的角度来说,只依赖应该依赖的对象。
2. 从被依赖者的角度说,只暴露应该暴露的方法。
7.7 合成复用原则
合成复用原则的定义
合成复用原则( Composite Reuse Principle , CRP )又叫组合 / 聚合复用原则(Composition/Aggregate Reuse Principle , CARP )。它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的,两者都是开闭原则的具体实现规范。
合成复用原则的重要性
通常类的复用分为继承复用和合成复用两种,继承复用虽然有简单和易实现的优点,但它也存在以下缺点。
1. 继承复用破坏了类的封装性
2. 子类与父类的耦合度高
3. 它限制了复用的灵活性
采用合成复用原则时,他可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它有以下优点。
1. 它维持了类的封装性
2. 新旧类之间的耦合度低
3. 复用的灵活性高
合成复用原则的实现方法
合成复用原则是通过将已有的对象纳入新对象中,作为新对象的成员对象来实现的,新对象可以调用已有对象的功能,从而达到复用。