设计模式、代理模式、动态代理、懒汉式、饿汉式(学习笔记)

一.设计模式

1.什么是设计模式

  设计模式(Design pattern):代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

  简而言之:就是书写代码的一些规律,例如经常开车的人都知道,开车要用一档起步

2. java中的设计模式

java的设计模式大体上分为三大类:一共23种
创建型模式(5种):工厂方法模式抽象工厂模式单例模式建造者模式原型模式
结构型模式(7种):适配器模式装饰器模式代理模式外观模式桥接模式组合模式享元模式
行为型模式(11种):策略模式模板方法模式观察者模式迭代子模式责任链模式命令模式备忘录模式状态模式访问者模式中介者模式解释器模式

3.设计模式遵循的原则有6个:

1、开闭原则(Open Close Principle
  对扩展开放,对修改关闭。

2、里氏代换原则(Liskov Substitution Principle
  只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。

3、依赖倒转原则(Dependence Inversion Principle
  这个是开闭原则的基础,对接口编程,依赖于抽象而不依赖于具体。

4、接口隔离原则(Interface Segregation Principle
  使用多个隔离的接口来降低耦合度。

5、迪米特法则(最少知道原则)(Demeter Principle
  一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

6、合成复用原则(Composite Reuse Principle
  原则是尽量使用合成/聚合的方式,而不是使用继承。继承实际上破坏了类的封装性,超类的方法可能会被子类修改。

二.工厂模式

1.工厂模式(Factory Pattern) 定义:
  是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

  在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

1.工厂模式(Factory Pattern) 定义:

  是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

  在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

2.意图:

  定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

  主要解决:主要解决接口选择的问题。

应用实例:
1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。
2、Hibernate 换数据库只需换方言和驱动就可以。

3.优点:

1、一个调用者想创建一个对象,只要知道其名称就可以了。
2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
3、屏蔽产品的具体实现,调用者只关心产品的接口

4.缺点:

  每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事

5.使用场景:

1、日志记录器:记录可能保存到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
3、设计一个连接服务器的框架,需要三个协议,"POP3""IMAP""HTTP",可以把这三个作为产品类,共同实现一个接口。

6.注意事项:

  作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,
  而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度

7.代码示例

简单工厂模式:就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。
简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(
这些产品类继承自一个父类或接口)的实例
示例代码:
步骤一:创建一个接口/抽象类和抽象方法

package com.rj.bd.factorypattern;
/**
 * @desc   面条的抽象类(基类)
 */
public abstract class Noodels {
public abstract void kindsOfNoodle();//面条的种类
}



步骤二:创建实现类:
package com.rj.bd.factorypattern;
/**
 * @desc   炒类类
 */
public class ChaoMian extends Noodels {

	@Override
	public void kindsOfNoodle()
	{
     System.out.println("此时创造的是:炒面");
	}

}


package com.rj.bd.factorypattern;
/**
 * @desc   兰州拉面类
 */
public class LanZhouLaMian extends Noodels {

	@Override
	public void kindsOfNoodle()
	{
     System.out.println("此时创造的面条为:兰州拉面");
	}

}



package com.rj.bd.factorypattern;
/**
 * @desc   泡面类
 */
public class PaoMian extends Noodels {

	@Override
	public void kindsOfNoodle() 
	{
     System.out.println("此时创造的是:泡面");
	}

}




步骤三:创建简单工厂对象类
package com.sj.bd.mds;
/**
 * @desc   简单工厂类
 */
public class SimpleNoodlesFactory {
	    public static final int TYPE_LM = 1;//兰州拉面
	    public static final int TYPE_PM = 2;//泡面
	    public static final int TYPE_CM = 3;//炒面
	    
	    public static Noodles createNoodles(int type) {
	     case TYPE_PM:
			 return new PaoMian();
		case TYPE_LM:
			 return new LanZhouLaMian();
		case TYPE_CM:
			return new ChaoMian();
		default:
			return new ChaoMian();//默认为炒面类
	    }    
	    	    
}


步骤四:测试类
package com.sj.bd.mds;
/**
 * @desc   测试类:测试简单工厂
 */
public class CeShi {
	public static void main(String[] args) {
         //此时指出本次创建的面条是哪一类的
		 Noodels noodel = SimpleNoodelFactory.getNoodels(SimpleNoodelFactory.LM);
	     noodel.kindsOfNoodle();
		
	}

}

三.代理模式

1.什么是代理模式:
给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用

2.代理模式好处:
可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能,换言之:不改变源码的情况下,实现对目标对象的功能扩展
简而言之:为他人做嫁衣
PS:代理售货,代收发快递,

1.静态代理模式

定义:
1.静态代理:所谓静态代理就是自己要为要(被)代理的类写一个代理类,或者用工具为其生成的代理类,总之,就
是程序运行前就已经存在的编译好的代理类
2. 特点:不灵活,但是清楚的知道代理类是什么/哪个

代码示例

    小结与提问:通过上面的例子,我们可能有个疑问,难道就不能直接去厂家买车吗?当然可以,如果在使用场景
中实现类能满足要求时,
    我们当然可以直接实现类,但当实现类不能满足要求,要扩展需求,根据开闭原则你又不能修改实现类代码,这
时你就用代理类。
    比如购买一辆车我们要对客户进行一个购车款审核,如果符合条件就买车,不符合要求我们就告知客户购车款
不足。

引申:通过代理模式实现权限控制


package com.rj.bd.poxystyle.staticproxystyle2;
/**
 * @desc   接口
 */
public interface ICar {
public void buyCar();
}




package com.rj.bd.poxystyle.staticproxystyle2;

/**
 * @desc 客户类
 */
public class Customer implements ICar {

	public int cash;
	public String name;

	public int getCash() {
		return cash;
	}
	public void setCash(int cash) {
		this.cash = cash;
	}	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public void buyCar() {
		System.out.println(this.name+"买车花了:" + this.cash + "元");
	}
}

package com.rj.bd.poxystyle.staticproxystyle2;
/**
 * @desc   4S店类也就是代理类
 */
public class S4Proxy  implements  ICar{

	public Customer  customer;//被代理的对象
		
	public S4Proxy(Customer  cu) 
	{
		System.out.println("4S店类初始化,也就是开门正式营业了");
		this.customer=cu;//接收买车客户
	}

	@Override
	public void buyCar() 
	{
		//先验资,也就是相当于现实世界中的先付款, 成功了然后才能说买车成功了
	    int cash = customer.getCash();
	   
		if (cash<100000)
		{
		  System.out.println("您的钱不够:先暂时不能买车呢");
		  return ;
		}
		customer.buyCar();
	}

}

package com.rj.bd.poxystyle.staticproxystyle2;
/**
 * @desc   测试类:测试静态代理模式
 */
public class Test {
	public static void main(String[] args) 
	{
        Customer  customer=new Customer();
        customer.setName("王百万");
		customer.setCash(200000);
		S4Proxy  proxy=new S4Proxy(customer);
		proxy.buyCar();
		
		System.out.println("-----------------------------");
		
		Customer  customer2=new Customer();
        customer2.setName("李美丽");
		customer2.setCash(80000);
		S4Proxy  proxy2=new S4Proxy(customer2);
		proxy2.buyCar();
	}

}

2.动态代理解释+jdk动态代理

1.导读模块:
  相比静态代理,动态代理具有更强的灵活性,因为它不用在我们设计实现的时候就指定某一个代理类来代理哪一个被代理对象,我们可以把这种指定延迟到程序运行时由JVM来实现

2.什么是动态代理:
  在程序运行时,运用反射机制动态创建而成

3.导读:
  在java的java.lang.reflect包下提供了一个Proxy(代理)类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。

4.动态代理好处
  使用Java动态代理机制的好处:

1、减少编程的工作量:假如需要实现多种代理处理逻辑,只要写多个代理处理器就可以了,无需每种方式都写一个代理类。

2、功能的扩展:系统扩展性和维护性增强,程序修改起来也方便多了(一般只要改代理处理器类就行了)。

5.动态代理的使用场景:
  在后期的商业框架中很多都有用到的,例如Spring,Struts2

6.JDK动态代理:
   JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的 业务实现类对象 以及方法名 ,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的,只需指定代理类的预处理、调用后操作即可

  简而言之:就是通过绑定接口与实现类,进而实现代理的
7.实现步骤:
1)首先,定义业务逻辑接口
2)然后,实现业务逻辑接口创建业务实现类
3)最后,实现 调用管理接口InvocationHandler 创建动态代理(工具)类
4)在使用时,首先创建一个业务实现类对象和一个代理类对象,然后定义接口引用(这里使用向上转型)
并用代理对象.bind(业务实现类对象)的返回值进行赋值。最后通过接口引用调用业务方法即可。
(接口引用真正指向的是一个绑定了业务类的代理类对象,所以通过接口方法名调用的是被代理的方法们)
8.代码解释

package com.rj.bd.poxystyle.JDKDynamicProxy;
/**
 * @desc   接口
 */
public interface ICar {
public void buyCar();
}



package com.rj.bd.poxystyle.JDKDynamicProxy;

/**
 * @desc 客户类
 */
public class Customer implements ICar {

	public int cash;
	public String name;

	public int getCash() {
		return cash;
	}

	public void setCash(int cash) {
		this.cash = cash;
	}

	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public void buyCar() {
		System.out.println(this.name+"买车花了:" + this.cash + "元");
	}

}


package com.rj.bd.poxystyle.JDKDynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @desc   动态代理工具类
 */
public class DymaicProxy  implements InvocationHandler {

public Object obj;//被代理的对象

	
	
	
public DymaicProxy(Object obj)
{
	this.obj = obj;//接受客户
}

/**
 * invoke():回调
 *   参数介绍:
 *         第一个参数:被代理的真实对象
 *         第二个参数:代表被动态代理类调用的方法。从中可得到方法名,参数类型,返回类型等等
 *         第三个参数:代表被调用方法的参数
 *  额外补充:因为是动态代理,所以可以实现一些特殊的功能,可以扩展增强其功能,
 *  怎样实现呢:before:dosomething();after:dosomething()
 */
	@Override
public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
	
		//before:dosomething();
		Object result = method.invoke(this.obj, args);
	    //after:dosomething()
		return result;
	}

}



package com.rj.bd.poxystyle.JDKDynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * @desc   测试类:测试JDK动态代理模式
 */
public class Test {
	public static void main(String[] args) 
	{
        Customer  customer=new Customer();
        customer.setName("王百万");
		customer.setCash(200000);
		//Java反射之getInterfaces()方法:可以获取某一类对像所所实现的接口
		//System.out.println(customer.getClass().getInterfaces()[0]);
		
	   InvocationHandler  handler=new DymaicProxy(customer);
	   
	   
	   /*
        * 通过Proxy类的newProxyInstance()方法来创建我们的代理对象,我们来看看其三个参数
        * 第一个参数:类加载器,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
        * 第二个参数:对象要实现哪些接口:我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
        * 第三个参数:它是一个接口!它的名字叫调用处理器,使用新建的proxy对象调用方法的时候,都会调用到该接口中的invoke方法。 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
        */
		ICar car = (ICar) Proxy.newProxyInstance(handler.getClass().getClassLoader(), customer.getClass().getInterfaces(), handler);

        //System.out.println("handler.getClass().getClassLoader()---->"+handler.getClass().getClassLoader());//获取到JVM创建的相当于是4S店的代理类

	    //System.out.println("cu.getClass().getInterfaces()---->"+cu.getClass().getInterfaces()[0]);//获取到Icar接口
	    //System.out.println("handler---->"+handler);//通过句柄可以间接的获取到被代理类,即客户类

		car.buyCar();
						
			
	}

}
小结:缺点:可以看出静态代理和JDK代理有一个共同的缺点,就是目标对象必须实现一个或多个接口,假如没有,则
可以使用Cglib代理

3.动态代理-cglib

代理类实现MethodInterceptor接口
定义:
  cglib代理:cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理

简而言之:对指定的目标类生成一个子类,并覆盖其中方法实现增强,进行代理的
备注:要想使用cglib代理,则必须额外的引入新的jar包:asm和cglib

PS:sping-core.jar包含了以上二者
代码解释

package com.rj.bd.poxystyle.CglibDynamicProxy;

/**
 * @desc 客户类:目标对象,没有实现任何接口
 */
public class Customer  {

	public int cash;
	public String name;

	public int getCash() {
		return cash;
	}

	public void setCash(int cash) {
		this.cash = cash;
	}

	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	
	public void buyCar() {
		System.out.println(this.name+"买车花了:" + this.cash + "元");
	}

}





package com.rj.bd.poxystyle.CglibDynamicProxy;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

/**
 * @desc   代理类
 */
public class CglibProxy  implements  MethodInterceptor {

public Object  target;//目标对象	**(客户)**
	
//给目标对象创建一个代理对象
public Object getProxyInstance(Object target){
	this.target = target;
    //1.工具类
    Enhancer en = new Enhancer();
    //2.设置父类
    en.setSuperclass(target.getClass());
    //3.设置回调函数
    en.setCallback(this);
    //4.创建子类(代理对象)
    return en.create();
}

	@Override//给目标对象创建一个代理对象之前实现方法增强
public Object intercept(Object arg0, Method method, Object[] arg2,
			MethodProxy arg3) throws Throwable 
{
		    System.out.println("向观众问好");
	        //执行目标对象的方法
	        Object returnValue = method.invoke(target, arg2);
	        System.out.println("谢谢大家");
	        return returnValue;
}

}



package com.rj.bd.poxystyle.CglibDynamicProxy;
/**
 * @desc   测试类:测试Cglib代理
 * @author wyh
 * @time   2018-11-6 下午01:56:06  
 * 
 */
public class Test {
public static void main(String[] args) {
	
	  //1.初始化客户对象,并且给对象中的变量赋值 
	  Customer  target=new Customer();
	  target.setName("王百万");
	  target.setCash(200000);
	  //2.初始化代理类:目的是将初始化获取得到的客户对象传递给cglib代理类
	  CglibProxy  cglib=new CglibProxy();
	  Customer bookCglib=(Customer)cglib.getProxyInstance(target);
	  bookCglib.buyCar();
	
}
}

四.单例模式

单例模式确保某个类只有一个实例,而且自动实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler(打印区),以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头

1.单例模式有一下特点:

1、单例类只能有一个实例。
2、单例类必须自创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例

2.单例模式的优缺点

优点:该类只存在一个实例,节省系统资源;对于需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
缺点:不能外部实例化(new),调用人员不清楚调用哪个方法获取实例时会感到迷惑,尤其当看不到源代码时

3.使用场景:

1)框架中:例如Spring框架
2)例如产生:商品订单号,保险单号

1.懒汉式单例模式

懒汉式:第一次调用的时候才初始化 小结:如果要用懒汉式实现单例的话 最好是结果双重判断的方式(双重加锁机制)实现
饿汉式:类加载的时候就初始化实例

package com.rj.bd.pattern.singles.lazyman;
/**
 * @desc   用户订单类(目的是为了模拟出多线程环境)
 */
public class OrderThread implements Runnable {

	@Override
	public void run() {

    GoodsNum onlyOneGoodsNum = GoodsNum.getInstance();
	System.out.println(onlyOneGoodsNum.hashCode());	
		
	}

}

package com.rj.bd.pattern.singles.lazyman;
/**
 * @desc   懒汉式单例应用之订单类
 */
public class GoodsNum {

private static GoodsNum goodsNum;
public static int i =1;//设定一个全局变量用来记录同步锁的判断次数
public GoodsNum() {
}

/**
 * @desc  1.初始化
 * @return
 */
public static GoodsNum getInstance()
{
	
   if (goodsNum == null) 
   {
	synchronized (GoodsNum.class) 
	{
		if (goodsNum == null) 
		{
			goodsNum = new GoodsNum();
		}
		i++;
	} 
}
System.out.println("同步代码块执行的次数:"+i);
return goodsNum;
}
}

package com.rj.bd.pattern.singles.lazyman;
/**
 * @desc   测试类:测试在多线程情况下懒汉式单例有问题
 */
public class Test {

	public static void main(String[] args) {

		OrderThread orderThread=new OrderThread();
		/**
		Thread thread01=new Thread(orderThread);
		Thread thread02=new Thread(orderThread);

		thread01.start();
		thread02.start();
		**/
		
		Thread []threads=new Thread[10000];
		for(int i =0;i<threads.length;i++)
		{
			threads[i]=new Thread(orderThread);
			threads[i].start();
		}
		
		
	}

}

二、饿汉式单例模式

饿汉式:类加载的时候就初始化实例
示例代码:
package com.rj.bd.poxystyle.Singletons.hungrystyle;
/**
 * @desc   饿汉式
 */
public class GoodsNum {
	 private static GoodsNum goodsNum = new GoodsNum();
	 
	  private GoodsNum()
	    {
	    }

/**
 * @desc  初始化类对象
 * @return
 */
public static GoodsNum getInstance() {
	        return goodsNum;
	    }
	 
	 
}



package com.rj.bd.poxystyle.Singletons.hungrystyle;
/**
 * @desc   用户的订单模块
 */
public class OrderThread implements Runnable{

	@Override
	public void run() 
	{
			    GoodsNum onlyOnGoodsNum = GoodsNum.getInstance();
				System.out.println(onlyOnGoodsNum.hashCode());
	}

}




package com.rj.bd.poxystyle.Singletons.hungrystyle;
/**
 * @desc   测试:测试懒汉式
 */
public class Test {
	public static void main(String[] args) {
		   OrderThread  orderThread=new OrderThread();
		  /* Thread  thread1=new Thread(orderThread);
		   Thread  thread2=new Thread(orderThread);
           thread1.start();
           thread2.start();*/
           
           Thread []threads=new Thread[10000];
           for (int i = 0; i <threads.length; i++)
           {
        	   threads[i]=new Thread(orderThread);
        	   threads[i].start();
		   }
		    
  
	}

}

  • 48
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 76
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值