本文最后更新于 983 天前,其中的信息可能已经有所发展或是发生改变,请谨慎参考。
GOF23
创建型模式
- 单例模式
- 工厂模式
- 抽象工厂模式
- 建造者模式
- 原型模式
结构性模式
- 适配器模式
- 桥接模式
- 装饰模式
- 组合模式
- 外观模式
- 享元模式
- 代理模式
行为型模式
- 模板方法模式
- 命令模式
- 迭代器模式
- 观察者模式
- 中介者模式
- 备忘录模式
- 解释器模式
- 状态模式
- 策略模式
- 职责链模式
- 访问者模式
创建型模式
单例模式
饿汉单例模式
代码
package com.xiheya.single;
/**
* @Author {xiheya}
* @Date: 2022/03/15/ 16:44
* @Description
*/
//饿汉单例模式
public class Hungry {
//可能会浪费空间,尽管对象空间没有被使用,但是还是会被开辟出来。
private byte[] data1 = new byte[1024*1024];
private byte[] data2 = new byte[1024*1024];
private byte[] data3 = new byte[1024*1024];
private byte[] data4 = new byte[1024*1024];
private Hungry(){
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
存在的问题
可能会浪费空间,尽管对象空间没有被使用,但是还是会被开辟出来。
懒汉式单例
代码
package com.xiheya.single;
/**
* @Author {xiheya}
* @Date: 2022/03/16/ 10:10
* @Description
*/
public class LazyMan {
// 1.构造器私有化
private LazyMan(){
}
// 2.实例化对象
private volatile static LazyMan lazyMan; //给lazyMan加上volatile可以避免指令重排 保证其原子性操作。
//双重检测锁模式的 懒汉式单例 DLC懒汉式
public static LazyMan getInstance(){
if (lazyMan == null){
synchronized (LazyMan.class){
if (lazyMan == null){
lazyMan = new LazyMan(); //不是一个原子性操作
/**
* 1.分配内存空间
* 2.执行构造方法,初始化对象
* 3.把这个对象指向这个空间
* 就有可能会出现指令重排现象
*
* 正常:123
* 异常:132
* 当A线程进来时 执行了132---先分配内存空间,然后把对象指向这个空间 先占用这个空间,最后再初始化对象,给其赋值
* 恰巧此时B线程进来了,他执行123操作,但是因为A线程先占用了空间,让B线程误以为对象不为null,所以他会不执行if语句,直接return一个lazyMan对象
*
*/
}
}
}
return lazyMan;
}
}
注意点
- 懒汉模式对象如果不加volatile就会出现指令重排。
- 而我们要保证其多线程安全,就需要加上双重锁
synchronized (LazyMan.class){
if (lazyMan == null){
lazyMan = new LazyMan(); //不是一个原子性操作
}
静态内部类
代码
package com.xiheya.single;
/**
* @Author {xiheya}
* @Date: 2022/03/16/ 15:13
* @Description
*/
public class Holder {
//1.构造器私有化
private Holder(){
}
//从静态内部类里返回HOLDER;
public static Holder getInstance(){
return InnerClass.HOLDER;
}
//静态内部类
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
单例不安全,因为有反射。反射会破坏单例
所以为了解决这个问题我们使用枚举来实现单例模式。
枚举单例模式
代码
package com.xiheya.single;
/**
* @Author {xiheya}
* @Date: 2022/03/16/ 15:19
* @Description
*/
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance(){
return INSTANCE;
}
}
因为反射不能破解枚举,所以我们使用枚举来实现单例模式。
工厂模式
作用
- 实现了创建者和调用者的分离
- 详细分类:
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
- OOP七大原则
- 开闭原则:一个软件的实体应当对扩展开放,对修改关闭。
- 依赖倒转原则:要针对接口编程,不要针对实现编程。
- 迪米特法则:只与你直接的朋友通信,而避免和陌生人通信。
- 核心本质
- 实例化对象不适用new,用工厂方法代替
- 将选择实现类,创建对象统一管理和控制,从而将调用者跟我们的实现类解耦
- 三种模式:
- 简单工厂模式
- 用来生产同一等级结构中的任意产品(对于增加新的产品,需要球盖已有代码)
- 工厂方法模式
- 用来生产同一等级结构中的固定产品(支持增加任意产品)
- 抽象工厂模式
- 围绕一个超级工厂创建其他工厂。该工厂又称为其他工厂的工厂。
- 简单工厂模式
简单工厂模式
代码
package com.xiheya.factory.simple;
/**
* @Author {xiheya}
* @Date: 2022/03/16/ 15:39
* @Description
*/
public class Consumer {
public static void main(String[] args) {
// Car car = new WuLing();
// Car car1 = new Tesla();
Car car = CarFactory.getCar("五菱");
Car car1 = CarFactory.getCar("特斯拉");
car.name();
car1.name();
}
}
/**
*
* public interface Car {
* void name();
* }
*
* public class WuLing implements Car {
* @Override
* public void name() {
* System.out.println("wuling");
* }
* }
*
* public class Tesla implements Car{
* @Override
* public void name() {
* System.out.println("Tesla!");
* }
* }
*
* public class CarFactory {
* //方法一
* public static Car getCar(String car){
* if (car.equals("五菱")){
* return new WuLing();
* }else if (car.equals("特斯拉")){
* return new Tesla();
* }else {
* return null;
* }
* }
* //方法二
* public static Car getWuLing(){
* return new WuLing();
* }
* public static Car getTesla(){
* return new Tesla();
* }
*
* }
*/
弊端
未实现开闭原则
实现结果
流程图
工厂方法模式
代码
package com.xiheya.factory.method;
import com.xiheya.factory.simple.CarFactory;
/**
* @Author {xiheya}
* @Date: 2022/03/16/ 15:39
* @Description
*/
public class Consumer {
public static void main(String[] args) {
Car car = new TeslaFactory().getCar();
car.name();
Car car1 = new WuLingFactory().getCar();
car1.name();
Car car2 = new DaZhongFactory().getCar();
car2.name();
}
}
/**
* public interface Car {
* void name();
* }
*
* public interface CarFactory {
* Car getCar();
* }
*
* public class DaZhong implements Car {
* @Override
* public void name() {
* System.out.println("DaZhong!");
* }
* }
*
* public class DaZhongFactory implements CarFactory{
* @Override
* public Car getCar() {
* return new DaZhong();
* }
* }
*
* public class Tesla implements Car {
* @Override
* public void name() {
* System.out.println("Tesla!");
* }
* }
*
* public class TeslaFactory implements CarFactory{
* @Override
* public Car getCar() {
* return new Tesla();
* }
* }
*
* public class WuLing implements Car {
* @Override
* public void name() {
* System.out.println("wuling");
* }
* }
*
* public class WuLingFactory implements CarFactory{
* @Override
* public Car getCar() {
* return new WuLing();
* }
* }
*
*
*/
弊端
虽然实现了代码的开闭原则,并且可以动态拓展。但是工厂方法模式实现成本太高了。
实现结果
流程图
小结
- 简单工厂模式(静态工厂模式)
- 虽然某种程度上不符合设计原则,但实际使用最多!
- 工厂方法模式
- 不修改已有类的前提下,通过新的工厂类实现扩展
- 抽象工厂模式
- 不可以增加产品,可以增加产品族!
- 应用场景
- JDK中的Calendar的getInstance方法
- JDBC中的Connection对象的获取
- Spring中IOC容器创建管理bean对象
- 反射中Class对象的newInstance方法
抽象工厂模式
定义
- 定义:抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们的类
- 适用场景
- 客户端(应用层)不依赖于产品类实例如何被创建、实现的细节
- 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量的重复代码。
- 提供一个产品类的库,所有产品以同样的接口出现,从而使得客户端不依赖于具体的实现
- 优点
- 具体产品在应用层的代码隔离,无需关心创建的细节
- 将一个系列的产品统一到一起创建
- 缺点
- 规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难;
- 增加了系统的抽象性和理解难度。
代码
package com.xiheya.factory.abstract1;
/**
* @Author {xiheya}
* @Date: 2022/03/16/ 17:00
* @Description
*/
public class Client {
public static void main(String[] args) {
System.out.println("==============小米系列产品================");
XiaomiFactory xiaomiFactory = new XiaomiFactory();
IPhoneProduct xiaomi = xiaomiFactory.iphoneProduct();
xiaomi.callup();
xiaomi.sendSMS();
IRouterProduct xiaomiRouter = xiaomiFactory.irouterProduct();
xiaomiRouter.openWifi();
xiaomiRouter.setting();
System.out.println("==============华为系列产品================");
HuaweiFactory huaweiFactory = new HuaweiFactory();
IPhoneProduct huawei = huaweiFactory.iphoneProduct();
huawei.callup();
huawei.sendSMS();
IRouterProduct huaweiRouter = huaweiFactory.irouterProduct();
huaweiRouter.openWifi();
huaweiRouter.setting();
}
}
/**
* //抽象产品工厂
* public interface IProductFactory {
* //生产手机
* IPhoneProduct iphoneProduct();
* //生产路由器
* IRouterProduct irouterProduct();
* }
*
* //手机产品接口
* public interface IPhoneProduct {
* void start();
* void shutdown();
* void callup();
* void sendSMS();
* }
*
* //路由器产品接口
* public interface IRouterProduct {
* void start();
* void shutdown();
* void openWifi();
* void setting();
* }
*
*
* //华为工厂
* public class HuaweiFactory implements IProductFactory{
* @Override
* public IPhoneProduct iphoneProduct() {
* return new HuaweiPhone();
* }
*
* @Override
* public IRouterProduct irouterProduct() {
* return new HuaweiRouter();
* }
* }
*
* //华为手机
* public class HuaweiPhone implements IPhoneProduct{
* @Override
* public void start() {
* System.out.println("Huawei start");
* }
*
* @Override
* public void shutdown() {
* System.out.println("Huawei shutdown");
* }
*
* @Override
* public void callup() {
* System.out.println("Huawei callup");
* }
*
* @Override
* public void sendSMS() {
* System.out.println("Huawei sendSMS");
* }
* }
*
* //华为路由器
* public class HuaweiRouter implements IRouterProduct{
* @Override
* public void start() {
* System.out.println("启动华为路由器");
* }
*
* @Override
* public void shutdown() {
* System.out.println("关闭华为路由器");
* }
*
* @Override
* public void openWifi() {
* System.out.println("华为由器打开WiFi");
* }
*
* @Override
* public void setting() {
* System.out.println("配置华为路由器");
* }
* }
*
* //小米工厂
* public class XiaomiFactory implements IProductFactory{
* @Override
* public IPhoneProduct iphoneProduct() {
* return new XiaomiPhone();
* }
*
* @Override
* public IRouterProduct irouterProduct() {
* return new XiaomiRouter();
* }
* }
*
* //小米手机
* public class XiaomiPhone implements IPhoneProduct{
* @Override
* public void start() {
* System.out.println("xiaomi start");
* }
*
* @Override
* public void shutdown() {
* System.out.println("xiaomi shutdown");
* }
*
* @Override
* public void callup() {
* System.out.println("xiaomi callup");
* }
*
* @Override
* public void sendSMS() {
* System.out.println("xiaomi sendSMS");
* }
* }
*
* //小米路由器
* public class XiaomiRouter implements IRouterProduct{
* @Override
* public void start() {
* System.out.println("启动小米路由器");
* }
*
* @Override
* public void shutdown() {
* System.out.println("关闭小米路由器");
* }
*
* @Override
* public void openWifi() {
* System.out.println("小米路由器打开WiFi");
* }
*
* @Override
* public void setting() {
* System.out.println("配置小米路由器");
* }
* }
*
*/
运行结果
流程图
建造者模式
定义
- 建造者模式也属于创建类模式,它提供了一种创建对象的最佳方式。
- 它将一个复杂对象的构建与他的表示分离,使得同样的构建过程可以创建不同的表示。
主要作用
在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。
- 用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)
- 例子:
- 工厂(建造者模式):负责制造汽车(组装过程和细节在工厂内)
- 汽车购买者(用户):你只需要说出你需要的型号(对象的类型和内容),然后直接购买就可以使用了(不需要知道汽车是怎么组装的(车轮、车门、发动机、方向盘等等))
代码
package com.design_patterns.builder;
/**
* @Author {xiheya}
* @Date: 2022/03/19/ 15:15
* @Description
*/
public class Test {
public static void main(String[] args) {
//新建一个指挥
Director director = new Director();
//指挥 工人盖房子
Product build = director.build(new Worker());
System.out.println(build.toString());
}
}
/**
* //抽象的建造者方法:盖房子
* public abstract class Builder {
*
* abstract void builderA(); //地基
* abstract void builderB(); //钢筋工程
* abstract void builderC(); //铺电线
* abstract void builderD(); //粉刷
* //完工:得到产品
* abstract Product getProduct();
* }
*
* public class Product {
* private String buildA;
* private String buildB;
* private String buildC;
* private String buildD;
*
* public String getBuildA() {
* return buildA;
* }
*
* public String getBuildB() {
* return buildB;
* }
*
* public String getBuildC() {
* return buildC;
* }
*
* public String getBuildD() {
* return buildD;
* }
*
* public void setBuildA(String buildA) {
* this.buildA = buildA;
* }
*
* public void setBuildB(String buildB) {
* this.buildB = buildB;
* }
*
* public void setBuildC(String buildC) {
* this.buildC = buildC;
* }
*
* public void setBuildD(String buildD) {
* this.buildD = buildD;
* }
*
* @Override
* public String toString() {
* return "Product{" +
* "buildA='" + buildA + '\'' +
* ", buildB='" + buildB + '\'' +
* ", buildC='" + buildC + '\'' +
* ", buildD='" + buildD + '\'' +
* '}';
* }
*
* }
*
* //具体的建造者:工人
* public class Worker extends Builder{
* private Product product;
*
* public Worker() {
* product = new Product(); //工人负责创建产品
* }
*
* @Override
* void builderA() {
* product.setBuildA("打好了地基");
* System.out.println(product.getBuildA());
* }
*
* @Override
* void builderB() {
* product.setBuildB("铺好了钢筋");
* System.out.println(product.getBuildB());
* }
*
* @Override
* void builderC() {
* product.setBuildC("铺好了电线");
* System.out.println(product.getBuildC());
* }
*
* @Override
* void builderD() {
* product.setBuildD("粉好了墙壁");
* System.out.println(product.getBuildD());
* }
*
* @Override
* Product getProduct() {
* return product;
* }
* }
*
* //指挥:核心,负责构建一个工程,工程如何创建 由他决定。
* public class Director {
*
* // 指挥工人建造房子
* public Product build(Builder builder){
* builder.builderA();
* builder.builderB();
* builder.builderC();
* builder.builderD();
* //建造完成后返回一个房子;
* return builder.getProduct();
* }
* }
*
*
*/
运行结果
- 上面示例是Builder模式的常规用法,导演类Director在Builder模式中具有很重要的作用,它用于指导具体构建者如何构建产品,控制调用先后次序,并向调用着返回完整的产品类,但是有些情况下需要简化系统结构,可以把Director和抽象建造者进行结合。
- 通过静态内部类方式实现零件无序装配构造,这种方式使用更加灵活,更符合定义。内部有复杂对象的默认实现,使用时可以根据用户需求自由定义更改内容,并且无需改变具体的构造方式。就可以生产出不同复杂产品。
- 比如:麦当劳的套餐,服务员(具体建造者)可以随意搭配任意几种产品(零件)组成一款套餐(产品),然后出售给客户。彼地种方式少了指挥者,主要是因为第二种方式把指挥者交给用户来操作,使得产品的创建更加简单灵活。
无指挥类的建造者模式
代码
package com.design_patterns.builder.demo02;
/**
* @Author {xiheya}
* @Date: 2022/03/20/ 20:54
* @Description
*/
public class Test {
public static void main(String[] args) {
//服务员
Worker worker = new Worker();
//链式编程,在原来的基础上可以自由组合,如果组合,则返回默认套餐。
Product product = worker.buildA("炸鸡").buildB("芬达").getProduct();
System.out.println(product.toString());
}
}
/**
* //建造者
* public abstract class Builder {
* abstract Builder buildA(String msg); //汉堡
* abstract Builder buildB(String msg); //薯条
* abstract Builder buildC(String msg); //可乐
* abstract Builder buildD(String msg); //甜点
*
* abstract Product getProduct();
* }
*
* public class Product {
* private String BuildA = "汉堡";
* private String BuildB = "可乐";
* private String BuildC = "薯条";
* private String BuildD = "甜点";
*
* public String getBuildA() {
* return BuildA;
* }
*
* public void setBuildA(String buildA) {
* BuildA = buildA;
* }
*
* public String getBuildB() {
* return BuildB;
* }
*
* public void setBuildB(String buildB) {
* BuildB = buildB;
* }
*
* public String getBuildC() {
* return BuildC;
* }
*
* public void setBuildC(String buildC) {
* BuildC = buildC;
* }
*
* public String getBuildD() {
* return BuildD;
* }
*
* public void setBuildD(String buildD) {
* BuildD = buildD;
* }
*
* @Override
* public String toString() {
* return "Product{" +
* "BuildA='" + BuildA + '\'' +
* ", BuildB='" + BuildB + '\'' +
* ", BuildC='" + BuildC + '\'' +
* ", BuildD='" + BuildD + '\'' +
* '}';
* }
* }
*
* //具体的建造者
* public class Worker extends Builder{
* private Product product;
*
* public Worker() {
* product = new Product();
* }
*
* @Override
* Builder buildA(String msg) {
* product.setBuildA(msg);
* return this;
* }
*
* @Override
* Builder buildB(String msg) {
* product.setBuildB(msg);
* return this;
* }
*
* @Override
* Builder buildC(String msg) {
* product.setBuildC(msg);
* return this;
* }
*
* @Override
* Builder buildD(String msg) {
* product.setBuildD(msg);
* return this;
* }
*
* @Override
* Product getProduct() {
* return product;
* }
* }
*
*/
运行结果
优缺点
优点
- 产品的建造和表示分离,实现了解耦。使用建造者模式可以使客户端不必知道产品内部组成的细节。
- 将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰.
- 具体的建造者类之间是相互独立的,这有利于系统的扩展。增加新的具体建造者无需修改原有类库的代码,符合“开闭原则”
缺点
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其适用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造类来实现这种变化,导致系统变得很庞大。
应用场景
- 需要生产的产品对象有复杂的内部结构,这些产品对象具有共性。
- 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品、
- 适合于一个具有较多的零件(属性)的产品(对象)的创建过程。
建造者与抽象工厂模式的比较
- 与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族。
- 在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象。
- 如果将抽象工厂模式堪称汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车。
原型模式
实现步骤
- 实现一个接口:Cloneable;
- 重写一个方法:clone();
- 主要用于:Spring Bean:单例模式,原型模式
- 原型模式+工厂模式 ===> new <=> 原型模式
浅克隆模式
代码
package com.design_patterns.prototype.demo01;
import java.util.Date;
/**
* @Author {xiheya}
* @Date: 2022/03/20/ 21:29
* @Description
*/
//客户端
public class Bilibili {
public static void main(String[] args) throws CloneNotSupportedException {
//原型对象 v1
Date date = new Date();
Video v1 = new Video("xiheya",date);
//克隆对象 v2
Video v2 =(Video) v1.clone();
System.out.println("v1-->" + v1);
System.out.println("v1:hashcode-->" + v1.hashCode());
System.out.println("v2-->" + v2);
System.out.println("v2:hashcode-->" + v2.hashCode());
System.out.println("====================================");
date.setTime(123456);
System.out.println("v1-->" + v1);
System.out.println("v1:hashcode-->" + v1.hashCode());
System.out.println("v2-->" + v2);
System.out.println("v2:hashcode-->" + v2.hashCode());
System.out.println("====================================");
}
}
/**
* //原型模式的实现:
* // 1.实现一个接口:Cloneable;
* // 2.重写一个方法:clone();
* // Video
* public class Video implements Cloneable{
* private String name;
* private Date creatTime;
*
* @Override
* protected Object clone() throws CloneNotSupportedException {
* return super.clone();
* }
*
* public Video() {
* }
*
* public Video(String name, Date creatTime) {
* this.name = name;
* this.creatTime = creatTime;
* }
*
* public String getName() {
* return name;
* }
*
* public void setName(String name) {
* this.name = name;
* }
*
* public Date getCreatTime() {
* return creatTime;
* }
*
* public void setCreatTime(Date creatTime) {
* this.creatTime = creatTime;
* }
*
* @Override
* public String toString() {
* return "Video{" +
* "name='" + name + '\'' +
* ", creatTime=" + creatTime +
* '}';
* }
* }
*/
结果
深克隆模式
代码
package com.design_patterns.prototype.demo02;
import java.util.Date;
/**
* @Author {xiheya}
* @Date: 2022/03/20/ 21:29
* @Description
*/
//客户端
public class Bilibili {
public static void main(String[] args) throws CloneNotSupportedException {
//原型对象 v1
Date date = new Date();
Video v1 = new Video("xiheya",date);
//克隆对象 v2
Video v2 =(Video) v1.clone();
System.out.println("v1-->" + v1);
System.out.println("v1:hashcode-->" + v1.hashCode());
System.out.println("v2-->" + v2);
System.out.println("v2:hashcode-->" + v2.hashCode());
System.out.println("====================================");
date.setTime(123456);
System.out.println("v1-->" + v1);
System.out.println("v1:hashcode-->" + v1.hashCode());
System.out.println("v2-->" + v2);
System.out.println("v2:hashcode-->" + v2.hashCode());
System.out.println("====================================");
}
}
/**
* //原型模式的实现:
* // 1.实现一个接口:Cloneable;
* // 2.重写一个方法:clone();
* // Video
* public class Video implements Cloneable{
* private String name;
* private Date creatTime;
*
* @Override
* protected Object clone() throws CloneNotSupportedException {
* Object obj = super.clone();
* //深克隆,序列化与反序列化
* Video v = (Video) obj;
* v.creatTime = (Date) this.creatTime.clone();//将对象的属性也序列化
* return obj;
* }
*
* public Video() {
* }
*
* public Video(String name, Date creatTime) {
* this.name = name;
* this.creatTime = creatTime;
* }
*
* public String getName() {
* return name;
* }
*
* public void setName(String name) {
* this.name = name;
* }
*
* public Date getCreatTime() {
* return creatTime;
* }
*
* public void setCreatTime(Date creatTime) {
* this.creatTime = creatTime;
* }
*
* @Override
* public String toString() {
* return "Video{" +
* "name='" + name + '\'' +
* ", creatTime=" + creatTime +
* '}';
* }
* }
*/
结果
浅克隆与深克隆对比
- 浅克隆:克隆出来的对象和原型共同指向一个对象,克隆对象只是引用了这个对象。
- 深克隆:克隆出来的对象会把原型的属性也克隆出来。