1.适配器设计模式
适配器模式(Adapter Pattern)
- ⻅名知意,是作为两个不兼容的接⼝之间的桥梁,属于结构型模式
- 适配器模式使得原本由于接⼝不兼容⽽不能⼀起⼯作的那些类可以⼀起⼯作
常⻅的⼏类适配器
类的适配器模式
- 想将⼀个类转换成满⾜另⼀个新接⼝的类时,可以使⽤类的适配器模式,创建⼀个新类,继承原有的类,实现新的接⼝即可
对象的适配器模式
- 想将⼀个对象转换成满⾜另⼀个新接⼝的对象时,可以创建⼀个适配器类,持有原类的⼀个实例,在适配器类的⽅法中,调⽤实例的⽅法就⾏
接⼝的适配器模式
- 不想实现⼀个接⼝中所有的⽅法时,可以创建⼀个Adapter,实现所有⽅法,在写别的类的时候,继承Adapter类即可
- 应⽤场景
- 电脑需要读取内存卡的数据,读卡器就是适配器⽇常使⽤的转换头,如电源转换头,电压转换头
- 系统需要使⽤现有的类,⽽这些类的接⼝不符合系统的需要
- JDK中InputStreamReader就是适配器
- JDBC就是我们⽤的最多的适配器模式
JDBC给出⼀个客户端通⽤的抽象接⼝,每⼀个具体数据库⼚商 如 SQL Server、Oracle、MySQL等,就会开发JDBC驱动,就是⼀个介于JDBC接⼝和数据库引擎接⼝之间的适配器软件

接口适配器
有些接⼝中有多个抽象⽅法,当我们写该接⼝的实现类时,必须 实现该接⼝的所有⽅法,这明显有时⽐较浪费,因为并不是所有 的⽅法都是我们需要的,有时只需要实现部分接⼝就可以了
public interface PayGateway {/*** 下单*/void unifiedorder();/*** 退款*/void refund();/*** 查询支付状态*/void query();/*** 发红包*/void sendRedPack();}
public class PayGatewayAdapter implements PayGateway {@Overridepublic void unifiedorder() {}@Overridepublic void refund() {}@Overridepublic void query() {}@Overridepublic void sendRedPack() {}}
public class ProductVideoOrder extends PayGatewayAdapter {@Overridepublic void unifiedorder() {System.out.println("ProductVideoOrder unifiedorder");}@Overridepublic void refund() {System.out.println("ProductVideoOrder refund");}}
⽣产环境接⼝-需要兼容新的业务怎么办?
- 需求背景
⼩滴课堂⾥⾯有个电商⽀付项⽬,⾥⾯有个登录功能,已经线上运⾏了 ,客户端A 调⽤⽣产环境的登录接⼝B,且线上稳定运⾏了好⼏年。
某天,公司接到收购了别的公司的项⽬,需要把这套系统部署在 起来,且收购的项⽬也有对应的客户端C,但是两个客户端和服务端的协议不⼀样
需求:收购的项⽬客户端C,需要做公司原来的项⽬⽤户数据打 通,连接公司的服务端登录接⼝B,你能想到⼏个解决⽅案?
- 1、修改就项⽬B的登录接⼝,兼容C客户端协议(可能影响线上接⼝,不稳定)
- 2、新增全新的登录接⼝F,采⽤C客户端协议(和旧的业务逻辑会重复)
- 3、新增⼀个转换协议接⼝,客户端C调⽤旧的B接⼝之前,使⽤转换接⼝转换下协议(适配器模式,推荐这个⽅式)

总结
- 在使⽤⼀些旧系统或者是类库时,经常会出现接⼝不兼 容的问题,适配器模式在解决这类问题具有优势
- 学习设计模式⼀定不要局限代码层⾯,要从软件系统整体去考虑,⽽不是为了使⽤设计模式,⽽去使⽤设计模式
优点
- 可以让任何两个没有关联的类⼀起运⾏,使得原本由于接⼝不兼容⽽不能⼀起⼯作的那些类可以⼀起⼯作
- 增加灵活度, 提⾼复⽤性,适配器类可以在多个系统使⽤,符合开闭原则
缺点
- 整体类的调⽤链路增加,本来A可以直接调⽤C,使⽤适配器后 是A调⽤B,B再调⽤C
类的适配器
- 类的适配器模式
- 想将⼀个类转换成满⾜另⼀个新接⼝的类时,可以使⽤ 类的适配器模式,创建⼀个新类,继承原有的类,实现新的接⼝即可

public class OldModule {public void methodA(){System.out.println("OldModule methodA");}}
public interface TargetModule {/*** 和需要适配的类方法名一样*/void methodA();/*** 新的方法,如果有多个新的方法直接编写就行*/void methodB();void methodC();}
public class Adapter extends OldModule implements TargetModule {/*** 新的方法,和老的类方法不一样*/@Overridepublic void methodB() {System.out.println("Adapter methodB");}/*** 新的方法,和老的类方法不一样*/@Overridepublic void methodC() {System.out.println("Adapter methodC");}}
2.桥接设计模式
⽜逼的⼯程师,在⽇常开发,明明⽤了某种设计模式了, 但他还没意识到是哪种模式,只知道这样做是最合适的, 这种就是已经把设计模式融会贯通了。 ⽽那种刚学了⼀点设计模式就到处想⽤,以及把精⼒浪费在区分各个设计模式上的⼈,未来就陷⼊死循环⾥⾯。
桥接设计模式
- 适配器模式类似,包括以后经常会遇到意思接近⼀样的设计模式,因为⼤神往往就是多个模式混⽤,且根据不 同的场景进⾏搭配,桥接设计模式也是结构型模式
- 将抽象部分与实现部分分离,使它们都可以独⽴的变化
- 通俗来说,是通过组合来桥接其它的⾏为/维度
应⽤场景
- 系统需要在构件的抽象化⻆⾊和具体化⻆⾊之间增加更多的灵活性
- 不想使⽤继承导致系统类的个数急剧增加的系统
- 有时候⼀个类,可能会拥有多个变化维度,⽐如啤酒, 有不同的容量和品牌,可以使⽤继承组合的⽅式进⾏开发,假如维度很多,就容易出现类的膨胀,使⽤桥接模
式就可以解决这个问题,且解耦
- 业务背景
我们需要构建⼀个⼿机类,我们知道⼿机有很多品牌,苹果、华 为等,从另外⼀个颜⾊维度,⼜有多种颜⾊,红、⻩、蓝等, 那如果描述这些类的话,传统⽅式就直接通过继承,就需要特别 多的类,品牌2,颜⾊3,就是6个类了, 如果后续再增加品牌就 更多了,类数⽬将会激增,即所谓的类爆炸
使⽤桥接模式就可以解决这个问题,且灵活度⼤⼤提⾼


public abstract class Phone {/*** 通过组合的方式来桥接其他行为*/protected Color color;public void setColor(Color color) {this.color = color;}/*** 手机的方法*/abstract public void run();}
public interface Color {void useColor();}
public class RedColor implements Color {@Overridepublic void useColor() {System.out.println("红色");}}public class YellowColor implements Color {@Overridepublic void useColor() {System.out.println("黄色");}}public class BlueColor implements Color {@Overridepublic void useColor() {System.out.println("蓝色");}}
public class HWPhone extends Phone {public HWPhone(Color color){super.setColor(color);}@Overridepublic void run() {color.useColor();System.out.println("华为手机");}}
public class SXPhone extends Phone {public SXPhone(Color color){super.setColor(color);}@Overridepublic void run() {color.useColor();System.out.println("三星手机");}}
- 使用 ```java public static void main(String[] args) {
HWPhone blueHwPhone = new HWPhone(new BlueColor());blueHwPhone.run();HWPhone redHwPhone = new HWPhone(new RedColor());redHwPhone.run();ApplePhone applePhone = new SXPhone(new RedColor());applePhone.run();
}
- 优点- 抽象和实现的分离。- 优秀的扩展能⼒,符合开闭原则- 缺点- 增加系统的理解与设计难度- 使⽤聚合关联关系建⽴在抽象层,要求开发者针对抽象进⾏设计与编程,⽐如抽象类汽⻋,⾥⾯聚合了颜⾊类,有点像对象适配器- 总结和对⽐- 按GOF的说法,桥接模式和适配器模式⽤于设计的不同 阶段,- 桥接模式⽤于设计的前期,精细化的设计,让系统更加灵活- 适配器模式⽤于设计完成之后,发现类、接⼝之间⽆法⼀起⼯作,需要进⾏填坑- 适配器模式经常⽤在第三⽅API协同⼯作的场合,在功能集成需求越来越多的今天,这种模式的使⽤频度越来越⾼,包括有些同学听过 外观设计模式 ,这个也是某些场景和适配器模式⼀样<a name="zxkpR"></a># 3.组合设计模式- 组合设计模式- ⼜叫部分整体模式,将对象组合成树形结构以表示“部分-整体”的层次结构,可以更好的实现管理操作- 组合模式使得⽤户可以使⽤⼀致的⽅法操作单个对象和组合对象- 部分-整体对象的基本操作多数是⼀样的,但是应该还 会有不⼀样的地⽅- 核⼼:组合模式可以使⽤⼀棵树来表示- 应⽤场景- 银⾏总⾏,总⾏有前台、后勤、⽹络部⻔等,辖区下还有地⽅分⾏,也有前台、后勤、⽹络部⻔,最⼩的分⾏ 就没有⼦分⾏了- 公司也是,总公司下有⼦公司,每个公司⼤部分的部⻔都类似- ⽂件夹和⽂件,都有增加、删除等api,也有层级管理关系- 当想表达对象的部分-整体的层次结构- 当我们的要处理的对象可以⽣成⼀颗树形结构,我们要 对树上的节点和叶⼦进⾏操作时,它能够提供⼀致的⽅式,⽽不⽤考虑它是节点还是叶⼦- ⻆⾊- 组合部件(Component):它是⼀个抽象接⼝,表示树根,例⼦:总⾏- 合成部件(Composite):和组合部件类似,也有⾃⼰ 的⼦节点,例⼦:总⾏下的分⾏- 叶⼦(Leaf):在组合中表示⼦节点对象,注意是没有⼦节点,例⼦:最⼩地⽅的分⾏```java/**根节点,抽象类,通用的属性和方法*/public abstract class Root {private String name;public Root(String name){this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}public abstract void addFile(Root root);public abstract void removeFile(Root root);public abstract void display(int depth);}
/*** 具体的文件夹,里面可以添加子文件夹或者文件**/public class Folder extends Root {List<Root> folders = new ArrayList<>();public Folder(String name){super(name);}public List<Root> getFolders() {return folders;}public void setFolders(List<Root> folders) {this.folders = folders;}@Overridepublic void addFile(Root root) {folders.add(root);}@Overridepublic void removeFile(Root root) {folders.remove(root);}@Overridepublic void display(int depth) {StringBuilder sb = new StringBuilder();for(int i=0; i<depth;i++){sb.append("-");}//打印横线和当前文件名System.out.println(sb.toString()+this.getName());for(Root r : folders){//每个下级,横线多2个r.display(depth+2);}}}
/** 这个类是没有节点,不用存储其他子类数组,所以是叶子节点*/public class File extends Root{public File(String name){super(name);}@Overridepublic void addFile(Root root) {}@Overridepublic void removeFile(Root root) {}@Overridepublic void display(int depth) {StringBuilder sb = new StringBuilder();for(int i=0; i<depth;i++){sb.append("-");}//打印横线和当前文件名System.out.println(sb.toString()+this.getName());}}
public static void main(String[] args) {//创造根文件夹Root root1 = new Folder("C://");//建立子文件Root desktop = new Folder("桌面");Root myComputer = new Folder("我的电脑");//建立子文件Root javaFile = new File("HelloWorld.java");//建立文件夹关系root1.addFile(desktop);root1.addFile(myComputer);//建立文件关系myComputer.addFile(javaFile);//从0级开始展示,每下一级,多2条横线root1.display(0);//另外一个根Root root2 = new Folder("D://");root2.display(0);}

4.装饰器设计模式
- 装饰器设计模式(Decorator Pattern)
- 也叫包装设计模式,属于结构型模式,它是作为现有的类的⼀个包装,允许向⼀个现有的对象添加新的功能, 同时⼜不改变其结构
- 给对象增加功能,⼀般两种⽅式 继承或关联组合,将⼀个类的对象嵌⼊另⼀个对象中,由另⼀个对象来决定是 否调⽤嵌⼊对象的⾏为来增强功能,这个就是装饰器模
式,⽐继承模式更加灵活
- 应⽤场景
- ⽼王,本来计划买跑⻋撩妹的,结果⼝袋没钱,改买⾃⾏⻋,为了显得突出,店家提供多种改装⽅案,加个⼤的喇叭、加个防爆胎等,经过装饰之后成为
⽬的更明确的⾃⾏⻋,更能解决问题。像这种不断为对象添加装饰的模式就叫 Decorator 模式,Decorator 指的是装饰物。

- 以动态、透明的⽅式给单个对象添加职责,但⼜能不改 变其结构
- JDK源码⾥⾯应⽤的最多的就是IO流,⼤量使⽤装饰设计模式

⻆⾊(装饰者和被装饰者有相同的超类(Component))
抽象组件(Component)
- 定义装饰⽅法的规范,最初的⾃⾏⻋,仅仅定义了⾃⾏⻋的API;
被装饰者(ConcreteComponent)
- Component的具体实现,也就是我们要装饰的具体对象
- 实现了核⼼⻆⾊的具体⾃⾏⻋
装饰者组件(Decorator)
- 定义具体装饰者的⾏为规范, 和Component⻆⾊有相同的接⼝,持有组件(Component)对象的实例引⽤
- ⾃⾏⻋组件 都有 名称和价格
具体装饰物(ConcreteDecorator)
- 负责给构件对象装饰附加的功能 ⽐如 喇叭,防爆胎
public interface Bike {String getDescription();int getPrice();}public class BigBike implements Bike{private String description = "大号自行车";@Overridepublic String getDescription() {return description;}/*** 200元是大号自行车的价格* @return*/@Overridepublic int getPrice() {return 200;}}public class SmallBike implements Bike{private String description = "小号自行车";@Overridepublic String getDescription() {return description;}/*** 100元是小号自行车的价格* @return*/@Overridepublic int getPrice() {return 200;}}
//装饰者组件public class BikeDecorator implements Bike {private String description = "我只是装饰器,啥都不表示,子类帮我传递";@Overridepublic String getDescription() {return description;}@Overridepublic int getPrice() {return 0;}}//具体装饰者 RSC 是防爆胎的缩写,角色 ConcreteDecoratorpublic class RSCBikeDecorator extends BikeDecorator {private String description = "增加一个防爆胎";private Bike bike;public RSCBikeDecorator(Bike bike){this.bike = bike;}@Overridepublic String getDescription() {return bike.getDescription() + ","+ description;}/*** 100 是防爆胎的价格* @return*/@Overridepublic int getPrice() {return bike.getPrice() + 100;}}//具体装饰者 喇叭,角色 ConcreteDecoratorpublic class SuonaBikeDecorator extends BikeDecorator {private String description = "增加一个喇叭";private Bike bike;public SuonaBikeDecorator(Bike bike){this.bike = bike;}@Overridepublic String getDescription() {return bike.getDescription() + ","+ description;}/*** 50 是唢呐喇叭的价格* @return*/@Overridepublic int getPrice() {return bike.getPrice() + 50;}}
public static void main(String[] args) {/*** 大自行车 200* 小自行车 100* 防爆胎 100* 喇叭 50*///选个自行车Bike bike = new BigBike();//搭配了2个RSC防爆胎bike = new RSCBikeDecorator(bike);bike = new RSCBikeDecorator(bike);//搭配了2个喇叭bike = new SuonaBikeDecorator(bike);bike = new SuonaBikeDecorator(bike);System.out.println(bike.getDescription()+", 价格:"+bike.getPrice());}
JDK源码⾥⾯的Stream IO流-装饰器设计 模式应⽤

- 应用场景
InputStream inputStream = new BufferedInputStream(new FileInputStream(""));
5.代理设计模式
- 代理设计模式(Proxy Pattern)
- 为其他对象提供⼀种代理以控制对这个对象的访问,属 于结构型模式。
- 客户端并不直接调⽤实际的对象,⽽是通过调⽤代理, 来间接的调⽤实际的对象
应⽤场景
- 各⼤数码专营店,代理⼚商进⾏销售对应的产品,代理 商持有真正的授权代理书
- 客户端不想直接访问实际的对象,或者访问实际的对象存在困难,通过⼀个代理对象来完成间接的访问
- 想在访问⼀个类时做⼀些控制,或者增强功能
⻆⾊
- Subject: 抽象接⼝,真实对象和代理对象都要实现的⼀个 抽象接⼝,好⽐销售数码产品
- Proxy: 包含了对真实对象的引⽤,从⽽可以随意的操作 真实对象的⽅法,好⽐ 代理加盟店
- RealProject :真实对象,好⽐⼚商销售数码产品

- 业务需求
⽼王,想开个数码⼩卖部,为以后退休⽣活做准备, 代理各⼤⼚商的⼿机和电脑,⽤代理设计模式帮他实现下 Subject 卖⼿机 RealProject 苹果、华为⼚商,核⼼是卖⼿机,但是选址不熟 悉 Proxy ⽼王数码专卖店:代理卖⼿机,附加选地址,增加⼴告 等
/*** 抽取公共的方法*/public interface DigitalSell {void sell();}
// 真实的对象public class DigitalSellReal implements DigitalSell{@Overridepublic void sell() {System.out.println("销售华为手机");}}
public class DigitalSellProxy implements DigitalSell {private DigitalSell realObj = new DigitalSellReal();@Overridepublic void sell() {makeAddress();realObj.sell();makeAD();}private void makeAddress(){System.out.println("一个人流量很高的地址");}private void makeAD(){System.out.println("投放广告");}}
6.外观设计模式
外观设计模式 Facade Pattern
- ⻔⾯模式,隐藏系统的复杂性,并向客户端提供了⼀个 客户端可以访问系统的接⼝
- 定义了⼀个⾼层接⼝,这个接⼝使得这系统更加容易使⽤
应⽤场景
- 在外⼈看来,⼩滴课堂-⽼王是负责消息推送这个⼯ 作,看起来很轻松,但他们不知道⾥⾯有多复杂,⽼王加班多久才输出⼀个统⼀的接⼝,只要轻松操作就可以完成复杂的事情
- 开发⾥⾯MVC三层架构,在数据访问层和业务逻辑层、 业务逻辑层和表示层的层与层之间使⽤interface接⼝进 ⾏交互,不⽤担⼼内部逻辑,降低耦合性
- 各种第三⽅SDK⼤多会使⽤外观模式,通过⼀个外观 类,也就是整个系统的接⼝只有⼀个统⼀的⾼层接⼝, 这对⽤户屏蔽很多实现细节,外观模式经常⽤在封装 API的常⽤⼿段
- 对于复杂难以维护的⽼系统进⾏拓展,可以使⽤外观设 计模式
- 需要对⼀个复杂的模块或⼦系统提供⼀个外界访问的接 ⼝,外界对⼦系统的访问只要⿊盒操作
- 角色
- 外观⻆⾊(Facade):客户端可以调⽤这个⻆⾊的⽅法, 这个外观⽅法知道多个⼦系统的功能和实际调⽤
- ⼦系统⻆⾊(SubSystem):每个⼦系统都可以被客户端 直接调⽤,⼦系统并不知道⻔⾯的存在,

7.享元设计模式
享元设计模式(Flyweight Pattern)
- 属于结构型模式,主要⽤于减少创建对象的数量,以减 少内存占⽤和提⾼性能, 它提供了减少对象数量从⽽改 善应⽤所需的对象结构的⽅式。
- 享元模式尝试重⽤现有的同类对象,如果未找到匹配的对象,则创建新对象
应⽤场景
- JAVA 中的 String,如果字符串常量池⾥有则返回,如 果没有则创建⼀个字符串保存在字符串常量池⾥⾯
- 数据库连接池、线程池等
- 如果系统有⼤量相似对象,或者需要⽤需要缓冲池的时候可以使⽤享元设计模式,也就是⼤家说的池化技术
- 如果发现某个对象的⽣成了⼤量细粒度的实例,并且这 些实例除了⼏个参数外基本是相同的,如果把那些共享 参数移到类外⾯,在⽅法调⽤时将他们传递进来,就可
以通过共享对象,减少实例的个数
- 内部状态
- 不会随环境的改变⽽有所不同,是可以共享的
外部状态
- 不可以共享的,它随环境的改变⽽改变的,因此外部状态是由客户端来保持(因为环境的变化⼀般是由客户端 引起的)
⻆⾊
- 抽象享元⻆⾊:为具体享元⻆⾊规定了必须实现的⽅法,⽽外部状态就是以参数的形式通过此⽅法传⼊
- 具体享元⻆⾊:实现抽象⻆⾊规定的⽅法。如果存在内 部状态,就负责为内部状态提供存储空间。
- 享元⼯⼚⻆⾊:负责创建和管理享元⻆⾊。要想达到共享的⽬的,这个⻆⾊的实现是关键
- 客户端⻆⾊:维护对所有享元对象的引⽤,⽽且还需要存储对应的外部状态

案例实战
⽼王为了增加收⼊,开始接了外包项⽬,开发了⼀个 AI⽹站模板,可以根据不同的客户需求⾃动⽣成不同类型的⽹站 电商类、企业产品展示、信息流等。 在部署的时候就麻烦了,是不是每个机器都⽤租⽤云服务器,购买独⽴域名呢 这些⽹站结构相似度很⾼,⽽且都不是⾼访问量⽹站,可以先公⽤服务器资源,减少服务器资源成本,类似虚拟机或者Docker
public abstract class CloudWebSite {public abstract void run(Company company);}
public class ConcreteWebSite extends CloudWebSite {private String category;public ConcreteWebSite(String category){this.category = category;}@Overridepublic void run(Company company) {System.out.println("网站分类:"+category+", 公司:"+company.getName());}}
public class Company {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public Company(){}public Company(String name){this.name = name;}}
public class WebSiteFactory {/*** map里面的key是分类*/private Map<String,ConcreteWebSite> map = new HashMap<>();/*** 根据key获取分类站点* @param category* @return*/public CloudWebSite getWebSiteByCategory(String category){if(map.containsKey(category)){return map.get(category);}else {ConcreteWebSite site = new ConcreteWebSite(category);map.put(category,site);return site;}}/*** 获取分类个数* @return*/public int getWebsiteCategorySize(){return map.size();}}
public static void main(String[] args) {WebSiteFactory factory = new WebSiteFactory();CloudWebSite companySite1 = factory.getWebSiteByCategory("企业官网");companySite1.run(new Company("小滴课堂"));CloudWebSite companySite2 = factory.getWebSiteByCategory("企业官网");companySite2.run(new Company("旭瑶课堂"));CloudWebSite byteDanceSite = factory.getWebSiteByCategory("信息流");byteDanceSite.run(new Company("字节跳动"));CloudWebSite ucNews = factory.getWebSiteByCategory("信息流");ucNews.run(new Company("优视科技"));System.out.println("网站分类总数:" +factory.getWebsiteCategorySize());}
- 优点
- ⼤⼤减少了对象的创建,降低了程序内存的占⽤,提⾼效率
缺点
- 提⾼了系统的复杂度,需要分离出内部状态和外部状态注意划分内部状态和外部状态,否则可能会引起线程安全问题,必须有⼀个⼯⼚类加以控制
享元设计模式和原型、单例模式的区别
- 原型设计模式是指定创建对象的种类,然后通过拷⻉这 些原型来创建新的对象。
- 单例设计模式保证⼀个类仅有⼀个实例
