常用的几种设计模式

常用的几种设计模式

在平时的工作中多多少少都会用到点设计模式。很多书籍中都阐述了设计模式中的几大原则:开闭、李氏代换、依赖、接口隔离、复用、迪米特等,很多网上博客中都列举了23中设计模式,设计模式同时分为几大类:创建型、结构型和行为型,但本文不一一列举,本篇只介绍平时我用到比较多的几种模式,以提高系统性能为目的,如单例、装饰、代理、享元等模式。

单例

单例平时应该用的比较多,一般确保系统中该类只产生一个实例,带来的好处也是很明显的。

  1. 频繁创建的对象,尤其是重量级对象,节省创建对象所花费的时间
  2. 另外一点就是减少gc,因为创建的对象少了,gc当然会相应的减少

在网上单例有很多种实现方式,一般就是饿汉、懒汉、内部类(推荐最优雅)以及双重检查(不推荐,而且网上有的demo都有存在错误的写法)。

public class Singleton{
	private Singleton(){//确保不会在系统中其它地方被实例化
		System.out.println("Create a Singleton");
	}
	private static Singleton instance = new Singleton();
	public static Singleton getInstance(){
		return instance;
	}
}

上面这种饿汉式的创建单例实现简单,有不好的地方,没有多instance做延迟加载,如果这个类初始化比较慢,那么在jvm在加载该类时,单例就会被创建,因为它是static。但是有时候我们可能不会用到该单例,但是它还是被初始化了,所以希望在我们调用的时候才去初始化它,延迟加载懒汉。

public class SingletonLazy{
	private SingletonLazy(){//确保不会在系统中其它地方被实例化
		System.out.println("Create a Singleton");
	}
	private static SingletonLazy instance = null;
	public static synchronized SingletonLazy getInstance(){
		if(instance == null){
			instance = new SingletonLazy();
		}
		return instance;
	}
}

相比饥饿的单例,在调用获取单例的时候加了同步,因为在多线程的情况下,如果不加同步,会导致单例被初始化多个或者单例对象初始化不完全。懒加载再多线程的情况下,获取单例比饥饿下获取单例所消耗的时间更长,大家可以简单测试下,主要是因为同步造成的。 相比前两种,饥饿模式不能延迟加载,懒汉模式引入了同步,有没有一种折中的方式,内部类就可以满足这种。(文字巴拉一大堆,还是看code吧)

public class SingletonInner{
	private SingletonInner(){//确保不会在系统中其它地方被实例化
		System.out.println("Create a SingletonInner");
	}
	private static class SingletonHolder{
		private static SingletonInner instance = new SingletonInner();
	}
	public static SingletonInner getInstance(){
		return SingletonHolder.instance;
	}
}

内部类的的实现最优雅,首先该类在jvm加载的时候不会被实例化,只有在调用的时候,jvm才会去加载该内部类SingletonHolder进而再去初始化实例instance,实例的创建发生在类加载时完成。推荐这种方式实现。

注意:极端情况,如果通过反射机制,强行调用私用构造函数,会产生多个实例。另外序列化和反序列化也会破坏单例。如果存在这些情况,那得注意。

代理

代理字面意思就是代替的意思,比如我们生活中的律师,他代理当事人授权处理问题,但是核心问题还得请示当事人。 代理我们一般分为静态代理和动态代理,代理的角色一般有:接口,接口实现类或者叫真实主题,代理类。 静态代理缺点很明显,如果需要代理的对象特别多,那代码也要写一大堆,而且如果接口变化了,需要被代理的类和代理类都需要一一修改,实在是噩梦。 动态代理却不一一样,动态代理是在运行时(字节码动态加载技术),代理类在运行时生成且加载类。与静态代理相比可以有效的减少代码行数,提升系统灵活性质。

  • 静态代理 先来个简单的例子,我们日常生活中吃饭的方式,比如用手或者筷子
public interface Eat{
	void tool();
}

真实的类

public class EatWithHands implements Eat{
	@override
	public void tool(){
		System.out.println(" eat food with hands..");
	}
}

代理实现类

public class EatProxy implements Eat{
	private Eat eat;//代理对象
	public EatProxy (){
		this.eat = new EatWithHands();
	}
	@override
	public void tool(){
		start();
		eat.tool();
		end();
	}
	private void startEat(){
		System.out.println("cook ..");
	}
	
	private void endQuery(){
		System.out.println("sweep ..");
	}
}

测试

//test
public static void main(String [] args){
	Eat eat = new EatProxy();
	eat.tool();
}

现在如果我想吃饭的时候使用筷子,但我又想复用吃饭前烹饪食物和收拾逻辑不变,那我该如何处理,那我只能在建一个使用筷子的类,然后再写个使用筷子代理类来实现该逻辑。是的,静态代理这么弄,如果我再想加个用西式的刀叉来吃饭,那还得跟使用筷子的代码类似,代码重复读较高。

  • jdk动态代理
public class EatJdkProxy<T> implements InvocationHandler{
	private T target;//被代理的对象
	EatJdkProxy(T t){
		this.target = t;
	}
	@override
	public object invoke(Object proxy, Method method, Object[] args) throws Throwable{
		start();
		method.invoke(target,args);
		end();
	}
	
	public <T> T getProxy(){
		return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
				target.getClass().getInterfaces(),
				this);
	}
	
	private void start(){
		System.out.println("cook ..");
	}

	private void end(){
		System.out.println("sweep ..");
	}
}

测试

public static void main(String [] args){
	//用手抓
	Eat e = new EatJdkProxy<Eat>(new EatWithHands()).getProxy();
	e.tool();
	//用筷子
	Eat e = new EatJdkProxy<Eat>(new EatWithChops()).getProxy();
	e.tool();
}


  • cglib代理 jdk自带的生成代理类固然好,但是缺点也明显,只能针对接口生成代理类,如果EatWithHands没有实现Eat接口那么不能生成EatWithHands的代理类。
public class EatCglibProxy<T> implements MethodInterceptor{
	private T target;//被代理的对象
	EatCglibProxy(T t){
		this.target = t;
	}
	@override
	public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable{
		start();
		method.invoke(target,args);
		end();
	}
	
	public <T> T getProxy(){
		Enhancer enhancer = new Enhancer();
		enhancer.setCallback(this);
		enhancer.setInterfaces(t.getClass().getInterfaces());
		return (T) enhancer.create();
	}
	
	private void start(){
		System.out.println("cook ..");
	}

	private void end(){
		System.out.println("sweep ..");
	}
}

测试

public static void main(String [] args){
	Eat e = new EatCglibProxy<Eat>(new EatWithChops()).getProxy();
	e.tool();
}


装饰

观察者

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值