适配器模式与桥接模式
适配器模式与桥接模式
参考教程:https://www.bilibili.com/video/BV1G4411c7N4
代码实现 Github:https://github.com/yaokuku123/pattern
适配器模式
概述
- 案例
现实生活中的适配器例子: 泰国插座用的是两孔的(欧标),可以买个多功能转换插头 (适配器) ,这样就可以使用了。
3-1592363751050.png- 适配器模式
解释:将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。
分类:类适配器模式、对象适配器模式、接口适配器模式
类适配器模式
- 案例
以生活中充电器的例子来说明适配器,充电器本身相当于Adapter,220V交流电相当于src (即被适配者),我们的目dst(即目标)是5V直流电。
4-1592364040787.png- 类适配器
基本介绍:Adapter类,通过继承 src类,实现 dst 类接口,完成src->dst的适配。
- 类适配器代码
package com.yqj.pattern.adapter.classadapter;
//被适配的类
class Voltage220V{
public int output220V(){
int src = 220;
System.out.println("输出电压="+src+"V");
return src;
}
}
//适配器接口
interface Voltage5V{
int output5V();
}
//类适配器
class VoltageAdapter extends Voltage220V implements Voltage5V{
//实现适配,将220V电压转换为5V电压
@Override
public int output5V() {
//调用父类的方法
int src = output220V();
//转换电压
int dst = src / 44;
return dst;
}
}
//手机
class Phone{
public void charging(Voltage5V voltage5V){
if(voltage5V.output5V() == 5){
System.out.println("可以充电 电压5V");
}else {
System.out.println("不可充电");
}
}
}
//用户
public class Client {
public static void main(String[] args) {
Phone phone = new Phone();
phone.charging(new VoltageAdapter());
}
}
- 小结
- Java是单继承机制,所以类适配器需要继承src类这一点算是一个缺点, 因为这要求dst必须是接口,有一定局限性。
- src类的方法在Adapter中都会暴露出来,也增加了使用的成本。
- 由于其继承了src类,所以它可以根据需求重写src类的方法,使得Adapter的灵活性增强了。
对象适配器模式
- 案例
以生活中充电器的例子来说明适配器,充电器本身相当于Adapter,220V交流电相当于src (即被适配者),我们的目dst(即目标)是5V直流电,使用对象适配器模式完成。
- 对象适配器
基本思路和类的适配器模式相同,只是将Adapter类作修改,不是继承src类,而 是持有src类的实例,以解决兼容性的问题。 即:持有 src类,实现 dst 类接口, 完成src->dst的适配。
- 对象适配器代码
package com.yqj.pattern.adapter.objectadapter;
//被适配的类
class Voltage220V{
public int output220V(){
int src = 220;
System.out.println("输出电压="+src+"V");
return src;
}
}
//适配器接口
interface Voltage5V{
int output5V();
}
//对象适配器
class VoltageAdapter implements Voltage5V{
Voltage220V voltage220V;
//聚合被适配对象
public VoltageAdapter(Voltage220V voltage220V) {
this.voltage220V = voltage220V;
}
//实现适配,将220V电压转换为5V电压
@Override
public int output5V() {
int src = voltage220V.output220V();
//转换电压
int dst = src / 44;
return dst;
}
}
//手机
class Phone{
public void charging(Voltage5V voltage5V){
if(voltage5V.output5V() == 5){
System.out.println("可以充电 电压5V");
}else {
System.out.println("不可充电");
}
}
}
//用户
public class Client {
public static void main(String[] args) {
Phone phone = new Phone();
phone.charging(new VoltageAdapter(new Voltage220V()));
}
}
- 小结
- 对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。 根据合成复用原则,使用组合替代继承, 所以它解决了类适配器必须继承src的局限性问题,也不再要求dst必须是接口。
- 使用成本更低,更灵活。
接口适配器模式
- 案例
生活上,可以吃饭,唱歌,看电影。但有时我们不想同时做这三件事情,仅想吃饭。
- 接口适配器
当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求。
- 接口适配器代码
package com.yqj.pattern.adapter.abstractadapter;
interface Life{
void eat();
void sing();
void movie();
}
abstract class AbsAdapter implements Life{
@Override
public void eat() {
}
@Override
public void sing() {
}
@Override
public void movie() {
}
}
public class Client {
public static void main(String[] args) {
AbsAdapter adapter = new AbsAdapter(){
@Override
public void eat() {
System.out.println("吃顿好的");
}
};
adapter.eat();
}
}
桥接模式
- 案例
- 传统方法
分析:
- 扩展性问题(类爆炸),如果我们再增加手机的样式(旋转式),就需要增加各个品牌手机的类,同样如果我们增加一个手机品牌,也要在各个手机样式类下增加。
- 违反了单一职责原则,当我们增加手机样式时,要同时增加所有品牌的手机,这 样增加了代码维护成本。
- 解决方案-使用桥接模式。
- 桥接模式
解释:将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变。Bridge模式基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现 (Implementation)分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展。
用途:对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
- 改进代码
package com.yqj.pattern.bridge;
//品牌,行为实现类的接口
interface Brand{
void open();
void call();
void close();
}
//具体品牌类1,具体的行为实现类
class Vivo implements Brand{
@Override
public void open() {
System.out.println("Vivo开机");
}
@Override
public void call() {
System.out.println("Vivo打电话");
}
@Override
public void close() {
System.out.println("Vivo关机");
}
}
//具体品牌类2,具体的行为实现类
class XiaoMi implements Brand{
@Override
public void open() {
System.out.println("小米开机");
}
@Override
public void call() {
System.out.println("小米打电话");
}
@Override
public void close() {
System.out.println("小米关机");
}
}
//桥接类
abstract class Phone{
Brand brand;
public Phone(Brand brand) {
this.brand = brand;
}
protected void open(){
brand.open();
}
protected void call(){
brand.call();
}
protected void close(){
brand.close();
}
}
//样式1,抽象类的子类
class FoldedPhone extends Phone{
public FoldedPhone(Brand brand) {
super(brand);
}
public void open(){
super.open();
System.out.println("折叠样式手机");
}
public void call(){
super.call();
System.out.println("折叠样式手机");
}
public void close(){
super.close();
System.out.println("折叠样式手机");
}
}
//样式2,抽象类的子类
class UpRightPhone extends Phone{
public UpRightPhone(Brand brand) {
super(brand);
}
public void open(){
super.open();
System.out.println("直立样式手机");
}
public void call(){
super.call();
System.out.println("直立样式手机");
}
public void close(){
super.close();
System.out.println("直立样式手机");
}
}
//用户
public class Client{
public static void main(String[] args) {
Phone phone = new FoldedPhone(new XiaoMi());
phone.open();
phone.call();
phone.close();
System.out.println("+++++++++++++");
UpRightPhone phone2 = new UpRightPhone(new Vivo());
phone2.open();
phone2.call();
phone2.close();
}
}
- 小结
- 实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实 现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统。
- 对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部 分由具体业务来完成。
- 桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本。
- 桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层, 要求开发者针对抽象进行设计和编程。
- 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围有一定的局 限性,即需要有这样的应用场景。