设计模式笔记

设计模式

分类:

  • 创建型模式:(描述怎样去创建一个对象,创建和使用分离)
    • 单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式
  • 结构型模式:(描述如何将类或对象安装某种类型组成更大的结构)
    • 适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
  • 行为型模式:(描述类和对象如何可以相互协作)
    • 模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式

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
  • 将选择实现类,创建对象统一管理和控制。从而将调用者跟我们的实现类解耦

简单工厂模式

1652953911339

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();
    }
}

工厂方法模式

1652954022716

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、抽象工厂模式

1652954646044

1652954791311

// 手机产品接口
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、建造者模式

它提供了一种创建对象的最佳方式

定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

主要作用:在用户不知道 对象的建造过程和细节 的情况下就可以直接创建复杂的对象

用户只需要给出指定复杂对象的类型和内容,建造者牧师负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)

例如:

  • 工厂(建造者模式):负责制造汽车(组装过程和细节在工厂内)
  • 汽车购买者(用户):你只需要说出你需要的型号(对象的类型和内容),然后直接购买就可以使用了(不需要知道汽车怎么组装的)

1653024198120

1653025735352

// 抽象的建造者
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());
    }
}

1653026044137

6、原型模式

浅克隆:对象的引用类型属性复制的是内存地址,即指向同一个内存,修改一个则两个都变

1653030856733

深克隆:克隆对象的引用类型属性指向一个新的内存地址,内存里存的数值与原对象相同

实现深克隆的方法有很多:

  • 改造clone(),将属性也进行clone
  • 序列化,反序列化

1653030937065

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、代理模式

提供了一种对目标对象的访问方式,即通过代理对象访问目标对象,代理对象是指具有与被代理对象相同的接口的类,客户端必须通过代理对象与被代理的目标类进行交互。

代理模式主要分为三个角色:客户端,代理类,目标类;而代理类需要与目标类实现同一个接口,并在内部维护目标类的引用,进而执行目标类的接口方法,并实现在不改变目标类的情况下前拦截,后拦截等所需的业务功能。

1653032336344

代理模式的优点:

  • 开闭原则:代理类除了是客户类和目标类的中介,还可以通过给代理类增加额外的功能来扩展目标类的功能,这样我们只需要修改代理类而不需要再修改目标类,符合代码设计的开闭原则(对扩展开放,对修改关闭)。代理类主要负责为目标类预处理消息、过滤消息、把消息转发给目标类,以及事后对返回结果的处理等。
  • 代理类本身并不真正实现服务,而是同过调用目标类的相关方法,来提供特定的服务。真正的业务功能还是由目标类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的目标类。

静态代理

方式一:定义一个公共的接口,实现静态代理

代理类持有目标类对象的引用,即属性包含目标类对象

代码步骤:

  1. 接口

    //租房
    public interface Rent {
        public void rent();
    }
    
  2. 真实角色

    //房东
    public class Host implements Rent{
    
        @Override
        public void rent() {
            System.out.println("房东要出租房子!");
        }
    }
    
  3. 代理角色

    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("签合同");
        }
    }
    
  4. 客户端访问代理角色

    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();
    }
}
posted @   柯文先生  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示