目录
双重校验锁的单例模式(饿汉式):
class Singleton{
private volatile static Singleton s;
private Singleton(){
}
public static Singleton getS(){
if (s == null){
synchronized (Singleton.class){
if (s == null){
s = new Singleton();
}
}
}
return s;
}
}
装饰模式:
侧重给一个实现类动态添加功能,不会对实现类的方法进行过滤拦截:
//定义一个接口Human,代码如下:
public interface Human {
public void run();
}
//定义一个被装饰的类Man,代码如下:
public class Man implements Human {
@Override
public void run() {
System.out.println("人会跑步");
}
}
//定义一个装饰的抽象类,代码如下:
public abstract class AbstractDecorator implements Human{
//持有被装饰类的引用
private Human human;
//构造函数注入被装饰者
public AbstractDecorator(Human human) {
this.human = human;
}
//调用被装饰类的方法
@Override
public void run() {
human.run();
}
}
//定义一个装饰的实现类,代码如下:
public class ManDecorator extends AbstractDecorator {
public ManDecorator(Human human) {
//调用父类的构造方法
super(human);
}
//装饰类增加的功能
private void fly() {
System.out.println("人可以飞");
}
//增强了功能的run方法
@Override
public void run() {
super.run();
fly();
}
}
客户端代码如下:
public class Client {
public static void main(String[] args) {
//创建被装饰的类
Human human = new Man();
//创建装饰的类,并添加被装饰类的引用
Human superMan = new ManDecorator(human);
//执行增强后的run方法
superMan.run();
}
}
代理模式:
侧重将一个实现类的功能,委托给代理类来处理,可以对实现类的方法进行过滤拦截(某种情况下,可能不执行实现类的方法)
普通代理:
//普通代理,结构类似于装饰模式
//接口IGamePlayer
public interface IGamePlayer {
public void beginGame();
}
//被代理类GamePlayer
public class GamePlayer implements IGamePlayer {
@Override
public void beginGame() {
System.out.println("GamePlayer beginGame");
}
}
//代理类ProxyGamePlayer
//代理类和真实类实现同一个接口
public class ProxyGamePlayer implements IGamePlayer {
//代理类持有真实类的引用
private IGamePlayer gamePlayer;
public ProxyGamePlayer(IGamePlayer gamePlayer) {
super();
this.gamePlayer = gamePlayer;
}
private void doSomething() {
System.out.println("ProxyGamePlayer doSomething");
}
@Override
public void beginGame() {
gamePlayer.beginGame();
doSomething();
}
}
//客户端代码调用,代码如下:
public class Client {
public static void main(String[] args) {
//创建真实类的实例
IGamePlayer gamePlayer = new GamePlayer();
//创建代理类,并通过构造函数产生依赖
ProxyGamePlayer proxy = new ProxyGamePlayer(gamePlayer);
//调用代理类的方法
proxy.beginGame();
}
}
*/
结果:
GamePlayer beginGame
ProxyGamePlayer doSomething
/*
普通代理模式特点:可以通过访问代理类,间接的访问真实的角色(被代理类)。
高扩展性:只要代理类和真实类实现同一个接口,不管真实类的逻辑如何变化,代理类都不需要做出修改,提高了程序的扩展性。
职责清晰:一件事物由代理类完成,代理可以对逻辑进行扩展,而真实类结构不会受其影响,只需关注自己的业务逻辑(例如上面的doSomething方法)。
动态代理:
动态代理:不需要提供代理类ProxyGamePlayer,却能通过Java提供的相关的API产生代理对象,并对真实的角色进行访问。
基本步骤:
定义一个接口IGamePlayer
定义一个被代理类,实现接口IGamePlayer
使用Proxy类生成一个代理对象
步骤1,2需要提供一个接口,一个实现该接口的真实角色。直接借用上面普通代理的代码,这里不再重复展示代码。
//在客户端中生成一个代理对象,代码如下:
public class Client {
public static void main(String[] args) {
//创建被代理对象
final IGamePlayer player = new GamePlayer();
//创建InvocationHandler的实例
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result = method.invoke(player, args);
return result;
}
};
//动态获取代理对象
IGamePlayer proxyIntance = (IGamePlayer) Proxy.newProxyInstance(
player.getClass().getClassLoader(),//被代理类的类加载器
new Class[]{IGamePlayer.class}, //接口的Class实例
h); //InvocationHandler实例
//调用代理对象的方法
proxyIntance.gameOver();
}
}
*/
实现动态代理的步骤:
1.实现InvocationHandler接口创建自己的调用处理器
2.给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类
3.以调用处理器类型为参数,利用反射机制得到动态代理类的构造函数
4.以调用处理器对象为参数,利用动态代理类的构造函数创建动态代理类对象
第12行,通过反射完成被代理类里方法的调用。不管是普通代理还是动态代理,都是调用真实角色的方法。
(第一个参数:传入被代理类的实例player;第二个参数:传入被代理类方法的输入参数的类型;)
第18行,动态生成一个代理对象proxyIntance。newProxyInstance第三个参数h,是一个InvocationHandler的子类实例。InvocationHandler是Java提供的一个反射接口,只有一个invoke方法。
/*
动态代理的使用场景
试想一个这样的场景:代练每完成一把游戏,就需要将游戏结果发送给客户。
解决方案一:在被代理类的gameOver方法中添加逻辑,但修改原有的代码结构并不好。
解决方案二:在InvocationHandler的invoke方法中添加逻辑。
对于方案二,注意到invoke方法的第二个参数method,通过这个反射类可以操作被代理类的所有方法。
//修改后代码如下:
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result = method.invoke(player, args);
if ("gameOver".equals(method.getName())) {
//如果是调用gameOver方法,则通知客户游戏结果
System.out.println("这把游戏失败啦~嘿嘿");
}
return result;
}
};
区别:
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理。
静态代理的代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。