【狂神说笔记——23种设计模式】

23种设计模式

了解设计模式


  • 设计模式 是前辈们对代码开发经验的总结, 是解决特定问题的一系列套路, 他不是语法规定, 而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案
  • 1995年,GoF合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了23种设计模式,从此树立了软件设计模式领域的里程碑,人称GoF设计模式

学习设计模式的意义

  • 设计模式的本质是面向对象设计原则的实际运用, 是对类的封装性, 继承性和多态性以及类的关联关系和组合关系的充分理解
  • 正确使用设计模式具有以下优点:
    • 可以提高程序员的思维能力, 编程能力和设计能力
    • 使程序设计更加标准化, 代码编制更加工程化, 使软件开发效率大大提高, 从而缩短 软件的开发周期
  • 使设计的代码可重用性高, 可读性强, 可靠性高, 灵活性好, 可维护性强

GoF 23


  • Gof23

    • 一种思维, 一种态度, 一种进步
  • 创建型模式:

    • 单例模式, 工厂模式, 抽象工厂模式, 建造者模式, 原型模式
  • 结构型模式:

    • 适配器模式, 桥接模式, 装饰模式, 组合模式, 外观模式, 享元模式, 代理模式
  • 行为型模式:

    • 模板方法模式, 命令模式, 迭代器模式, 观察者模式, 中介者模式, 备忘录模式, 解释器模式, 状态模式, 策略模式, 职责链模式, 访问者模式

OOP 七大原则


  • 开闭原则: 对扩展开放, 对修改关闭
  • 里氏替换原则: 继承必须确保超类所拥有的性质在子类中仍然成立
  • 依赖倒置原则: 要面向接口编程, 不要面向实现编程
  • 职责原则: 控制类的粒度大小, 将对象解耦, 提高其内聚性
  • 接口隔离原则: 要为各个类建立它们需要的专用接口
  • 迪米特法则: 只与你的直接朋友交谈,不跟"陌生人"说话
  • 合成复用原则: 尽量先使用组合或者聚合等关联关系实现,其次才考虑使用继承关系来实现

单例模式


饿汉式、DCL懒汉式,深究

饿汉式

//饿汉式单例
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懒汉式解决并发问题

public class LazyMan {
   
    private LazyMan(){
   
        System.out.println(Thread.currentThread().getName()+"OK");
    }

    private static LazyMan lazyMan;


    //双重检测锁模式的  懒汉式单例    DCL懒汉式
    public static LazyMan getInstance(){
   
        if(lazyMan==null){
   
            lazyMan = new LazyMan();//不是一个原子性操作
        }
        return lazyMan;
    }
    /*
    * 1.分配内存空间
    * 2、执行构造方法,初始化对象
    * 3、把这个对象指向者个空间
    *
    * 123
    * 132 A
    *
    *     B //此时lazyMan还没有完成构造
    *
    * */


    //多线程并发
    public static void main(String[] args) {
   
        for (int i = 0; i < 10; i++) {
   
            new Thread(()->{
   
                LazyMan.getInstance();
            }).start();
        }
    }
}

DCL懒汉式
注意:synchronized 解决并发问题,但是因为lazyMan = new LazyMan();不是原子性操作(可以分割,见代码注释),可能发生指令重排序的问题,通过volatil来解决

  • Java 语言提供了 volatile和 synchronized 两个关键字来保证线程之间操作的有序性,volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized 是由“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得的,此规则决定了持有同一个对象锁的两个同步块只能串行执行。
  • 原子性就是指该操作是不可再分的。不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来对它进行操作。简而言之,在整个操作过程中不会被线程调度器中断的操作,都可认为是原子性。比如 a = 1;
public class LazyMan {
   
    private LazyMan(){
   
        System.out.println(Thread.currentThread().getName()+"OK");
    }

    private volatile static LazyMan lazyMan;


    //双重检测锁模式的  懒汉式单例    DCL懒汉式
    public static LazyMan getInstance(){
   
        if(lazyMan==null){
   
            synchronized (LazyMan.class){
   //synchronized加锁解决多线程下的问题
                if(lazyMan == null){
   
                    lazyMan = new LazyMan();//不是一个原子性操作
                }
            }

        }
        return lazyMan;
    }
    /*
    * 1.分配内存空间
    * 2、执行构造方法,初始化对象
    * 3、把这个对象指向者个空间
    *
    * 123
    * 132 A
    *
    *     B //此时lazyMan还没有完成构造
    *
    * */


    //多线程并发
    public static void main(String[] args) {
   
        for (int i = 0; i < 10; i++) {
   
            new Thread(()->{
   
                LazyMan.getInstance();
            }).start();
        }
    }
}

静态内部类

//静态内部类
public class Holder {
   

    //构造器私有
    private Holder(){
   

    }
    public static Holder getInstance(){
   
        return InnerClass.HOLDER;
    }

    public static class InnerClass{
   
        private static final Holder HOLDER = new Holder();

    }
}

**单例不安全,反射破坏(**见注释及main方法中反射破解步骤)

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

//单例懒汉式
//懒汉式单例

public class LazyMan {
   
    private static boolean qingjiang = false;//红绿等解决通过反射创建对象(反编译可以破解该方法)
    private LazyMan(){
   
        synchronized (LazyMan.class){
   
            if (qingjiang==false){
   
                qingjiang = true;
            }else{
   
                throw new RuntimeException("不要试图使用反射破坏单例");
            }

        }
        System.out.println(Thread.currentThread().getName()+"OK");
    }

    private volatile static LazyMan lazyMan;//volatile避免指令重排


    //双重检测锁模式的  懒汉式单例    DCL懒汉式
    public static LazyMan getInstance(){
   
        if(lazyMan==null){
   
            lazyMan = new LazyMan();//不是一个原子性操作
        }
        return lazyMan;
    }

//反射!
public static void main(String[] args) throws Exception {
   
    //LazyMan instance = LazyMan.getInstance();
    Field qingjiang = LazyMan.class.getDeclaredField("qingjiang");
    qingjiang.setAccessible(true);

    Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
    declaredConstructor.setAccessible(true);//无视私有的构造器
    LazyMan instance1 = declaredConstructor.newInstance();
    qingjiang.set(instance1,false);
    System.out.println(instance1);
    LazyMan instance2 = declaredConstructor.newInstance();

    System.out.println(instance2);

}

    /*
    * 1.分配内存空间
    * 2、执行构造方法,初始化对象
    * 3、把这个对象指向者个空间
    *
    * 123
    * 132 A
    *
    *     B //此时lazyMan还没有完成构造
    *
    * */


    //多线程并发
   /* public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                LazyMan.getInstance();
            }).start();
        }
    }*/
}

枚举: 通过反射破解枚举发现不成功:
1、普通的反编译会欺骗开发者,说enum枚举是无参构造
2、实际enum为有参构造(见后面);
3、通过反射破解枚举会发现抛出异常
Exception in thread “main” java.lang.IllegalArgumentException: Cannot reflectively create enum objects at java.lang.reflect.Constructor.newInstance(Constructor.java:417) at com.ph.single.Test.main(EnumSingle.java:19)

import java.lang.reflect.Constructor;

//enmu是什么?本身也是一个class类
public enum EnumSingle {
   
    INSTANCE;
    public EnumSingle getInstance(){
   
        return INSTANCE;
    }
}

class Test{
   
    public static void main(String[] args) throws Exception {
   
        EnumSingle instance = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);
        EnumSingle instance2 = declaredConstructor.newInstance();

        //java.lang.NoSuchMethodException: com.ph.single.EnumSingle.<init>()
        System.out.println(instance);
        System.out.println(instance2);

    }
}

通过idea和jdk自带的反编译枚举如下:

请添加图片描述

通过jad反编译枚举的代码如下

请添加图片描述
发现枚举是有参构造

工厂模式


  • 作用:

    • 实现了创建者和调用者的分离
    • 详细分类
      • 简单工厂模式
      • 工厂方法模式
      • 抽象工厂模式
  • OOP七大原则

    • 开闭原则: 一个软件的实体应当对扩展开放, 对修改关闭
    • 依赖倒转原则: 要针对接口编程, 不要针对实现编程
    • 迪米特法则: 只与你直接的朋友通信, 而避免和陌生人通信
  • 核心本质

    • 实例化对象不使用new, 用工厂方法代替
    • 将选择实现类, 创建对象统一管理和控制. 从而将调用者跟我们的实现类解耦
  • 三种模式:

    • 简单工厂模式
      • 用来生产统一等级结构中的任意产品(对于增加新的产品,需要覆盖已有代码)
    • 工厂方法模式
      • 用来生产同一等级结构中的固定产品(支持增加任意产品)
    • 抽象工厂模式
      • 围绕一个超级工厂创建其他工厂, 该超级工厂又称为其他工厂的工厂

简单工厂模式

Car

package com.kuang.factory.simple;

public interface Car {
   
    void name();
}

123456

Tesla

package com.kuang.factory.simple;

public class Tesla implements Car {
   
    public void name() {
   
        System.out.println("特斯拉");
    }
}

12345678

WuLing

package com.kuang.factory.simple;

public class WuLing implements Car {
   
    public void name() {
   
        System.out.println("五菱宏光");
    }
}
1234567

CarFactory

package com.kuang.factory.simple;

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(
  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值