一、什么是分布式系统?
指一个硬件或软件, 分布在不同的网络计算机中, 彼此之间仅仅通过消息传递进行通信协调的系统。通俗来说, 一个业务拆分成多个子业务, 分布在不同的服务节点,共同构成了分布式系统。
二、分布式与集群的关系
集群: 指多个服务器做同一件事
分布式:指多个服务器做不一样的事
分布式系统特性:
1. 分布性
2. 对等性 ==> 指没有主从之分
3. 并发性
4. 缺乏全局时钟 ==> 每个计算机之间依赖于消息进行通信, 很难判断先后序, 缺乏全局时钟控制序列
5. 故障总会发生
6. 处理单点故障
三、 分布式系统面临的问题
1. 通信异常, 消息丢失或消息延迟
2. 网络分区:网络之间出现了网络不连通,但各个子网络的内部网络是正常的,从而导致整个系统的网络环境被 切分成了若干个孤立的区域,分布式系统就会出现局部小集群,在极端情况下,这些小集群会独立 完成原本需要整个分布式系统才能完成的功能,包括数据的事务处理,这就对分布式致性提出非 常大的挑战。
3. 节点故障
4. 三态: 分布式系统每一次请求与响应存在特有的“三态”概念,即成功、失败和超时
5. 重发: 分布式系统在发生调用的时候可能会出现 失败 超时 的情况. 这个时候需要重新发起调用.
6. 幂等性: 不管多少次请求, 与第一次请求结果无影响
四、 分布式的数据一致性
分布式数据一致性,指的是数据在多份副本中存储时,各副本中的数据是一致的。
副本一致性: 由于网络通信可用延迟, 也有可能失败, 所以我们很难保证同时更新所有机器的备份数据, 因此, 在保证数据的一致性下,同时不影响计算机性能, 是每个分布式系统应该重点考虑以及权衡。 而有了一致性级别:
1 .强一致性
这种一致性级别是最符合用户直觉的,它要求系统写入什么,读出来的也会是什么,用户体验好,
但实现起来往往对系统的性能影响大。但是强一致性很难实现。
2. 弱一致性
这种一致性级别约束了系统在写入成功后,不承诺立即可以读到写入的值,也不承诺多久之后数据
能够达到一致,但会尽可能地保证到某个时间级别(比如秒级别)后,数据能够达到一致状态。
3. 最终一致性 (2小时到账)
最终一致性也是弱一致性的一种
,它无法保证数据更新后,所有后续的访问都能看到最新数值,而
是需要一个时间,在这个时间之后可以保证这一点(
就是在一段时间后,节点间的数据会最终达到
一致状态
),而在这个时间内,数据也许是不一致的,这个系统无法保证强一致性的时间片段被称
为「不一致窗口」。不一致窗口的时间长短取决于很多因素,比如备份数据的个数、网络传输延迟
速度、系统负载等。
最终一致性的变种:
因果一致性: A修改后通知了B, B在获取得到修改后的数据, C没有关联, 则读的旧数据
读已之所写一致性: 当A自己更新了节点一, 下一次A在读取节点二同一个值时, 他一定是更新过的值。
会话一致性:
它把访问存储系统的进程放到会话的上下文中。只要会话还存在,系统就保证
“
读己之所写
”
一
致性。如果由于某些失败情形令会话终止,就要建立新的会话,而且系统的保证不会延续到新
的会话。
单调读一致性: A更新后, 继续读取到更新的值, 往后不可能读到以前的值
单调写一致性: 系统保证对同一进程的写操作, 串行化
![](https://img-blog.csdnimg.cn/7f589bae0b29481f9467afe9e227c803.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56iL5bqP54y_6Im-5byX5qOu,size_20,color_FFFFFF,t_70,g_se,x_16)
五、CAP定理
它指分布式系统中不能同时满足CAP三个条件,只能(
CA, CP, AP)
Consistency: 一致性 所有节点访问时都是一份最新的数据 ( 强一致性)
Availability: 可用性
每次请求都能获取到非错的响应,但是不保证获取的数据为最新数据 (不允许忽略客服端请求)
Partition tolerance: 分区容错性
分布式系统在遇到任何网络分区故障的时候,仍然能够对外提供满足一致
性和可用性的服务,除非整个网络环境都发生了故障 (能够容忍, 一个节点发送到另外节点的多个消息丢失)
CAP如何权衡:
CA: 一致性+可用性,
它需要非常严格的全体一致的协议。不能容忍网络错误或节点错误, 一旦出现这种问题, 整个系统拒绝写请求,
因为它并不
知道对面的那个结点是否挂掉了,还是只是网络问题。唯一安全的做法就是把自己变成只读的。
CP: 一致性+容错性,
它关注的是系统里大多数人
的一致性协议。这样的系统只需要保证大多数结点数据一致,而少
的结点会在没有同步到最新版
本的数据时变成不可用的状态。这样能够提一部分的可用性。
AP (availability + partition tolerance)
:这样的系统关心可用性和分区容忍性。因此,这样的系统
不能达成一致性,需要给出数据冲突,给出数据冲突就需要维护数据版本。
如何进行三选二
放弃了一致性,满足分区容错,那么节点之间就有可能失去联系,为了高可用,每个节点只能用本
地数据提供服务,而这样会容易导致全局数据不一致性。对于互联网应用来说,机器数量庞大,点分 散,网络故障再正常不过了,那么此时就是保障AP
,放弃
C
的场景,而从实际中理解,像站这偶尔 没有一致性是能接受的,但不能访问问题就非常大了。
对于银行来说,就是必须保证强一致性,也就是说
C
必须存在,那么就只用
CA
和
CP
两种情况,当保 障强一致性和可用性(CA
),那么一旦出现通信故障,系统将完全不可用。另一方面,如果保了强一 致性和分区容错(CP
),那么就具备了部分可用性。实际究竟应该选择什么,是需要通过业务景进行 权衡的(并不是所有情况都是CP
好于
CA
,只能查看信息但不能更新信息有时候还不直接拒务)
BASEL理论:
CAP
不可能同时满足,而分区容错性是对于分布式系统而言,是必须的。最后,我们说,如果系统能够同时实现 CAP
是再好不过的了,所以出现了
BASE
理论!
BASE
:全称:
Basically Available(
基本可用
)
,
Soft state
(软状态)
,
和
Eventually consistent
(最终一 致性)
BASE理论是对, 一致性和可用性权衡的结果, 其来源与大型互联网分布式实践的总结, 是基于CAP定理逐步演化而来, 其核心思想是: 即使做不到强一致性, 但每个应用都可以更加自生的业务特点, 采用适当的方式达到最终一致性。
什么是基本可用 ?
假设系统出现不可预知的故障, 但还能用, 相比较正常的系统而言:
1. 响应时间的缺失, 正常0.5秒, 基本可用1秒
2.
功能上的损失:在一个电商网站上,正常情况下,用户可以顺利完成每一笔订单,但是到了大
促期间,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面
什么是软状态?
相对于原子性而言, 要求多个节点的数据副本都是一致的, 是硬状态,
软状态指的是:允许系统中的数据存在中间状态,并认为该状态不会影响系统的整体可用性,即允
许系统在多个不同节点的数据副本存在数据延时。
最终一致性?
上面说软状态,然后不可能一直是软状态,必须有个时间期限。在期限过后,应当保证所有副本保
持数据一致性。从而达到数据的最终一致性。这个时间期限取决于网络延时,系统负载,数据复制
方案设计等等因素。
六、分布式设计策略
在分布式环境下,有几个问题是普遍关心的
.
如何检测当前节点还活着?
如何保障高可用?
容错处理
负载均衡
6.1. 心跳检测:
周期检测心跳机制
、
累计失效检
测机制
6.2. 高可用HA设计
系统高可用性的常用设计模式包括三种:主备(
Master-SLave)、互备(Active-Active)和集群
(Cluster)模式。
主备模式: 当主机宕机了, 备机接管主机工作,
待主机恢复正常后,按使用者的设定以自动(热备)或手动(冷备)方式将服务切换到主机上运行。在数据库中称M/S模式 (Master/Slave)
这在数据库高可用性方案中比较常用,如 MySQL、Redis
等就采用
MS
模式实现主从复制。保证高可用
互备模式:
指两台主机同时运行各自的服务工作且相互监测情况。在数据库高可用部分,常见的互
备是
MM
模式。
MM
模式即
Multi-Master
模式,指一个系统存在多个
master
,每个
master
都具有
read-write
能力,会根据时间戳或业务逻辑合并版本。
集群模式
集群模式是指有多个节点在运行,同时可以通过主控节点分担服务请求。集群模式需要解决主控
节点本身的高可用问题,一般采用主备模式。
高可用下脑裂问题?
当俩个节点断开联系, 成为俩个独立的节点, 由于互相失去了联系, 都以为对方出了故障, 导致本能的争夺资源:
共享资源被瓜分、两边
"
服务
"
都起不来了;
两边
"
服务
"
都起来了,但同时读写
"
共享存储
"
,导致数据损坏(常见如数据库轮询着的联机日
志出错)。
脑裂出现的原因?
高可用服务器各节点之间心跳线链路发生故障,导致无法正常通信。
因网卡及相关驱动坏了,
ip
配置及冲突问题(网卡直连)。
因心跳线间连接的设备故障(网卡及交换机)。
因仲裁的机器出问题(采用仲裁的方案)。
高可用服务器上开启了
iptables
防火墙阻挡了心跳消息传输。
高可用服务器上心跳网卡地址等信息配置不正确,导致发送心跳失败。
其他服务配置不当等原因,如心跳方式不同,心跳广插冲突、软件
Bug
等。
脑裂预防方案
6.3.1. 添加冗余的心跳线
[
即冗余通信的方法
]
同时用两条心跳线路 (
即心跳线也
HA)
,这样一条线路坏了,另一个还是好的,依然能传送心
跳消息,尽量减少"
脑裂
"
现象的发生几率。
6.3.2. 仲裁机制
当两个节点出现分歧时,由第3
方的仲裁者决定听谁的。这个仲裁者,可能是一个锁服务,一
个共享盘或者其它什么东西
6.3.3. Lease机制
6.3.4. 隔离(Fencing)
机制
共享存储fencing
:确保只有一个
Master
往共享存储中写数据。
客户端fencing
:确保只有一个
Master
可以响应客户端的请求。
Slave fencing:确保只有一个Master
可以向
Slave
下发命令
6.4. 容错性
容错的处理是保障分布式环境下相应系统的高可用或者健壮性,一个典型的案例就是对于缓存穿透 问题的解决方案。(如频繁的发送id=-1的请求, 每次穿透到DB, DB可能就会挂掉)
解决方案: 1. 临时存放null值 2. 使用布隆过滤器
6.5. 负载均衡
负载均衡:其关键在于使用多台集群服务器共同分担计算任务,把网络请求及计算分配到集群可用
的不同服务器节点上,从而达到高可用性及较好的用户操作体验。
负载均衡器有硬件解决方案,也有软件解决方案。硬件解决方案有著名的
F5
,软件有
LVS
、
HAProxy
、 Nginx等。
Nginx负载均衡策略:
1. 轮询 2. weight 3. ip_hash等
七、 分布式架构服务调用
和传统的单体架构相比,分布式多了一个远程服务之间的通信,不管是
soa
还是微服务,他们本
质上都是对于业务服务的提炼和复用。那么远程服务之间的调用才是实现分布式的关键因素。
7.1 HTTP应用协议通信框架
7.1.1. HttpURLConnection
java
原生
HttpURLConnection
是基于
http
协议的,支持
get
,
post
,
put
,
delete
等各种请求方
式,最常用的就是
get
和
post
7.1.2. Apache Common HttpClient
HttpClient
是
Apache Common
下的子项目,可以用来提供高效的、最新的、功能丰富的支持
HTTP
协议的客户端编程工具包,并且它支持
HTTP
协议最新的版本。
实现了所有
HTTP
的方法(
GET,POST,PUT,HEAD
等)
支持
HTTPS
协议
支持代理服务器等
7.1.3. OKhttp3
OKHttp
是一个当前主流的网络请求的开源框架
,
用于替代
HttpUrlConnection
和
Apache HttpClient
支持
http2.0
,对一台机器的请求共享一个
socket
。
采用连接池技术,可以有效的减少
Http
连接数量。
无缝集成
GZIP
压缩技术。
支持
Response Cache
,避免重复请求
域名多
IP
支持
7.1.4. RestTemplate
Spring RestTemplate
是
Spring
提供的用于访问
Rest
服务的客户端,
RestTemplate
提供了多
种便捷访问远程
Http
服务的方法,能够大大提高客户端的编写效率,所以很多客户端比如
Android
或者第三方服务商都是使用
RestTemplate
请求
restful
服务。
面向
URL
组件,必须依赖于主机
+
端口
+ URI
RestTemplate
不依赖于服务接口,仅关注
REST
响应内容
Spring Cloud Feign
7.2 RPC框架
7.2.1. Java RMI
Java RMI
(
Romote Method Invocation
)是一种基于
Java
的远程方法调用技术,是
Java
特有的一
种
RPC
实现。
7.2.2. Hessian
Hessian
是一个轻量级的
remoting onhttp
工具,使用简单的方法提供了
RMI
的功能
.
相比
WebService
,
Hessian
更简单、快捷。采用的是二进制
RPC
协议,因为采用的是二进制协议,所以
它很适合于发送二进制数据。
7.2.3. Dubbo
Dubbo
是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的
RPC
实现服
务的输出和输入功能,可以和
Spring
框架无缝集成。
Dubbo
是一款高性能、轻量级的开源
Java RPC
框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注
册和发现
7.2.4. gRPC
gRPC
是由
Google
公司开源的一款高性能的远程过程调用
(RPC)
框架,可以在任何环境下运行。该
框架提供了负载均衡,跟踪,智能监控,身份验证等功能,可以实现系统间的高效连接。
7.3 跨域调用
在分布式系统中
,
会有调用其他业务系统
,
导致出现跨域问题
,跨域实质上是浏览器的一种保护处
理。如果产生了跨域,服务器在返回结果时就会被浏览器拦截
(
注意:此时请求是可以正常发起的,只是 浏览器对其进行了拦截)
,导致响应的内容不可用
.
产生跨域的几种情况有一下
:
7.3.1 同源(
协议
,
域名
,
端口号 相同)
7.3.2 协议不同
(http/https)
7.3.3.
主域名不同
(home/baidu)
7.3.4 子域名不同
7.3.5 端口号不同
1.
使用
jsonp
解决网站跨域
缺点:不支持
post
请求,代码书写比较复杂
2.
使用
HttpClient
内部转发
3.
使用设置响应头允许跨域
response.setHeader(“Access-Control-Allow-Origin”, “*”);
设置响应头允许跨域
.
4.
基于
Nginx
搭建企业级
API
接口网关
5.
使用
Zuul
搭建微服务
API
接口网关
Zuul
是
spring cloud
中的微服务网关。网关: 是一个网络整体系统中的前置门户入口。请求首先通
过网关,进行路径的路由,定位到具体的服务节点上。可以使用
zuul
的过滤器的请求转发去解决跨
域问题
八、 分布式服务治理
分布式协调技术主要用来解决分布式环境当中多个进程之间的同步控制,让他们有序的去访问某
种临界资源,防止造成
"
脏数据
"
的后果。
分布式锁也就是我们
分布式协调技术
实现的核心内容。
8.1.1.
基于缓存(
Redis
等)实现分布式锁
获取锁的时候,使用
setnx
加锁,并使用
expire
命令为锁添加一个超时时间,超过该时间则自
动释放锁,锁的
value
值为一个随机生成的
UUID,
释放锁的时候进行判断。
获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。
释放锁的时候,通过
UUID
判断是不是该锁,若是该锁,则执行
delete
进行锁释放。
8.1.2. ZooKeeper
是一个为分布式应用提供一致性服务的开源组件,它内部是一个分层的文件系统目录树
结构,规定同一个目录下只能有一个唯一文件名
,
基于
ZooKeeper
实现分布式锁的步骤如下:
创建一个目录
mylock
线程
A
想获取锁就在
mylock
目录下创建临时顺序节点
获取
mylock
目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前
线程顺序号最小,获得锁
线程
B
获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点
线程
A
处理完,删除自己的节点,线程
B
监听到变更事件,判断自己是不是最小的节点,如果
是则获得锁
九、 服务削峰
为什么要削峰?
主要是还是来自于互联网的业务场景,例如,春节火车票抢购,大量的用户需要同一时间去抢购;
以及大家熟知的阿里双
11
秒杀, 短时间上亿的用户涌入,瞬间流量巨大(高并发)
.
削峰从本质上来说就是更多地延缓用户请求,以及层层过滤用户的访问需求,遵从
“
最后落地到数据库的请求数要尽量少”
的原则。
9.1.
消息队列解决削峰
要对流量进行削峰,最容易想到的解决方案就是用消息队列来缓冲瞬时流量,把同步的直接调用转
换成异步的间接推送,中间通过一个队列在一端承接瞬时的流量洪峰,在另一端平滑地将消息推送
出去。
在这里,消息队列就像
“
水库
”
一样,拦截上游的洪水,削减进入下游河道的洪峰流量,从而达
到减免洪水灾害的目的。
9.2.
流量削峰漏斗:层层削峰
分层过滤其实就是采用
“
漏斗
”
式设计来处理请求的,这样就像漏斗一样,尽量把数据量和请求量一
层一层地过滤和减少了。
9.2.1 分层过滤的核心思想
通过在不同的层次尽可能地过滤掉无效请求。
通过
CDN
过滤掉大量的图片,静态资源的请求。
再通过类似
Redis
这样的分布式缓存过滤请求
9.2.2 分层过滤的基本原则
对写数据进行基于时间的合理分片,过滤掉过期的失效请求。
对写请求做限流保护,将超出系统承载能力的请求过滤掉。
涉及到的读数据不做强一致性校验,减少因为一致性校验产生瓶颈的问题。
对写数据进行强一致性校验,只保留最后有效的数据。
十、服务降级
当服务器压力剧增的情况下,根据实际业务情况及流量,对一些服务和页面有策略的不处理或换 种简单的方式处理,从而释放服务器资源以保证核心服务正常运作或高效运作
整个架构整体的负载超出了预设的上限阈值或即将到来的流量预计将会超过预设的阈值时,为了保
证重要或基本的服务能正常运行,我们可以将一些 不重要 或 不紧急 的服务或任务进行服务的
延迟使用 或 暂停使用
降级处理方案:
页面降级
——
可视化界面禁用点击按钮、调整静态页面
延迟服务
——
如定时任务延迟处理、消息入
MQ
后延迟处理
写降级
——
直接禁止相关写操作的服务请求
读降级
——
直接禁止相关读的服务请求
缓存降级
——
使用缓存方式来降级部分读频繁的服务接口
后端处理措施:
抛异常
返回
NULL
调用
Mock
数据
调用
Fallback
处理逻辑
十一、 服务限流
限流的目的是通过对并发访问请求进行限速或者一个时间窗口内的的请求数量进行限速来保护系
统,一旦达到限制速率则可以拒绝服务、排队或等待
实施:
1. 客户端防止重点击校验
2. Nginx可用限制单位时间内的请求数和 同一时间内连接数
3. tomcat 可用配置最大连接数, 配置请求处理队列等
4. 接口层通过限制访问速率来控制接口的并发请求
限流算法:
1. 固定窗口,每秒钟或分钟固定次数。 问题:在59秒访问100次,在第二分钟第一秒100次, 2秒后将造成200次请求压力, 形成流量突刺
2. 滑动窗口:
时间窗口
划分的越细,滑动窗口的滚动就越平滑,限流的效果就会越精确
3.
漏桶( 固定大小
FIFO
队列
+定时取队列元素)
优点是可以削峰填谷,不论请求多大多快,都只会匀速发给后端,不会出现突刺现象,保证下游服
务正常运行
,
缺点就是在桶队列中的请求会排队,响应时间拉长
4. 令牌桶
令牌桶算法是以一个恒定的速度往桶里放置令牌(如果桶里的令牌满了就废弃),每进来一个请求
去桶里找令牌,有的话就拿走令牌继续处理,没有就拒绝请求
令牌桶的优点是可以应对突发流量,当桶里有令牌时请求可以快速的响应,也不会产生漏桶队列中
的等待时间
,
缺点就是相对漏桶一定程度上减小了对下游服务的保护
十二、 服务熔断
问题: A(上),B(中),C(下游)三个服务,当下游服务变得不可用堆压大量请求时, 由于他们是调用链关系, 服务B也将不可用, 在导致A不可用, 雪崩的现象
Spring Cloud Hystrix
是基于
Netflix
的开源框架
Hystrix
实现,该框架实现了服务熔断、线程隔离等
一系列服务保护功能。
熔断的三种状态: 熔断
关闭状态, 熔断
开启状态, 半熔断
状态
十三、 架构设计的基本原则:
13.1 开闭原则:
软件实体应当对扩展开放,对修改关闭,这就是开闭原则的经典定义。
含义: 指在不改变软件实体的源代码前提下, 可用通过扩展模块的功能, 满足需求
实现方法:
可以通过
“
抽象约束、封装变化
”
来实现开闭原则,即通过接口或者抽象类为软件实体定义一个相对
稳定的抽象层,而将相同的可变因素封装在相同的具体实现类中。
案例: 餐馆的菜单==> 首先我们简单的接口 Imenu ==> id, name, peice ChineseMenu, JapaneseMenu 实现IMenu , 如果这时日式餐单要进行折扣活动, 那么我们有三种方案:
1. 在Imenu添加折扣方法, 所有实现类都将实现折扣。
2. 直接修改日式餐单, 这肯定是不推荐
3. 扩展一个打折类继承日式餐单。
13.2 单一职责
单一职责原则又称单一功能原则,这里的职责是指类变化的原因,单一职责原则规定一个类应该有
且仅有一个引起它变化的原因,否则类应该被拆分。
单一职责解决了什么?
1.
一个职责的变化可能会削弱或者抑制这个类实现其他职责的能力;
2.
当客户端需要该对象的某一个职责时,不得不将其他不需要的职责全都包含进来,从而造成冗余代 码或代码的浪费。
单一职责原则的核心就是控制类的粒度大小、将对象解耦、提高其内聚性。如果遵循单一职责原则将有以下优点:
1.
降低类的复杂度
2.
提高类的可读性
3.
提高系统的可维护性
4.
变更引起的风险降低
实现: 其实就是把一个业务中的职责划分开, 必须学生工作管理:
分为: 生活辅导, 学业指导从而实现了学生工作的拆分
13.3 接口隔离原则
接口隔离原则要求程序员尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客
户感兴趣的方法。
实现案例:
学生成绩管理程序学
,
学生成绩管理程序一般包含插入成绩、删除成绩、修改成绩、计算总分、计算均分、打印成绩信息、査询成绩信息等功能
我们可以进行: 输出模块, 统计模块, 打印模块的拆分
13.4 里氏替换原则
里氏替换原则主要阐述了有关继承的一些原则。里氏替换原则是继承复用的基础,它反映了基类与
子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范。总结
:
子类可以扩展父类的
功能,但不能改变父类原有的功能
1.
里氏替换原则是实现开闭原则的重要方式之一。
2.
它克服了继承中重写父类造成的可复用性变差的缺点。
3.
它是动作正确性的保证。即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性。
4.
加强程序的健壮性,同时变更时可以做到非常好的兼容性,提高程序的维护性、可扩展性,降低需 求变更时引入的风险。
13.5 依赖倒置原则
依赖倒置原则的原始定义为:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依 赖细节,细节应该依赖抽象。其核心思想是:要面向接口编程,不要面向实现编程
。
1.
可以降低类间的耦合性。
2.
可以提高系统的稳定性。
3.
可以减少并行开发引起的风险。
4.
可以提高代码的可读性和可维护性。
实现方法: 顾客类
”
与
“
商店类
”
的关系
class Customer {
public void shopping(ShaoguanShop shop) {
//购物
System.out.println(shop.sell());
}
}
// 该方法如果该顾客想从另外一家商店(如婺源网店 WuyuanShop)购物,就要将该顾客的代码修改如下:
class Customer {
public void shopping(WuyuanShop shop) {
//购物
System.out.println(shop.sell());
}
}
解决:
class Customer {
public void shopping(Shop shop) {
//购物
System.out.println(shop.sell());
}
}
13.6 迪米特法则
1.
从依赖者的角度来说,只依赖应该依赖的对象。
2.
从被依赖者的角度说,只暴露应该暴露的方法。
案例
:
明星与经纪人的关系实例
分析:明星由于全身心投入艺术,所以许多日常事务由经纪人负责处理,如与粉丝的见面会,与媒
体公司的业务洽淡等。这里的经纪人是明星的朋友,而粉丝和媒体公司是陌生人,所以适合使用迪米特法则
实现: 明显
13.7 合成复用原则
合成复用原则(
Composite Reuse Principle
,
CRP
)又叫组合
/
聚合复用原则
(
Composition/Aggregate Reuse Principle
,
CARP
)。它要求在软件复用时,要尽量先使用组合或者 聚合等关联关系来实现,其次才考虑使用继承关系来实现。
如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的,
两者都是开闭原则的具体实现规范。
1.
它维持了类的封装性
2.
新旧类之间的耦合度低
3.
复用的灵活性高
实现:
汽车按
“
动力源
”
划分可分为汽油汽车、电动汽车等;按
“
颜色
”
划分可分为白色汽车、黑色汽车
和红色汽车等。如果同时考虑这两种分类,其组合就很多
如何是继承复用, 那么将导致, 汽油下有很多颜色子类, 电动下也有, 这时我们需要把颜色抽取出来单独做一个类体系, 最后把颜色类, 注入代汽车里。