设计模式之常用的

1.单例设计模式:

/**
 *   单例设计模式例子
 * @author wx 2019-02-13
 */
public class SingleModel { //类一加载 就对象就已经存在
	//饿汉式单例:弊端:类一加载 s 对象就一直占着内存,直到类被卸载时,静态变量才会被摧毁,释放内存。特定情况下会消耗内存
	//三步 
	private static SingleModel s = new SingleModel();
	private SingleModel() {	}  //私有构造
	public static SingleModel getInstance() { //静态工厂方法
		return s;
	}
}
package designModelTest;

/**
 * 懒汉式单例模式 (延迟加载方式)
 * @author wx 2019-02-13
 *弊端:多线程环境下会产生多个对象,使用同步锁可以避免
 */
/*public class SingleModel2 {

	private static SingleModel2 s = null;
	private SingleModel2(){}
	public static SingleModel2 getInstance() {
		if(s == null) {
			s=new SingleModel2();
		}
		return s;
	}
}*/

/**
 * 使用同步代码块或对类使用同步锁
 * 虽然解决了多个实例对象,但是效率低下,下一个对象想要使用对象,就必须等到上一个线程释放锁,才可以继续运行
 * @author wx
 *
 */
/*public class SingleModel2 {

	private static SingleModel2 s = null;
	private SingleModel2(){}
	public static SingleModel2 getInstance() {
		
		//等同于 synchronized public SingleModel2 getInstance() {}
		synchronized(SingleModel2.class) {//同步代码块
			if(s == null) {//这个判断一定要加 否则出现线程安全问题
				s=new SingleModel2();
			}
			return s;
		}
	}
}*/

/**
 * 使用DCL双检查锁机制,对上面代码进行优化
 * 可以避免整个方法被锁,只需对锁的代码部分加锁,能提高执行效率
 */
public class SingleModel2 {

	private static SingleModel2 s = null;
	private SingleModel2(){}
	public static SingleModel2 getInstance() {//只有调用此方法时,才会实例化对象
		
		if(s == null) {
			synchronized(SingleModel2.class) {//同步代码块
				if(s == null) {//这个判断一定要加 否则出现线程安全问题
					s=new SingleModel2();
				}
			}
		}
		return s;
	}
}

2.模板方法模式:
实现方式:
a) 父类模板类(规定要执行的方法和顺序,只关心方法的定义及顺序,不关心方法实现)
b) 子类实现类(实现a规定要执行的方法,只关心方法实现,不关心调用顺序)

/**
 * 模板设计模式  模式--
 * 解决问题;当功能内部一部分实现是确定,一部分实现是不确定的,这时可以把不确定的吧部分暴露出去,让子类去实现。
 * @author wx 2019-02-20
 */
public abstract class TemplateModel {
	public final void getTime() {//此功能不需要复写  ,可以加final 限定
		long start =System.currentTimeMillis();
		code();//不确定的部分 ,提取出来 ,通过抽象方法实现
		long end=System.currentTimeMillis();
		System.out.println("毫秒是:"+ (end-start));
	}
	public abstract void code();
}
public class TemplateModelImpl extends TemplateModel{
	@Override
	public void code() {
		for (int i=0;i<100;i++) {//子类复写功能方法
			System.out.println(i);
		}
	}
	public void getAuthor() {
		System.out.println("吴霞");
	}
	
public class TemplateModelTest {
	public static void main(String[] args) {
		TemplateModel t = new TemplateModelImpl();
		t.getTime();
		//子类可以直接使用父类非抽象的方法
		TemplateModelImpl t1 = new TemplateModelImpl();
		t1.getTime();
	}
}
}

3.代理模式:
3.1.代理模式的定义:给某一个对象提供一个代理对象,并有代理对象控制原对象的引用。

代理模式类图代理模式类图
在这里插入图片描述
3.2.为什么要有代理模式:

a.中介隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。

b.开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。

3.3.代理模式的类型:
a.静态代理:

i.首先业务类接口,代理类,业务类,客户类

/**
 * @author WX
 * 定义一个账户接口
 */
public interface Account {
	public List queryAccount(Long id);
	public void updateAccount(Long id);
}
/**
 * @author WX
 *  业务实现类 (委托类),包含业务逻辑
 */
public class AcountImpl implements Account{
	public List queryAccount(Long id) {
		System.out.println("查看用户.....");
		return null;
	}
	public void updateAccount(Long id) {
		System.out.println("修改账户...");  
	}
}

ii.代理类和业务类(委托类)实现同一个接口,代理类中要组合业务类对象(以参数的形式加入到代理类的构造方法中)。代理类中和业务类同名的方法中,在调用真正业务方法之前,可以加入预处理操作,在调用真正业务方法之后,可以加入调用后的操作。

/**
 * 账户代理类 :实现  委托类实现的接口,注入委托类对象,通过委托类对象去调用真正的业务逻辑方法。
 * @author WX
 */
public class AccountProxy implements Account {
	private AcountImpl accountImpl;//组合一个业务实现类对象进行真正的业务方法的调用
	public AccountProxy(AcountImpl acountImpl){
		this.accountImpl=acountImpl;
	}
	public List queryAccount(Long id) {
		System.out.println("查询账户得预处理——————————");
		//调用真正的查询账户得方法
		accountImpl.queryAccount(id);
		System.out.println("查询账户之后——————————");
		return null;
	}
	public void updateAccount(Long id) {
		System.out.println("更新账户得预处理——————————");
		//调用真正的更新账户得方法
		accountImpl.updateAccount(id);
		System.out.println("更新账户之后——————————");
	}
}

iii.客户类:创建业务对象,代理对象。通过代理对象调用和业务类同名的方法,实现代理模式。

/**
 * 模拟  account 类的调用方
 * 静态代理的测试
 * @author WX
 */
public class AccountClient {
	public static void main(String[] args) {
		//创建业务实现类对象,即委托类
		AcountImpl accountImpl =new AcountImpl();
		//创建代理类对象,通过构建参数的方式,将委托类对象传给代理类对象
		AccountProxy accProxy=new AccountProxy(accountImpl);
		accProxy.queryAccount(1L);
		accProxy.updateAccount(1L);
	}
}

静态代理总结:
优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
缺点:我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。 如果业务类不一样,但实现的功能相同,就会有很多重复代码出现。

扫描二维码关注公众号,回复: 6167179 查看本文章

b.JDK动态代理:

在JDK动态代理中我们不再需要再手动的创建代理类,只需要编写一个动态处理器就可以了。真正的代理对象由JDK再运行时为我们动态的来创建。

i.创建业务接口类,业务类,动态处理器[是一个java类],客户类

/**
 * 业务接口
 * @author WX
 */
public interface BookFacade {
	public void addBook();
	public List queryBook(String name);
	//public List queryBookByid();
}
//实现类
public class BookFacadeImpl implements BookFacade {
	BookDaoImpl bookDaoImpl=new BookDaoImpl();
	public void addBook() {		
		System.out.println("增加一本java图书方法.....");
	}
	public List queryBook(String name){
		List list=bookDaoImpl.queryBooks("java");
		System.out.println("增加一本java图书方法开始.....");
		if(list.size()>0){
			  // Iterator遍历
			  for (Iterator it = list.iterator(); it.hasNext();)
			  {
				  Book book=(Book) it.next();System.out.println("======="+book.getId()+","+book.getBookName()+","+book.getAuthor()+"=======");
			  }
		}
		System.out.println("增加一本java图书方法结束.....");
		return null;
	}
	public List queryBookByid(){
		List list=bookDaoImpl.queryBooksByid(201801L);
		System.out.println("查询一本js图书方法开始.....");
		if(list.size()>0){
			  // Iterator遍历
			  for (Iterator it = list.iterator(); it.hasNext();)
			  {
				  Book book=(Book) it.next();System.out.println("======="+book.getId()+","+book.getBookName()+","+book.getAuthor()+"=======");
			  }
		}
		System.out.println("查询一本js图书方法结束.....");
		return list;
	}
}

ii.动态处理器[是一个java类]实现InvocationHandler接口,重写invoke()方法,再写一个.bind方法,用来创建代理类并返回代理对象

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import interfaceImpl.BookFacadeImpl;
import interfaces.BookFacade;
/**
 * JDK动态代理 :实现调用管理接口InvocationHandler  创建动态代理类
 * @author WX
 * 写死实现委托类实现的接口,就是静态代理
 *  public class BookFacadeProxy implements BookFacade {
 */
public class BookFacadeProxy implements InvocationHandler {
	/*静态代理模式的处理方法
	 * private BookFacadeImpl bookFacadeImpl;
	public BookFacadeProxy(BookFacadeImpl bookImpl){//构造方法
		this.bookFacadeImpl=bookImpl;
	}*/
	private Object target;//具体的业务类对象,调用实际的业务逻辑方法
	/**
	 * 绑定业务对象,并返回一个代理类
	 * @param target
	 * @return
	 */
	public Object bind(Object target){
		this.target=target;
		//通过反射机制,创建一个代理类对象实例,并返回。用户进行方法调用时使用
		//创建代理对象时,需要传递该业务类的类加载器(用来获取业务实现类的元数据,在包装方法时调用真正的业务方法),接口,handler实现类
		return Proxy.newProxyInstance(target.getClass().getClassLoader(),
					target.getClass().getInterfaces(), this);
	}
	/**
	 * 包装调用方法:进行预处理,方法调用后处理
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// TODO Auto-generated method stub
		Object result=null;
		System.out.println("增加图书预处理操作——————————1");
		//调用真正的业务方法
		result=method.invoke(target, args);
		System.out.println("增加图书调用后处理——————————4");
		return result;
	}
}

iii.在客户类中,创建业务接口对象,动态处理器对象。通过向上转型得到业务接口对象(a),然后通过这个对象(a),调用接口中的方法。

/**
 * JDK动态代理测试类:
 * 			1.创建业务实现类(委托类)对象,代理类对象
 * 			2.定义接口引用(向上转型),并用代理对象.bind(业务实现对象)的返回值进行赋值
 * 			3.通过接口引用调用业务方法(接口引用真正指向的是一个绑定了业务类的代理类对象,所以通过接口方法名调用的是被代理的方法们)
 * @author WX
 */
public class BookFacadeClient {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		BookFacadeImpl bookFacadeImpl=new BookFacadeImpl();
		BookFacadeProxy proxy=new BookFacadeProxy();
		BookFacade bookFacade=(BookFacade)proxy.bind(bookFacadeImpl);
		//bookFacade.addBook();
		bookFacade.queryBook("js-0");
	}
}

总结:
优点:相对于静态代理,动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度;
缺点:始终无法摆脱仅支持interface代理的桎梏,如果业务实现类新增了业务接口中没有的方法,这个方法,无法被JDK动态代理。

底层原理:利用了反射原理

c.cglib动态代理:
JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

i.创建cglib代理类,客户类,导入cglib.xx.jar包,asm.xxx.jar包

/**
 * CGLIB动态代理:
 * 1.定义一个业务类,无需实现接口,实现也不影响
 * @author WX
 */
public class UserImpl{
	public void addUser() {
		System.out.println("增加一个用户.....");
	}
}

ii.cglib代理类实现MethodInterceptor接口,并在代理中,写genInstance方法(同.bind方法一样)

/**
 * 2.实现MethodInterceptor方法代理接口(需要导入cglib jar包), 创建代理类
 * @author WX
 */
public class UserProxyCglib implements MethodInterceptor{
	private Object target;//业务对象,供代理方法中进行真正的业务方法调用
	/**
	 * 相当于JDK动态代理中的绑定
	 * @param object
	 * @return
	 */
	public Object getInstance(Object target){
		this.target=target;//给业务对象赋值
		//创建加强器,用来创建动态代理类
		Enhancer enhancer=new Enhancer();
		//为加强器指定要代理的业务类(即为下面生成的代理类指定父类)
		enhancer.setSuperclass(this.target.getClass());;
		//设置回调:对于代理类上所有方法的调用,都会调用callback,而callback需要实现intercepter方法进行拦截
		enhancer.setCallback(this);
		//创建动态代理类对象并返回
		return enhancer.create();
	}	
	//实现回调方法
	@Override
	public Object intercept(Object obj, Method method, Object[] arg2, MethodProxy proxy) throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("增加用户预处理--------------");
		//调用业务类(父类)方法
		proxy.invokeSuper(obj, arg2);
		System.out.println("增加用户调用后--------------");
		return null;
	}
}

iii.在客户类中,创建业务类和代理类对象

/**
 * cglib 动态代理的测试:
 * cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,
 * 并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。 
 * @author WX
 */
public class UserCglibClient {
	public static void main(String[] args) {
		/**
		 * 1.创建业务类和代理类对象,
		 * 2.然后通过  代理类对象.getInstance(业务类对象) 返回一个动态代理类对象(它是业务类的子类,可以用业务类引用指向它)。
		 * 3.最后通过动态代理类对象进行方法调用。
		 */
		//创建业务类对象
		UserImpl userImpl=new UserImpl();
		//创建代理类对象
		UserProxyCglib proxyCglib=new UserProxyCglib();
		UserImpl userCglib= (UserImpl) proxyCglib.getInstance(userImpl);
		userCglib.addUser();
	}
}

CGLIB代理总结:
优点:CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。

缺点:由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。对于final修饰的类,也不能进行代理,因为被final修饰的类,不能被继承。

底层原理:底层将方法全部存入一个数组中,通过数组索引直接进行方法调用​​​​

4.观察者模式:
观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
实现方式:
a) 角色抽象类(提供对观察者的添加,删除和通知功能)。
b) 角色具体类,实现a,维护一个c的集合(对角色抽象类的实现)。
c) 观察者抽象类(被角色通知后实现的方法)。
d) 观察者实现类,实现c(多个)。
注:JDK提供了对观察者模式的支持,使用Observable类和Observer接口

5.策略模式:
实现方式:
a) 提供公共接口或抽象类,定义需要使用的策略方法。(策略抽象类)
b) 多个实现的策略抽象类的实现类。(策略实现类)
c) 环境类,对多个实现类的封装,提供接口类型的成员量,可以在客户端中切换。
d) 客户端 调用环境类 进行不同策略的切换。
注:Jdk中的TreeSet和 TreeMap的排序功能就是使用了策略模式。

6.简单工厂模式:
就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。
实现方式:
a) 抽象产品类(也可以是接口)
b) 多个具体的产品类
c) 工厂类(包括创建a的实例的方法)

7.适配器模式:
适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
7.1. 类适配器(子类继承方式)
实现方式:
a) 目标抽象角色(定义客户要用的接口)
b) 适配器(实现a继承c,作为一个转换器被客户调用)
c) 待适配器(真正需要被调用的)
d) 客户端(借用a的实例调用c的方法)
7.2. 对象适配器(对象的组合方式)
实现方式:
a) 目标抽象角色(定义客户要用的接口)
b) 适配器(实现a,维护一个c的引用,作为一个转换器被d调用)
c) 待适配器(真正需要被调用的)
d) 客户端(此类,借用a类的实例调用c类的方法,类似静态代理,但是解决的问题不同)
7.3. 缺省的方式
实现方式:
a) 抽象接口
b) 实现a的适配器类(空实现)
c) 客户端,继承b,调用b中的方法,不必直接实现a(直接实现a需要实现a中的所有的方法)

8.装饰模式:
实现方式:
a) 抽象的被装饰角色 (所有的角色都要直接或间接的实现本角色)
b) 具体的被装饰角色,实现或继承a (被功能扩展的角色)
c) 装饰角色,实现或继承a (本类有对a的引用,所有的具体装饰角色都需要继承这个角色)
d) 多个具体修饰角色 ,继承c(对被装饰角色的功能扩展,可以任意搭配使用)

猜你喜欢

转载自blog.csdn.net/baidu_35160588/article/details/88956988