设计模式
一、代理模式
1.静态代理
- 静态代理的特点:
①目标类和代理类都实现了同一接口
②比较容易实现
③在不修改目标类核心功能的前提下,很容易就可以对目标l类进行功能的增强
④缺点:因为目标类和代理类实现了同一接口,此时接口一旦发生改变,两个类都必须同时实现新增的功能(动态代理可以解决这一缺点)
/**
*共同的接口
*/
public interface SingPerform {
//举办演唱会
void sing();
//void play();
//void xidu();
}
/**
* 目标类:做主要工作的类.
*/
public class Singer implements SingPerform {
@Override
public void sing() {
System.out.println("大歌星开始唱歌啦....");
}
}
/**
* 代理类:执行辅助性的一些功能
* 利用有参构造函数引入目标类
* 对目标类的核心功能进行增强
*/
public class Broker implements SingPerform {
private Singer singer;
public Broker(Singer singer) {
this.singer = singer;
}
@Override
public void sing() {
System.out.println("经纪人开始准备演唱会啦...");
singer.sing();
System.out.println("经纪人为演唱会善后...");
}
}
/**
*静态代理测试
*/
public class StaticProxyTest {
public static void main(String[] args){
Singer singer=new Singer();
Broker broker=new Broker(singer);
broker.sing();
/* 结果:"经纪人开始准备演唱会啦..."
"大歌星开始唱歌啦...."
"经纪人为演唱会善后..." */
}
}
2.动态代理
- 动态代理的特点
①目标类实现接口,代理类不实现接口
②以后目标类根据需要扩展接口的功能,而不会影响到代理类
③利用反射实现,性能不高
/**
*目标类的接口
*/
public interface SingPerform {
//举办演唱会
void sing();
//void play();
//void xidu();
}
/**
* 目标类:做主要工作的类.
*/
public class Singer implements SingPerform {
@Override
public void sing() {
System.out.println("大歌星开始唱歌啦....");
}
}
/**
* 代理类:用来执行一些辅助性工作的类.
*/
public class Broker {
public void prepareSing() {
System.out.println("经纪人开始准备演唱会...");
}
public void afterSing() {
System.out.println("经纪人为演唱会善后...");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理工厂:在这里具体实现动态代理
*/
public class ProxyFactory {
private Object target;//Singer
private Broker broker;
public ProxyFactory(Object target, Broker broker) {
this.target = target;
this.broker = broker;
}
//创建出以目标类为模板的代理对象的方法
public Object newInstance() {
Class<?> clazz = this.target.getClass();
//得到目标类的类加载器
//ClassLoader classLoader = clazz.getClassLoader();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new InvocationHandler() {
//创建出一个目标类的代理对象: 以目标类为模板,克隆出目标类的一个副本,然后以此为模板创建出一个对象,该对象就是目标类的代理对象
//proxy:就是克隆出的代理对象(歌星的分身)
//method:歌星的分身要执行的方法--->sing(),play(),xidu()
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
broker.prepareSing();
//target.sing();
//target.method(args)
Object obj = method.invoke(target, args);
broker.afterSing();
return obj;
}
});
}
}
/**
*动态代理的测试
*/
public class DynamicProxyTest {
public static void main(String[] args) {
Singer target = new Singer();
Broker broker = new Broker();
ProxyFactory factory = new ProxyFactory(target, broker);
//注意:此处必须转换为目标类的父类:接口
SingPerform perform = (SingPerform) factory.newInstance();
perform.sing();
}
}
3.Cglib代理
- Cglib代理的特点
①代理类和目标类都无需实现接口
②但是以目标类为父类,创建出一个功能增强的子类
③Spring中AOP的实现原理就是用的代理模式–>动态代理+Cglib代理
(如果目标类实现了接口,Spring底层实现AOP就调用动态代理,否则就调用Cglib代理)
/**
*
* 1.目标类UserDaoImpl:执行核心功能,CRUD.
*
* 在进行增删改的时候,自动为这3个方法添加事务功能,查询方法除外
* 事务的具体实现逻辑:
* ① 开启事务;
* ② 执行核心功能代码;
* ③ 提交事务.
*
*
*/
public class UserDaoImpl {
public void insert(){
//System.out.println("开启事务...");
System.out.println("添加用户");
//System.out.println("提交事务...");
}
public void delete(){
//System.out.println("开启事务...");
System.out.println("删除用户");
//System.out.println("提交事务...");
}
public void update(){
System.out.println("修改用户");
}
public void find(){
System.out.println("查询用户");
}
}
/**
* 2.代理类:执行辅助性的额外功能.
*/
public class Transaction {
public void beginTransaction(){
System.out.println("开启事务了...");
}
public void commit(){
System.out.println("提交事务了...");
}
}
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
*3.Cglib代理类
*
*/
public class DaoInterceptor implements MethodInterceptor {
private Object target;//目标类--->只能进行crud;
private Transaction transaction;//代理类--->只有事务的代码
public DaoInterceptor(Object target, Transaction transaction) {
this.target = target;
this.transaction = transaction;
}
//创建出一个Cglib代理对象:执行带有事务功能的crud任务.也就是该对象把事务与crud合二为一了!
//proxy=target+transaction
public Object createProxyInstance() {
//Proxy.newProxyInstance(classloader,interfaces,invocationHandler);
//Enhancer增强者:给目标类增强,添加事务功能
Enhancer enhancer = new Enhancer();
//为什么要获取目标类的字节码?
Class<?> clazz = this.target.getClass();
enhancer.setClassLoader(clazz.getClassLoader());
enhancer.setInterfaces(clazz.getInterfaces());
//设置增强的回调函数
enhancer.setCallback(this);
//指定目标类,作为父类,以此为模板,创建出一个"儿子"出来
enhancer.setSuperclass(clazz);
//创建出一个增强目标类
return enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
String methodName = method.getName();
System.out.println("methodName=" + methodName);
if ("find".equals(methodName)) {
//直接执行find方法,不做额外操作
method.invoke(target, args);
} else {
//对 增删改 进行了 增强!
transaction.beginTransaction();
method.invoke(target, args);
transaction.commit();
}
return null;
}
}
/**
*Cglib代理测试类
*/
public class CglibTest {
public static void main(String[] args) {
UserDaoImpl target = new UserDaoImpl();
Transaction transaction = new Transaction();
DaoInterceptor interceptor = new DaoInterceptor(target, transaction);
//一定要转换成父类类型!
UserDaoImpl proxyInstance = (UserDaoImpl) interceptor.createProxyInstance();
//proxyInstance.insert();
proxyInstance.find();
}
}
二、单例模式
- 特点:
- 1.单例类只能有一个实例。
- 2.单例类必须自己创建自己的唯一实例。
- 3.对外提供一种访问其唯一对象的方式。
- 意图:保证一个类仅有一个实例,并提供一个全局访问点。
- 主要解决:一个全局使用的类,频繁的创建和销毁。(例:一个班级只有一个班主任)
1.非线程安全懒汉式
- 是否延迟初始化:是
public class Singleton{
private static Singleton instance;
private Singleton(){}//私有化的构造函数
//对外提供方法获取唯一实例
public static Singleton getInstance(){
if(instance==null){
instance = new Singleton();
}
return instance;
}
}
2.线程安全懒汉式
- 是否延迟初始化:是
- 优点:第一次调用才初始化,避免内存浪费。
- 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3.饿汉式(线程安全的):一般情况下建议使用
- 是否延迟初始化:否
- 优点:没有加锁,执行效率会提高。
- 缺点:类加载时就初始化,浪费内存。
public class Singleton{
private static Singleton instance = new Singleton();
private Singleton(){};
public static Singleton getInstance(){
return instance;
}
}
4.双重校验锁(DCL,即 double-checked locking,线程安全的)
-
JDK 版本:JDK1.5 起
-
是否 延迟初始化:是
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
5.静态内部类(线程安全)
- 是否延迟初始化:是
public class Singleton{
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
private Singleton(){}
public static final Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
6.枚举(线程安全)
- JDK版本:JDK1.5起
- 是否延迟初始化:否
*枚举单例演示*
public enum Singleton{
INSTANCE;
public Singleton getInstance(){
return INSTANCE;
}
}
*完整的枚举单例*
public class User {
//私有化构造函数
private User(){ }
//定义一个静态枚举类
static enum SingletonEnum{
//创建一个枚举对象,该对象天生为单例
INSTANCE;
private User user;
//私有化枚举的构造函数
private SingletonEnum(){
user=new User();
}
public User getInstnce(){
return user;
}
}
//对外暴露一个获取User对象的静态方法
public static User getInstance(){
return SingletonEnum.INSTANCE.getInstnce();
}
}
public class Test {
public static void main(String [] args){
User instance = User.getInstance();
User instance2 = User.getInstance();
System.out.println(instance==instance2);//结果为true
}
}
三、简单工厂,工厂方法,抽象工厂
部分引用地址: https://blog.csdn.net/TesuZer/article/details/89415055
1.简单工厂模式
角色组成
- 工厂类:这是本模式的核心,含有一定的商业逻辑和判断逻辑,根据逻辑不同,产生具体的工厂产品。如例子中的Driver类。
- 抽象产品:它一般是具体产品继承的父类或者实现的接口。由接口或者抽象类来实现。如例中的Car接口。如例中的Car接口。
- 具体产品: 工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现,如例子中的Benz、Bmw类。
简单工厂模式例1:
//简单工厂(Car为接口,Benz类和Bmw类均实现Car接口)
class Driver{
public static Car createCar(String car){
Car c = null;
if("Benz".equalsIgnoreCase(car))
c = new Benz();
else if("Bmw".equalsIgnoreCase(car))
c = new Bmw();
return c;
}
}
简单工厂模式例2:
2.工厂方法模式
角色组成
- 抽象工厂:这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。
- 具体工厂:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。在java中它由具体的类来实现。
- 抽象产品:它是具体产品继承的父类或者是实现的接口。
- 具体产品:具体工厂角色所创建的对象就是此角色的实例
工厂方法模式例1:
//抽象工厂
abstract class Driver{
public abstract Car createCar(String car) throws Exception;
}
//具体工厂(每个具体工厂负责一个具体产品)
class BenzDriver extends Driver{
public Car createCar(String car) throws Exception {
return new Benz();
}
}
class BmwDriver extends Driver{
public Car createCar(String car) throws Exception {
return new Bmw();
}
}
工厂方法模式例2:
3. 抽象工厂模式
角色组成
- 抽象工厂:这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。
- 具体工厂:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。在java中它由具体的类来实现。
- 抽象产品:它是具体产品继承的父类或者是实现的接口。
- 具体产品:具体工厂角色所创建的对象就是此角色的实例。
抽象工厂模式(例子1)
- BmwCar和BenzCar就是两个产品树(产品层次结构);BenzSportsCar和BmwSportsCar就是一个产品族。他们都可以放到跑车家族中,因此功能有所关联。同理BmwBussinessCar和BenzBusinessCar也是一个产品族。
//抽象工厂
abstract class Driver3{
public abstract BenzCar createBenzCar(String car) throws Exception;
public abstract BmwCar createBmwCar(String car) throws Exception;
public abstract AudiCar createAudiCar(String car) throws Exception;
}
//一个具体工厂生产跑车系汽车
class SportDriver extends Driver3{
public BenzCar createBenzCar(String car) throws Exception {
return new BenzSportCar();
}
public BmwCar createBmwCar(String car) throws Exception {
return new BmwSportCar();
}
public AudiCar createAudiCar(String car) throws Exception {
return new AudiSportCar();
}
}
//一个具体工厂生产商务系汽车
class BusinessDriver extends Driver3{
public BenzCar createBenzCar(String car) throws Exception {
return new BenzBusinessCar();
}
public BmwCar createBmwCar(String car) throws Exception {
return new BmwBusinessCar();
}
public AudiCar createAudiCar(String car) throws Exception {
return new AudiBusinessCar();
}
}
抽象工厂模式(例子2)
- 抽象工厂模式也就是不仅生产鼠标,同时生产键盘。
- 也就是 PC 厂商是个父类,有生产鼠标,生产键盘两个接口。
- 戴尔工厂,惠普工厂继承它,可以分别生产戴尔鼠标+戴尔键盘,和惠普鼠标+惠普键盘。
- 创建工厂时,由戴尔工厂创建。
- 后续工厂.生产鼠标()则生产戴尔鼠标,工厂.生产键盘()则生产戴尔键盘。
4.工厂方法模式和抽象工厂模式的区别?
- ①抽象产品:工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。
- ②具体工厂类能创建的具体产品类型:工厂方法模式的具体工厂类只能创建一种具体产品类的实例,而抽象工厂模式可以创建多种具体产品类的实例。