设计模式笔记
设计模式
分类:
- 创建型模式:(描述怎样去创建一个对象,创建和使用分离)
- 单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式
- 结构型模式:(描述如何将类或对象安装某种类型组成更大的结构)
- 适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
- 行为型模式:(描述类和对象如何可以相互协作)
- 模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式
1、OOP七大原则
- 开闭原则:对扩展开放,对修改关闭(当需求需要改变的时候,尽量去扩展)
- 里氏替换原则: 继承必须确保超类所拥有的性质在子类中仍然成立(尽量不重写父类的方法)
- 依赖倒置原则: 要面向接口编程,不要面向实现编程
- 单一职责原则: 控制类的粒度大小,将对象解耦,提高其内聚性(一个对象不应该担任太多的职责,原子性,单一的方法做单一的事情)
- 接口隔离原则: 要为各个类建立他们需要的专用接口
- 迪米特法则: 只与你的直接朋友交谈,不跟“陌生人”说话,降低代码之间的耦合度
- 合成复用原则: 尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
2、单例模式
核心作用:保证一个类只有一个实例,并且提供一个访问该实例的 全局访问点
饿汉式
package single;
/**
* 饿汉式单例:一上来就把对象创建完成
*/
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;
}
}
DCL懒汉式
package single;
/**
* 懒汉式单例模式
* 单线程下OK,多线程使用双重检测锁
*/
public class LazyMan {
private LazyMan() {
System.out.println(Thread.currentThread().getName()+" OK");
}
private volatile static LazyMan lazyMan;
// 双重检测锁模式 double check lock (DCL)
public static LazyMan getInstance() {
if (lazyMan == null) {
synchronized (LazyMan.class) {
if (lazyMan == null) {
lazyMan = new LazyMan();
/**
* 1、分配内存空间
* 2、执行构造方法,初始化对象
* 3、把这个对象指向这个内存空间
*
* 123 线程A
* 132 线程B 出现了执行重排,需要声明volatile禁止指令重排
*/
}
}
}
return lazyMan;
}
// 多线程并发
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
lazyMan.getInstance();
}).start();
}
}
}
静态内部类
package single;
// 静态内部类
public class Holder {
private Holder() {}
private static Holder getInstance() {return InnerClass.HOLDER;}
public static class InnerClass {
private static final Holder HOLDER = new Holder();
}
}
单例不安全 :反射 破坏了单例
解决:使用枚举类,枚举没有无参构造,只有有参构造
3、工厂模式
实现了创建者和调用者分离
分类:
- 简单工厂模式:用来生产同一等级结构中的任意产品(对于增加新的产品,需要扩展已有代码)
- 工厂方法模式:用来生产同一等级结构中的固定产品(支持增加任意产品)
- 抽象工厂模式:围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。
工厂设计模式的原则(OOP七大原则):
- 开闭原则:一个软件的实体应当对扩展开放,对修改关闭
- 依赖倒转原则:要针对接口编程,不要针对实现编程
- 迪米特法则:只与你直接的朋友通信,而避免和陌生人通信
核心本质:
- 实例化对象不使用new,用工厂方法代替 factory
- 将选择实现类,创建对象统一管理和控制。从而将调用者跟我们的实现类解耦
简单工厂模式
public interface Car {
void name();
}
public class WuLing implements Car{
@Override
public void name() {
System.out.println("五菱宏光");
}
}
public class Tesla implements Car{
@Override
public void name() {
System.out.println("特斯拉");
}
}
// 静态工厂模式
// 开闭原则
public class CarFactory {
// 方法一: 不满足开闭原则
public static Car getCar(String car){
if(car.equals("wuling")){
return new WuLing();
}else if(car.equals("tesila")){
return new Tesla();
}else {
return null;
}
}
// 方法二:
public static Car geyWuling(){
return new WuLing();
}
public static Car geyTesla(){
return new Tesla();
}
}
public class Consumer {
public static void main(String[] args) {
// 接口,所有的实现类
// Car car = new WuLing();
// Car car1 = new Tesla();
// 2、使用工厂创建
Car car = CarFactory.getCar("wuling");
Car car1 = CarFactory.getCar("tesila");
car.name();
car1.name();
}
}
工厂方法模式
public interface Car {
void name();
}
public class WuLing implements Car {
@Override
public void name() {
System.out.println("五菱宏光");
}
}
public class Tesla implements Car {
@Override
public void name() {
System.out.println("特斯拉");
}
}
// 工厂方法模式
public interface CarFactory {
Car getCar();
}
public class WulingFactory implements CarFactory{
@Override
public Car getCar() {
return new WuLing();
}
}
public class TeslaFactory implements CarFactory{
@Override
public Car getCar() {
return new Tesla();
}
}
public class Consumer {
public static void main(String[] args) {
Car car = new WulingFactory().getCar();
Car car1 = new TeslaFactory().getCar();
car.name();
car1.name();
Car car2 = new MoBaiFactory().getCar();
car2.name();
}
}
对比工厂模式:
1、结构复杂度:simple>method
2、代码复杂度:simple>method
3、编程复杂度:simple>method
4、管理上的复杂度:simple>method
根据设计原则,使用工厂方法模式;根据实际业务,使用简单工厂模式
4、抽象工厂模式
// 手机产品接口
public interface IphoneProduct {
void start();
void shutdown();
void callup();
void sendSMS();
}
// 小米手机
public class XiaomiPhone implements IphoneProduct{
@Override
public void start() {
System.out.println("开启小米手机");
}
@Override
public void shutdown() {
System.out.println("关闭小米手机");
}
@Override
public void callup() {
System.out.println("小米手机打电话");
}
@Override
public void sendSMS() {
System.out.println("小米手机发短信");
}
}
// 华为手机
public class HuaweiPhone implements IphoneProduct{
@Override
public void start() {
System.out.println("开启华为手机");
}
@Override
public void shutdown() {
System.out.println("关闭华为手机");
}
@Override
public void callup() {
System.out.println("华为手机打电话");
}
@Override
public void sendSMS() {
System.out.println("华为手机发短信");
}
}
// 路由器产品接口
public interface IRouterProduct {
void start();
void shutdown();
void openWifi();
void setting();
}
// 小米路由器
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("打开小米Wi-Fi");
}
@Override
public void setting() {
System.out.println("小米设置");
}
}
// 华为路由器
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("打开华为Wi-Fi");
}
@Override
public void setting() {
System.out.println("华为设置");
}
}
// 抽象产品工厂
public interface IProductFactory {
// 生产手机
IphoneProduct iphoneProduct();
// 生产路由器
IRouterProduct irouterProduct();
}
public class XiaomiFactory implements IProductFactory{
@Override
public IphoneProduct iphoneProduct() {
return new XiaomiPhone();
}
@Override
public IRouterProduct irouterProduct() {
return new XiaomiRouter();
}
}
public class HuaweiFactory implements IProductFactory{
@Override
public IphoneProduct iphoneProduct() {
return new HuaweiPhone();
}
@Override
public IRouterProduct irouterProduct() {
return new HuaweiRouter();
}
}
public class Client {
public static void main(String[] args) {
System.out.println("小米系列产品--------------------");
// 小米工厂
XiaomiFactory xiaomiFactory = new XiaomiFactory();
IphoneProduct iphoneProduct = xiaomiFactory.iphoneProduct();
iphoneProduct.callup();
iphoneProduct.sendSMS();
IRouterProduct iRouterProduct = xiaomiFactory.irouterProduct();
iRouterProduct.openWifi();
System.out.println("华为系列产品--------------------");
// 小米工厂
HuaweiFactory huaweiFactory = new HuaweiFactory();
iphoneProduct = huaweiFactory.iphoneProduct();
iphoneProduct.callup();
iphoneProduct.sendSMS();
iRouterProduct = huaweiFactory.irouterProduct();
iRouterProduct.openWifi();
}
}
优点:
抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体情况下,创建多个产品族中的产品对象。
缺点:
规定了所有可能被创建的产品集合,产品族中扩展新的产品困难;
增加了系统的抽象性和理解难度
工厂模式小结:
简单工厂模式:虽然某种程度上不符合设计原则,但实际使用最多。针对一定问题的解决方案。
工厂方法模式:不修改已有类的前提下,通过增加新的工厂类实现扩展。针对的是多个产品系列结构。
抽象工厂模式:不可以增加产品,可以增加产品族。针对的是多个产品族结构,一个产品族内有多个产品系列。
5、建造者模式
它提供了一种创建对象的最佳方式
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
主要作用:在用户不知道 对象的建造过程和细节 的情况下就可以直接创建复杂的对象。
用户只需要给出指定复杂对象的类型和内容,建造者牧师负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)
例如:
- 工厂(建造者模式):负责制造汽车(组装过程和细节在工厂内)
- 汽车购买者(用户):你只需要说出你需要的型号(对象的类型和内容),然后直接购买就可以使用了(不需要知道汽车怎么组装的)
// 抽象的建造者
public abstract class Builder {
public abstract Builder builderA(String msg);// 汉堡
public abstract Builder builderB(String msg);// 可乐
public abstract Builder builderC(String msg);// 薯条
public abstract Builder builderD(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
public Builder builderA(String msg) {
product.setBuildA(msg);
return this;
}
@Override
public Builder builderB(String msg) {
product.setBuildB(msg);
return this;
}
@Override
public Builder builderC(String msg) {
product.setBuildC(msg);
return this;
}
@Override
public Builder builderD(String msg) {
product.setBuildD(msg);
return this;
}
@Override
Product getProduct() {
return product;
}
}
public class Test {
public static void main(String[] args) {
// 服务员
Worker worker = new Worker();
// 链式编程 :在原来的基础上,可以自由的自合,如果不组合也有固定的套餐
Product product = worker.builderA("全家桶").builderB("雪碧")
.getProduct();
System.out.println(product.toString());
}
}
6、原型模式
浅克隆:对象的引用类型属性复制的是内存地址,即指向同一个内存,修改一个则两个都变
深克隆:克隆对象的引用类型属性指向一个新的内存地址,内存里存的数值与原对象相同
实现深克隆的方法有很多:
- 改造clone(),将属性也进行clone
- 序列化,反序列化
public class Video implements Cloneable{
private String name;
private Date createTime;
@Override
protected Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
Video v = (Video) obj;
//将这个对象的属性也进行克隆
v.createTime = (Date) this.createTime.clone();
return obj;
}
public Video() {
}
public Video(String name, Date createTime) {
this.name = name;
this.createTime = createTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
return "Video{" +
"name='" + name + '\'' +
", createTime=" + createTime +
'}';
}
}
public class Bilibili {
public static void main(String[] args) throws CloneNotSupportedException {
//原型对象 v1
Date date = new Date();
Video v1 = new Video("狂神说Java", date);
//v1克隆v2
Video v2 = (Video) v1.clone();
System.out.println("v1=>"+v1);
System.out.println("v2=>"+v2);
System.out.println("===============");
date.setTime(213124124);
System.out.println("v1=>"+v1);
System.out.println("v2=>"+v2);
}
}
7、代理模式
提供了一种对目标对象的访问方式,即通过代理对象访问目标对象,代理对象是指具有与被代理对象相同的接口的类,客户端必须通过代理对象与被代理的目标类进行交互。
代理模式主要分为三个角色:客户端,代理类,目标类;而代理类需要与目标类实现同一个接口,并在内部维护目标类的引用,进而执行目标类的接口方法,并实现在不改变目标类的情况下前拦截,后拦截等所需的业务功能。
代理模式的优点:
- 开闭原则:代理类除了是客户类和目标类的中介,还可以通过给代理类增加额外的功能来扩展目标类的功能,这样我们只需要修改代理类而不需要再修改目标类,符合代码设计的开闭原则(对扩展开放,对修改关闭)。代理类主要负责为目标类预处理消息、过滤消息、把消息转发给目标类,以及事后对返回结果的处理等。
- 代理类本身并不真正实现服务,而是同过调用目标类的相关方法,来提供特定的服务。真正的业务功能还是由目标类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的目标类。
静态代理
方式一:定义一个公共的接口,实现静态代理
代理类持有目标类对象的引用,即属性包含目标类对象
代码步骤:
-
接口
//租房 public interface Rent { public void rent(); }
-
真实角色
//房东 public class Host implements Rent{ @Override public void rent() { System.out.println("房东要出租房子!"); } }
-
代理角色
public class Proxy implements Rent{ private Host host; public Proxy() { } public Proxy(Host host) { this.host = host; } @Override public void rent() { seeHouse(); host.rent(); sign(); fee(); } //看房 public void seeHouse() { System.out.println("中介带你去看房"); } //收费 public void fee() { System.out.println("收中介费"); } //签合同 public void sign() { System.out.println("签合同"); } }
-
客户端访问代理角色
public class Client { public static void main(String[] args) { //房东要租房子 Host host = new Host(); //代理,中介帮房东租房子,代理角色一般会有附属操作 Proxy proxy = new Proxy(host); //你去找中介租房 proxy.rent(); } }
方式二:代理类直接继承目标类
这样Proxy则拥有了RealSubject的功能,Proxy还可以通过重写RealSubject中的方法,来实现多态。
动态代理
-
静态代理是由程序员创建或特定工具自动生成代理类,再对其编译,在程序运行之前,代理类.class文件就已经被创建了。
-
动态代理是在程序运行时通过反射机制动态创建代理对象。
反射详解:Java反射详解 - 梦小冷 - 博客园 (cnblogs.com)
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的,不是我们写好的
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口:JDK 动态代理【我们在这里使用】
- 基于类:cglib
- java字节码实现:javasist
使用基于接口的动态代理,需要了解两个类:
- Proxy:代理
- InvocationHandler:调用处理程序
Proxy 是Java 的一个类
InvocationHandler 是 Java的一个接口,里面只有一个invoke() 方法
对被代理对象的方法的访问都会落实到代理者的invoke上来
1.动态生成代理类
Proxy.newProxyInstance(代理对象类加载器,代理接口类,InvocationHandler)
2.调用方法
自动调用InvocationHandler
类的method.invoke(代理接口对象,args)
方法执行
//租房接口
public interface Rent {
public void rent();
}
//目标类:房东
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要出租房子!");
}
}
//用这个类动态生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成得到代理类
public Object getProxy() {
return Proxy.newProxyInstance(rent.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
}
//处理代理实例 并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//动态代理的本质就是使用反射机制
Object result = method.invoke(rent, args);
return result;
}
}
//客户端
public class Client {
public static void main(String[] args) {
//目标类
Host host = new Host();
//代理类,现在没有
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们要调用的接口对象
pih.setRent(host);
//动态生成的代理类
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)