——————————————————— 原来这世上,比之成双鸳侣,多的却是相思枉然 ———————————————————
设计模式共计23种,可以分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
0、简单工厂:将创建产品的部分抽离。其实不是一个设计模式,比较像是一种编程习惯。
1、工厂方法:由子类实现如何创建具体的产品。
2、抽象工厂方法:将工厂和产品都设置为抽象,由子类工厂实现创建具体的产品(工厂抽象),不同产品的创建方式也不同(产品抽象)。
3、单例模式:确保一个类最多只有一个实例,并提供一个全局访问点。
public class Singleton {
private volatile static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
4、建造者模式:将一个复杂对象的构建过程与它的实现分离。例如我们需要组装一个电脑,电脑需要有CPU、显卡、主板和电源。我们不关心具体组件的生产方式,不同规格的组件最终电脑的价格也不相同,这种场景下适合使用建造者模式。
@Data
public class Computer {
private String cpu;
private String mainBoard;
private String gpu;
private String power;
}
public class ComputerBuilder { // 通过建造者模式装填电脑的各个组件,装填的组件规格和顺序都是可以调整的
private Computer computer = new Computer();
public ComputerBuilder addCpu(String cpu) {
System.out.println("给电脑装上CPU:" + cpu);
this.computer.setCpu(cpu);
return this;
}
public ComputerBuilder addGpu(String gpu) {
System.out.println("给电脑装上显卡:" + gpu);
this.computer.setGpu(gpu);
return this;
}
public ComputerBuilder addMainBoard(String mainBoard) {
System.out.println("给电脑装上主板:" + mainBoard);
this.computer.setMainBoard(mainBoard);
return this;
}
public ComputerBuilder addPower(String power) {
System.out.println("给电脑装上电源:" + power);
this.computer.setPower(power);
return this;
}
}
public class Test {
public static void main(String[] args) {
ComputerBuilder computerBuilder = new ComputerBuilder();
computerBuilder.addCpu("i7-12700 CPU")
.addGpu("GTX3080显卡")
.addPower("长城电源")
.addMainBoard("微星主板");
}
}
5、原型模式:给出一个原型对象来指明类型,然后复制这个原型对象创建出更多同类型的对象。例如有一只绵羊多利,需要进行复制,克隆出许多一样的绵羊。也就是给对象添加克隆方法,可以通过实现clone深拷贝和序列化两种方式实现。注意,如果使用@Data注解,会重写hashcode方法,导致克隆的对象hashcode都一样。
public class SheepClone implements Cloneable { // 省略构造函数和get/set方法
private String name;
private String color;
private int weight;
public SheepClone(String name, String color, int weight) {
this.name = name;
this.color = color;
this.weight = weight;
}
@Override
@SneakyThrows
public SheepClone clone() {
return (SheepClone) super.clone();
}
}
public class SheepSerialize implements Serializable { // 省略构造函数和get/set方法
private String name;
private String color;
private int weight;
@SneakyThrows
public SheepSerialize serialize() {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (SheepSerialize)ois.readObject();
}
}
@org.junit.Test
public void TestClone() {
SheepClone sheep1 = new SheepClone("Dolly","white",20);
SheepClone sheep2 = sheep1.clone();
SheepClone sheep3 = sheep1.clone();
SheepClone sheep4 = sheep1.clone();
System.out.println("sheep1 hashcode: " + sheep1.hashCode() + "\n" + "sheep2 hashcode: " + sheep2.hashCode() +
"\n" + "sheep3 hashcode: " + sheep3.hashCode() + "\n" + "sheep4 hashcode: " + sheep4.hashCode());
sheep2.setName("Andy");
System.out.println(sheep1.getName());
System.out.println(sheep2.getName());
}
@org.junit.Test
public void TestSerialize() {
SheepSerialize sheep1 = new SheepSerialize("Dolly","white",20);
SheepSerialize sheep2 = sheep1.serialize();
SheepSerialize sheep3 = sheep1.serialize();
SheepSerialize sheep4 = sheep1.serialize();
System.out.println("sheep1 hashcode: " + sheep1.hashCode() + "\n" + "sheep2 hashcode: " + sheep2.hashCode() +
"\n" + "sheep3 hashcode: " + sheep3.hashCode() + "\n" + "sheep4 hashcode: " + sheep4.hashCode());
sheep2.setName("Andy");
System.out.println(sheep1.getName());
System.out.println(sheep2.getName());
}
6、适配器模式:将一个类的接口转换成客户期望的另一个接口(看着是天鹅,其实可能是鸭子)。例如动物园有两只天鹅,其中一只是鸭子假扮的,但是它看起来和天鹅一样(可以执行天鹅的行为)。
@AllArgsConstructor
public class Duck {
private String name;
public void quack() {
System.out.println("鸭子叫,quack,quack,quack");
}
public void glide() {
System.out.println("鸭子飞,glide,glide,glide");
}
}
@NoArgsConstructor
@AllArgsConstructor
public class Swan {
private String name;
public void chirp() {
System.out.println("天鹅叫,chirp,chirp,chirp");
}
public void fly() {
System.out.println("天鹅飞,fly,fly,fly");
}
}
@AllArgsConstructor
public class DuckAdaptor extends Swan{
private Duck duck;
@Override
public void chirp() {
duck.quack();
}
@Override
public void fly() {
duck.glide();
}
}
public static void main(String[] args) {
Duck duck = new Duck("duck");
Swan swan = new Swan("Swan");
DuckAdaptor fakeSwan = new DuckAdaptor(duck);
swan.chirp();
swan.fly();
fakeSwan.chirp(); // 伪装的天鹅同样可以执行天鹅的各种行为
fakeSwan.fly();
}
7、装饰器模式:动态地将责任附加到对象上,提供有别于继承的另一种扩展功能的选择。例如钢铁侠随着不断的改进,逐渐有更多的功能。
public abstract class IronMan {
abstract String function();
}
public class Mk1 extends IronMan{
private String function = "喷火器和响尾蛇导弹";
@Override
String function() {
return function;
}
}
public class Mk2 extends IronMan{
private String function = "飞行稳定系统";
private IronMan ironMan;
public Mk2(IronMan ironMan) {
this.ironMan = ironMan;
}
@Override
String function() {
return ironMan.function() + "\n" + function;
}
}
public class Mk6 extends IronMan{
private String function = "方舟反应堆";
private IronMan ironMan;
public Mk6(IronMan ironMan) {
this.ironMan = ironMan;
}
@Override
String function() {
return ironMan.function() + "\n" + function;
}
}
public class Mk7 extends IronMan{
private String function = "散弹枪和激光武器";
private IronMan ironMan;
public Mk7(IronMan ironMan) {
this.ironMan = ironMan;
}
@Override
String function() {
return ironMan.function() + "\n" + function;
}
}
public static void main(String[] args) {
IronMan ironMan = new Mk1();
ironMan = new Mk2(ironMan);
ironMan = new Mk6(ironMan);
ironMan = new Mk7(ironMan);
System.out.println("钢铁侠当前的能力:" + "\n" + ironMan.function());
}
适配器模式和装饰器模式的区别:适配器模式是将一个类转变为另一个类,而装饰器模式是在保持原有对象的基础上,增强原有对象的功能
8、代理模式:给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用(类似中介)。例如常见的将房子挂给房产中介,由房产中介帮忙卖。
@Data
public class Seller {
private int money;
public void sell(int money) {
System.out.println("房子出售,客户拿到" + money + "元");
this.money = money;
}
}
public class Agent {
private Seller seller;
public Agent(Seller seller) {
this.seller = seller;
}
public void look() {
System.out.println("中介带客户看房子");
}
public void sell(int money) {
System.out.println("房子成交,总共卖出" + money + "元,中介拿100元");
seller.sell(money - 100);
}
}
public static void main(String[] args) {
Seller client = new Seller();
Agent agent = new Agent(client);
agent.look();
agent.look();
agent.look();
agent.sell(3000);
System.out.println("客户现在的钱:" + client.getMoney() + "元");
}
1. 静态代理:直接手动创建
2. 动态代理:不需要实现接口,利用JDK的API动态的在内存中构建代理对象(要求被代理对象必须有接口)
3. CGLIB代理:在子类中拦截所有父类方法的调用,顺势织入横切逻辑(无法代理final方法)
9、外观模式:把一些复杂的流程封装成一个接口供给外部用户,客户端只需要跟Facade类交互即可。例如洗衣机洗衣服会有浸泡、洗涤、漂洗和甩干四个步骤,但是洗衣机将这四个步骤封装成一个洗衣的按钮给用户,类似外观模式。
10、桥接模式:将抽象部分和实现部分桥接在一起。例如需要3种不同粗细和3种不同颜色的蜡笔,需要构建9个实例。如果用3种不同粗细的画笔和3种不同的颜料,就只需要6个实例。
11、组合模式:将对象组装成树状的层次结构。例如公司架构是层次结构,有老板、经理和职员。可以将每个员工视为一个节点,所有节点都继承根节点,转换成树状结构。
public class Root {
protected String name;
protected List<Root> nodes = new ArrayList<>();
public Root(String name) {
this.name = name;
}
public void addNode(Root node) {
nodes.add(node);
}
public void removeNode(Root node) {
nodes.remove(node);
}
public void describe() {
System.out.println(name);
if (CollectionUtils.isNotEmpty(nodes)) {
System.out.println("==========");
for (Root node : nodes) {
node.describe();
}
System.out.println("==========");
}
}
}
public class Boss extends Root{
public Boss(String name) {
super(name);
}
}
public class Manager extends Root{
public Manager(String name) {
super(name);
}
}
public class Staff extends Root{
public Staff(String name) {
super(name);
}
}
public class Test {
public static void main(String[] args) {
Root company = new Root("company");
Root boss = new Boss("boss");
Root managerA = new Manager("managerA");
Root managerB = new Manager("managerB");
Root staff1 = new Staff("staff1");
Root staff2 = new Staff("staff2");
Root staff3 = new Staff("staff3");
Root staff4 = new Staff("staff4");
// 一个公司一个老板
company.addNode(boss);
// 老板管着两个经理
boss.addNode(managerA);
boss.addNode(managerB);
// 每个经理手下都有两个员工
managerA.addNode(staff1);
managerA.addNode(staff2);
managerB.addNode(staff3);
managerB.addNode(staff4);
company.describe();
}
12、享元模式:运用共享技术有效地支持大量细粒度对象的复用,也就是将一些不变的对象进行共享。例如查询火车票时,火车票信息是基本不变的,可以将这个信息缓存起来进行共享。
13、策略模式:针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。
14、模板方法模式:只定义算法的骨架,子类可以在不改变算法结构的情况下,重新定义某些特殊步骤。
策略模式和模板方法模式的区别:策略模式是针对于同一类的算法,例如判断是否是偶数,可能具体实现是对2取模,也有可能是除2。而模板方法模式是定义一系列的算法步骤,例如机器学习的步骤可能是构建数据集、训练模型和交叉验证,但是具体每个步骤如何实现不同的机器学习算法都是不同的。
15、观察者模式:定义对象间一对多的依赖关系,当主题的状态发生改变时,所有依赖于它的观察者都得到通知并自动更新。类似订阅报纸,当报社更新时,会通知每个订阅的读者。
16、迭代器模式:提供一种方法顺序访问一个聚合对象中的各个元素, 而又无须暴露该对象的内部表示。比如列表、链表等结构都提供迭代器,对外提供统一的访问方式。
17、责任链模式:了避免请求发送者与多个处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链。比如我们常见的请假审批、奖学金审批都是这样的设计。
public abstract class Hander {
protected Hander next;
abstract Boolean handle(int day);
}
public class Teacher extends Hander{
private Hander next = new Dean();
@Override
public Boolean handle(int day) {
if (day <= 3) {
System.out.println("老师同意了请假申请");
return true;
}
return next.handle(day);
}
}
public class Dean extends Hander {
private Hander next = new Principal();
@Override
public Boolean handle(int day) {
if (day <= 7) {
System.out.println("院长同意了请假申请");
return true;
}
return next.handle(day);
}
}
public class Principal extends Hander{
private Hander next = null;
@Override
public Boolean handle(int day) {
if (day <= 30) {
System.out.println("校长同意了请假申请");
return true;
}
System.out.println("请这么久假,辍学吧你");
return false;
}
}
public class Test {
public static void main(String[] args) {
Hander hander = new Teacher();
hander.handle(2);
hander.handle(5);
hander.handle(10);
hander.handle(100);
}
}
18、命令模式:将请求封装为对象,使发出请求的对象和执行请求的对象分离开。这样两者之间通过命令对象进行沟通,方便将命令对象进行储存、传递、调用、增加与管理。例如客户和厨师之间通过菜单进行命令传递,二者不直接接触,通过菜单进行沟通。
19、备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后需要时能将该对象恢复到原先保存的状态。例如游戏里的存档。
20、状态模式:对象对不同的状态会有不同的反应,对象的行为依赖于它的状态。例如:妈妈心情好的时候你要玩具,妈妈会给你买,心情不好的时候你要玩具,你会挨揍。
21、访问者模式:是一种将数据操作与数据结构分离的设计模式,对于同一个数据结构,不同的访问者操作是不一样的。例如你自己查看聊天记录只是为了看看,家长查看你的聊天记录是为了看你有没有乱花钱,男朋友查看你的聊天记录是为了看你有没有出轨,同一个数据结构,访问者不同操作也不同。
public class WeiXin {
private List<String> records = new ArrayList<>();
public List<String> getRecords() {
return records;
}
public void chat(String record) {
records.add(record);
}
public void accept(Viewer viewer) {
viewer.view(this);
}
}
public abstract class Viewer {
abstract void view(WeiXin weiXin);
}
public class Me extends Viewer{
@Override
public void view(WeiXin weiXin) {
List<String> records = weiXin.getRecords();
System.out.println("自己查看聊天记录");
System.out.println("==============");
for (String record : records) {
System.out.println(record);
}
System.out.println("==============");
}
}
public class Parent extends Viewer {
@Override
public void view(WeiXin weiXin) {
List<String> records = weiXin.getRecords();
if (records.stream().anyMatch(it -> it.contains("购买"))) {
System.out.println("家长觉得你乱花钱了");
return;
}
System.out.println("家长觉得你没有乱花钱");
}
}
public class Boyfriend extends Viewer{
@Override
public void view(WeiXin weiXin) {
List<String> records = weiXin.getRecords();
if (records.stream().anyMatch(it -> it.contains("帅哥"))) {
System.out.println("男朋友觉得你出轨了");
return;
}
System.out.println("男朋友觉得你没有出轨");
}
}
public class Test {
public static void main(String[] args) {
WeiXin weiXin = new WeiXin();
weiXin.chat("09.07 购买了喜欢的化妆品");
weiXin.chat("09.08 自己一个人出去跑步");
weiXin.chat("09.09 和帅哥出去吃饭");
Viewer me = new Me();
Viewer parent = new Parent();
Viewer boyFriend = new Boyfriend();
me.view(weiXin);
parent.view(weiXin);
boyFriend.view(weiXin);
}
}
22、中介者模式:用一个中介对象封装一系列的对象交互,各对象不需要显式的相互作用。例如各种租房中介,房主将房源挂在中介处,租客去中介处看房子。租客可以通过中介砍价,房主通过中介收租,二者不互相接触。将二者解耦,这样房主不关心租客是谁,租客也不关心房主是谁。
23、解释器模式:给定一个语言 ,定义文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子。例如密语和解密本,按照密语写文章,然后通过解密本进行解密。中介者模式和代理模式的区别:代理模式是用一个代理类代表另一个类的功能,例如代理律师。中介者模式则是封装对象交互,使交互对象解耦。