23种设计模式--解决程序发展过程中的变化,要找到稳定的部分和变化的部分才能引入设计模式

  1. 单例模式 singleton

    参考:

    https://blog.csdn.net/wayne_lee_lwc/article/details/104228469?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_title~default-8.no_search_link&spm=1001.2101.3001.4242.8

    模式定义:保障一个类只有一个实例,并且提供一个全局访问点(外部不能进行实例化,内部需要实例化一个私有对象),构造方法私有

    典型应用:线程池,数据库连接池,各种managr(mgr) 各种factory ThreadLocal多线程应用

    一般使用spring的bean工厂进行保障
    在这里插入图片描述

    实现:

    1.饿汉模式:一上来就把对象给new好了,你来了就直接可以拿去吃了,JVM保障每种class只load一次,final是不可变对象(内部成员变量值(引用所指向的地址)不会被改变,因为没有set方法,string都是final的

    /*单例模式,饿汉式写法1-静态常量*/
    public class Singleton1 {
    	private static final Singleton1 uniqueInstance = new Singleton1(); //final必须初始化
    	
    	private Singleton1(){}
    	
    	public static Singleton1 getInstance()
    	{
    		return uniqueInstance;
    	}
    }
    

    优点:不用考虑线程问题

    缺点:类进行加载时便进行了实例化,浪费内存

    2.饿汉模式,静态代码块写法

    /*单例模式,饿汉式写法2-静态代码块*/
    
    public class Singleton2 {
    	private Singleton2(){};
    	private static Singleton2 uniqueInstance;
    	
    	static 
    	{
    		uniqueInstance = new Singleton2();
    	}
    	
    	public static Singleton2 getInstance(){
    		return uniqueInstance;
    	}
    }
    

    特点:在装载类时,在初始化顺序上先静态变量后静态代码块,所以写法和上一个相同,相当于将定义和初始化分开写的静态版本,优缺点相同

    3.懒汉模式,线程不安全型:一开始懒,不创建对象,等你来找我,我再给你创建一个对象,线程不安全型

    /*单例模式,懒汉式写法,懒加载线程不安全型*/
    
    public class Singleton3 {
    	private Singleton3(){}
    	private static Singleton3 uniqueInstance;
    	public static Singleton3 getInstance()
    	{
    		if(uniqueInstance == null)
    		{
    			uniqueInstance = new Singleton3();
    		}
    		return uniqueInstance;
    	}
    }
    
    

    优点:避免过早实例化占用内存,

    缺点:在if语句这里线程不安全,可能出现多个实例对象

    4.懒汉模式,线程安全型:在获取对象方法前增加synchronized来实现该方法的线程同步,保障线程安全

    /*单例模式,懒汉式写法,懒加载线程安全*/
    
    public class Singleton4 {
    	private Singleton4(){}
    	private static Singleton4 uniqueInstance;
    	public static synchronized Singleton4 getInstance()
    	{
    		if(uniqueInstance == null)
    		{
    			uniqueInstance = new Singleton4();
    		}
    		return uniqueInstance;
    	}
    }
    

    优点:线程安全

    缺点:在线程同步过程中造成不小的延迟,问题:有多少延迟?,仍有改进余地

    5.懒汉模式,双重检查型:为减小延迟,提高效率,将同步从对方法转为对类,缩小同步代码块,加入两重判断防止重复进入,同时对对象加上volatile关键字修饰(可以粗浅理解为一个轻量的synchronized,在线程中每次操作都对变量的值进行重读)

    参考:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

volatile 关键字用来解决指令重排序问题

/*单例模式,双重检查,懒加载高效线程安全*/

public class Singleton5 {
	
	private static volatile Singleton5 uniqueInstance;
	
	private Singleton5(){}
	
	public static Singleton5 getInstance()
	{
		if(uniqueInstance == null)
		{
			synchronized(Singleton5.class){
				if(uniqueInstance == null)
				{
					uniqueInstance = new Singleton5();
				}
			}
		}
		
		return uniqueInstance;
	}
}

优点:保障线程安全和懒加载,是实际应用中常用的推荐写法

缺点:无

6.懒汉模式,静态内部类型:该设计十分巧妙,将对象定义在静态内部类中,由于内部静态类不随类加载,且加载时线程安全特性,所以将懒加载,线程安全,效率问题一并解决,JVM保障每种class只load一次

/*单例模式,静态内部类,懒加载高效线程安全*/

public class Singleton6 {

	private Singleton6(){}
	
	private static class LazyHolder
	{
		private final static Singleton6 uniqueInstance = new Singleton6();
	}
	
	public static Singleton6 getInstance()
	{
		return LazyHolder.uniqueInstance;
	}
}

优点:回归最初的简易逻辑,更简洁,没有性能损耗,是实际使用时常用的推荐写法

缺点:无

7.枚举单例:Java圣经《Effective Java》中,Joshua Bloch提供的方法,最完美的方法,根因是:枚举类没有构建方法,枚举类在字节码层面是abstract class

参考:https://www.jianshu.com/p/d9d9dcf23359

public enum Singleton {

    INSTANCE;

    public void doSomething() {
        System.out.println("doSomething");
    }

}
调用方法:

public class Main {

    public static void main(String[] args) {
        Singleton.INSTANCE.doSomething();
    }

}

直接通过Singleton.INSTANCE.doSomething()的方式调用即可。方便、简洁又安全。

优点:可以防御反射调用和序列化攻击,最完美的方法

缺点:无

  1. 策略模式 strategy

    jdk1.8之后接口类中才有方法实现,而且是必要要实现comapre方法和eauqls方法

    重点理解Comparable接口(中需要自己实现compareTo方法,只有一个比较策略)和Comparator接口(才是策略模式,传参中带有new的比较器类对象,比Comparable的优点是可以实现多种比较器对象,实现多种比较策略,例如sort算法实现了这个接口)

    策略类一般都是interface接口类中默认都是public,也可以使用泛型来定义

    模式定义:变化当中有稳定的部分,解决某一类问题的方法簇,将统一的方法定义成策略接口类(名字一般是xxxable),遵循开闭原则,对新需求新增加代码开放,对修改原有代码关闭。(定义了算法家族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化不会影响到使用算法的用户)

    override 重写

    典型应用:Arrays.sort中的compare比较器自定义 可以代码重构合并大量的if-else-模块 org.springframework.beans.factory.support.InstantiationStrategy 游戏角色(移动是一个接口类,攻击是一个接口类等等,一个抽象类把这些接口组合到一块,使用的时候构造函数需要传入移动攻击的new对象)
    支付时选择支付方式场景

    参考:https://blog.csdn.net/weixin_43533825/article/details/104304015

可以用来避免多重判断语句 ,取代大量的if-else和switch语句
img

  1. 工厂方法模式 Factory Method

    模式定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类,Factory Method使得一个类的实例化延迟到子类

    典型应用:各种factory 1. 当不知道该使用对象的确切类型的时候 2.当希望为库或者框架提供扩展其内部组件的方法时

    优点:1. 将具体产品和创建者解耦 2.符合单一职责原则 3.符合开闭原则

    把稳定的部分抽出来形成interface接口或者abstract抽象类,然后在子类中new各自不同的对象

    new对象是不变的部分时可以使用工厂方法模式(使用不同子类继承父类,然后子类中new不同的子类对象)或者简单工厂(不是设计模式,是一种编程习惯,传入type,使用if分支对type进行判断走不同分支)

  2. 抽象工厂模式 Abstract Factory

    模式定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类

    典型应用:不同数据库产品的连接和命令发送(产品类中new连接对象和命令发送对象,业务代码中new产品类)Connection 接口类 1. 程序需要处理不同系列的相关产品,但是又不希望它依赖于这些产品的具体类时可以使用抽象工厂

    优点:

    1. 可以确信你从工厂得到的产品时彼此是兼容的
    2. 可以避免具体产品和客户端代码之间的紧密耦合
    3. 符合单一职责原则(mysql只关注自己系列的实现,oracl只关注自己的实现)
    4. 符合开闭原则

    抽象工程模式和工厂方法模式有什么区别?

    工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。

    工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。

  3. 建造者模式

    模式定义:先new一个builder建造者(get/set/build在这个类中实现),然后new一个director指导者(在这个类中指定创建顺序),然后在用指导者按照参数进行创建product

    典型应用:

    1. 需要生成的对象具有复杂的内部结构

    2. 需要生成的对象内部属性本身相互依赖

    3. 有不可变对象配合使用

      RequestMappingInfo
      BeanDefinitionBuilder
      

    优点:可以使代码高内聚,product中不用写默认值 1. 建造者独立,易扩展 2.便于控制细节风险

    建造者模式和有参构造函数有什么区别?

    1. 当一个对象创建有参构造时,原有的无参构造会被取代,编译会报The constructor Man() is undefined; 只能新增加无参构造函数

    2. 当有N个参数的时候,且参数必填与非必填不确定因素多的实体,建造者可以更好来处理

      参考:https://www.jianshu.com/p/916c38158b8c/

      建造者模式包含:

      1. 一个实体(Object):要被创建的对象
      2. 一个接口(BuilderInterface):提供建造方法
      3. 一个实现类(BuilderImplement):为各个属性赋值
      4. 一个建造方法(BuilerContract):用来把各个属性组装成需要的实体
  4. 原型模式 Prototype

    模式定义:指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,原型模式主要用于对象的复制,它的核心是就是类图中的原型类Prototype

    典型应用:当代码不应依赖于需要复制的对象的具体类时,请使用原型模式

    AbstractBeanDefinition
    java.util.Arrays
    

    优点:

    1. 可以不耦合具体类的情况下克隆对象
    2. 避免重复的初始化代码(直接内存复制,不走类的构建函数,因此单例模式和原型模式是冲突的,使用时要注意)
    3. 更方便的构建复杂对象,比直接new一个对象在性能上要好得多,原型模式的clone时本地方法,性能好

    8中基本数据类型都是不可变类型 immutable,所以clone只能拷贝这8中数据类型

    浅拷贝和深拷贝的应用对比?通过序列化实现深拷贝为什么不推荐?

    • 深拷贝与浅拷贝。Object类的clone方法只会拷贝对象中的基本的数据类型,对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。
      例如:

    • public class Prototype implements Cloneable {
      	private ArrayList list = new ArrayList();
      	public Prototype clone(){
      		Prototype prototype = null;
      		try{
      			prototype = (Prototype)super.clone();
      			prototype.list = (ArrayList) this.list.clone();
      		}catch(CloneNotSupportedException e){
      			e.printStackTrace();
      		}
      		return prototype; 
      	}
      }
      

      由于ArrayList不是基本类型,所以成员变量list,不会被拷贝,需要我们自己实现深拷贝,幸运的是java提供的大部分的容器类都实现了Cloneable接口。所以实现深拷贝并不是特别困难。

      PS:深拷贝与浅拷贝问题中,会发生深拷贝的有java中的8中基本类型以及他们的封装类型,另外还有String类型。其余的都是浅拷贝。
      参考:https://www.cnblogs.com/fengyumeng/p/10646487.html

  5. 享元模式 Flayweight

    模式定义:运用共享技术有效地支持大量细粒度的对象,所有不同的类对象只创建一次

    典型应用:游戏中的树,使用***CocurrentHashMap***来保存树的元数据,如果已经存在直接从工厂类中返回已有树,如果没有再在map中增加元数据并返回

    java.lang.String
    java.lang.Integer
    com.sun.org.apache.bcel.internal.generic.InstructionConstants
    

    优点:如果系统有大量类似的对象,可以节省大量的内存和CPU资源

    为什么要共享?什么对象才共享?

    共享可以节省cpu,内存资源;共享了需要保证线程安全,数据一致性,不可变对象,例如string这种不可变对象,直接放到全局变量区

  6. 门面模式 Facade(外观模式)

    模式定义:为子系统中的一组接口提供一个一致的接口,Facade 模式定义了一个高层接口,这个接口使得这一子系统更容易使用

    典型应用:系统级应用(按钮) requestfacade(拿到数据的是这个类,但处理实在下层的coyote来处理)

    优点:简化客户端的调用

  7. 适配器模式 Adapter

    模式定义:将一个类的接口转换成客户希望的另一个接口,Adapter模式使得原本由于接口不兼容而不能一块工作的那些类可以一起工作,就像手机充电器

    典型应用:ApplicationListener

    1. 当希望使用某些现有类,但其接口与其他代码不兼容时,可以使用适配器类
    2. 当希望重用几个现有的子类,这些子类缺少一些不能添加到超类中的公共功能时,使用该模式

    优点:

    1. 符合单一职责原则
    2. 符合开闭原则

    对象的适配器(使用组合,需要使用原始对象作为参数构建新对象) 类的适配(使用继承,会导致接口污染误用,不符合迪米特原则,最少知道原则)

  8. 装饰着模式 Decorator

    模式定义:在不改变原有对象的基础上,将功能附加到对象上,可以是一个抽象类

    典型应用:HttpServletRequestWrapper 拓展一个类的功能,或给一个类增加附加职责

在这里插入图片描述

优点:

1. 不改变原有对象的情况下给一个对象扩展功能
2. 使用不同的组合可以实现不同的效果
3. 符合开闭原则

**方法定义成抽象的**可以让子类去具体实现来扩展不一样的功能
  1. 模板方法模式 Template Method

    模式定义:定义一个操作的算法股价,而将一些步骤延迟到子类中。模板方法类使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。最主要的是不能用interface接口类,而是要用AbstractClass抽象类

    典型应用:在模板类中写一个方法,并编到执行顺序中,子类来实现 jakarta.servlet.http.HttpServlet#service(jakarta.servlet.http.HttpServletRequest, jakarta.servlet.http.HttpServletResponse) 用户需要重写doGet 。。。等方法

    优点:大的算法骨架不发生改变,又能切入用户自己通过子类实现的个性代码(父类的算法骨架中已经调用方法接口)

  2. 观察者模式 Observer

    模式定义:定义了对象之间的一对多依赖,让多个观察者对象同时监听某一个主题对象,当主题对象发生变化时,他的所有依赖者都会收到通知并更新

    典型应用:java.util.Observable ApplicationListener

    优点:

    1. 符合开闭原则
    2. 可以在运行时建立对象之间的关系

    主题对象能够增删和通知观察者(可以定义为接口类,产生多个子类)

    主题对象需要有一个容器,更新时遍历容器去通知。

  3. 责任链模式 Chain Of Responsibility

    模式定义:为请求创建了一个接收者对象的链

    典型应用:jakarta.servlet.Filter jakarta.servlet.FilterChain xxxHandler类

    优点:

    1. 请求的发送者和接受者解耦
    2. 可以控制执行顺序
    3. 符合开闭原则和单一职责原则

    责任链 Handler可以定义为一个抽象类,里边包含一个next和process(),setnext()

在这里插入图片描述

参考:

http://c.biancheng.net/view/1359.html
  1. 代理模式
    模式定义:为其他对象提供一种代理,以实现控制对这个对象的访问,代理对象在客户端和目标对象之间起到中介作用。属于结构型设计模式
    分为:静态代理 (显示的声明被代理对象,对对象有限制,不符合开闭原则,对象变化了代理类也要变化),动态代理模式(对对象没有限制
    目的:保护对象 , 增强对象
    JDK是采用读取接口的信息(对应用户而言必须有一个接口实现(接口数小于65535),目标类相对较复杂,生成的代理逻辑简单,执行效率相对较低,每次反射动态调用)
    CGLib时通过覆盖父类方法(可以代理任何一个类,没有任何要求,生成的代码逻辑更复杂,效率低,但调用效率更高,生成了一个包含所有逻辑的的FastClass,不再需要反射调用)

CGLib有个坑,不能代理final的方法。

目的:都是生成一个新的类,去实现增强代码逻辑的功能。

  1. 委派模式 Delegate Pattern
    模式定义:基本作用就是负责任务的调度和分配任务,跟代理模式很像,可以看做是一种特殊情况下的静态代理的全权代理,但是代理模式注重过程,而委派模式注重结果。
    不属于GOF 23种设计模式之一

BOSS -> Leader -> EmployeeA 擅长加密
-> EmployeeB 擅长架构
在这里插入图片描述

属于行为型模式
典型应用:以Delegate或者dispatcher结尾的类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值