设计模式
一、单例模式
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。
懒汉式单例(线程不安全)
//懒汉式单例类.在第一次调用的时候实例化自己
public class Singleton {
private Singleton() {}
private static Singleton single = null;
//静态工厂方法
public static Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
}
饿汉式单例(线程安全)
//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton1 {
private Singleton1() {}
private static final Singleton1 single = new Singleton1();
//静态工厂方法
public static Singleton1 getInstance() {
return single;
}
}
二、适配器模式
适配器所涉及的角色有:
- Target(目标接口):所要转换的所期待的接口(USB)
- Adaptee(源角色):需要适配的类(Type-c)
- Adapter(适配器):将源角色适配成目标接口,Type-c -> USB
- Client(客户类):通过目标角色获取服务(笔记本电脑)
优点:
- 可以让任何两个没有关联的类一起运行。
- 提高了类的复用。
- 增加了类的透明度。
- 灵活性好。
缺点:
- 过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
- 由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。
三、工厂模式
工厂模式又称为创建模式,是GOF23设计模式之一,它是建对象的一种最佳方式。工厂模式的本质就是用工厂方法代替new操作创建一种实例化对象的方式。一句话中总结就是方便创建同种类型接口产品的复杂对象。
举例:实例化一个对象sample 一般会想到的方法是通过构造器来创建sample s=new sample();但是在实际情况下最好不要这样做,如果sample类的在实例化的时候需要初始化参数而这些参数需要别的类的信息,这样你new的话会增加你代码的耦合度,不利于维护。所以,我们就需要将创建实例的工作和使用使用实例的工作分开,即:**使用工厂方法创建实例的工作封装起来。**这样我们在需要调用对象的时候就不需要关心那些复杂的实例化问题。
- 优点:从简单工厂中我们可以看出使用一个静态方法createXXX()将实例化的创建和使用分离开。我们只需要调用方法传递参数就可以获得我们需要的对象。
- 缺点:我们不难看出如果我们想要增加一个产品,不仅需要添加一个该产品的实现类,而且我们必须要修改静态方法createXXX,这样就违背了开闭原则(对于扩展是开放的,对于修改是封闭的)
具体概念:
- 抽象工厂:工厂模式的核心,提供一个创建对象的接口,任何需要创建对象的具体工厂都要实现这个接口。
- 具体工厂:实现抽象工厂接口的类,受到调用者的调用从而创建对象。
- 抽象产品:工厂方法模式所创建的对象的超类。也就是所有具体产品的共同父类。
- 具体产品:实现抽象工厂接口的类。工厂模式中所有创建的对象都是具体产品的实例。
Finally:
无论是简单工厂模式,还是抽象工厂模式,他们都属于工厂模式,在形式和特点上也是极为相似的,他们的最终目的都是为了解耦。在使用时,我们不必去在意这个模式到底工厂方法模式还是抽象工厂模式,因为他们之间的演变常常是令人琢磨不透的。经常你会发现,明明使用的工厂方法模式,当新需求来临,稍加修改,加入了一个新方法后,由于类中的产品构成了不同等级结构中的产品族,它就变成抽象工厂模式了;而对于抽象工厂模式,当减少一个方法使的提供的产品不再构成产品族之后,它就演变成了工厂方法模式。所以,在使用工厂模式时,只需要关心降低耦合度的目的是否达到了。
四、代理模式
代理模式的优点:
- 符合开闭原则,不用修改被代理者任何的代码,就能扩展新的功能
- 项目的扩展和维护比较方便
静态代理
- 代理者和被代理者都实现了相同的接口(或继承相同的父类)
- 代理者包含了一个被代理者的对象
- 调用功能时,代理者会调用被代理者的功能,同时附加新的操作
静态代理只能适合一种业务,如果有新的业务,就必须创建新的接口和新的代理,如添加卖电脑的接口和电脑工厂,就要创建新的电脑代理类。
动态代理
动态代理是一种常用的设计模式,广泛应用于框架中,Spring框架的AOP特性就是应用动态代理实现的,想要理解AOP的实现原理我们就必须先理解动态代理。
动态代理的特点:
- 在不修改原有类的基础上,为原来类添加新的功能
- 不需要依赖某个具体业务
动态代理分为:JDK动态代理和CGLib动态代理。区别是:
- JDK动态代理的被代理者必须实现任意接口
- CGLib动态代理不用实现接口,是通过继承实现的
JDK动态代理
实现步骤:
- 代理类需要实现InvocationHandler接口
- 实现invoke方法
- 通过Proxy类的newProxyInstance方法来创建代理对象
CGLib动态代理
特点:通过继承实现,被代理者必须能被继承,通过被代理类创建子类,子类就是父类的代理。子类重写父类的方法。
Finally:
代理模式分为静态代理和动态代理,静态代理只能代理某一种业务,动态代理可以代理各种业务而不用添加新的代理类,动态代理分为JDK动态代理和CGLib动态代理,JDK动态代理类必须实现某个接口,如果没有实现接口则可以使用CGlib实现。
CGLIB动态代理与JDK动态区别: Java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
在Spring中:
- 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
- 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
- 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换