《设计模式之禅》学习及源码示例---装饰模式\适配器模式\代理模
一、装饰模式
定义:
动态的给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更加灵活
组成角色:
Component:抽象被装饰者
一个接口或者是抽象类,通常是我们需要修饰的类的接口或者是继承的抽象类
ConcreteComponent:具体被装饰者
我们需要装饰的对象就是这个类,这个类一般会实现或者继承Component
Decorator:装饰抽象角色
这个接口是一个抽象类实现或继承Component,作为具体装饰角色和被装饰角色的中转对象,非常重要
ConcreteDecorator:具体的装饰角色
ConcreteDecoratorA\ConcreteDecoratorB\ConcreteDecoratorC.....作为具体的装饰实现,继承Decorator现象具体对ConcreteComponent的装饰
需求:现在我们要给一个毛坯房装修
代码演示:
public interface Component {
void operate();
}
public class ConcreteComponent implements Component {
@Override
public void operate() {
System.out.println("这是一间毛坯房");
}
}
public abstract class Decorator implements Component{
private Component component=null;
//用于传递被装饰者
public Decorator(Component component) {
this.component = component;
}
//给具体装饰者执行,并在其之上进行添加前后置功能
@Override
public void operate() {
this.component.operate();
}
}
public class ConcreteDecoratorDoor extends Decorator {
public ConcreteDecoratorDoor(Component component) {
super(component);
}
private void method1(){
System.out.println("装个门");
}
private void method2(){
System.out.println("门上装个眼睛");
}
@Override
public void operate() {
super.operate();
method1();
method2();
}
}
public class ConcreteDecoratorWindow extends Decorator {
public ConcreteDecoratorWindow(Component component) {
super(component);
}
private void method1(){
System.out.println("装个窗户");
}
private void method2(){
System.out.println("窗户换上防弹玻璃");
}
@Override
public void operate() {
super.operate();
method1();
method2();
}
}
测试
public class Test {
public static void main(String[] args) {
Component mopifang=new ConcreteComponent();
mopifang=new ConcreteDecoratorDoor(mopifang);
mopifang=new ConcreteDecoratorWindow(mopifang);
mopifang.operate();
}
}
源码使用示例:JDK的IO使用了装饰模式
InputStream作为Component,FilterInputStream是Decorator,BufferedInputStream通过继承FilterInputStream,给FileInputStream装饰拓展了功能
public class FilterInputStream extends InputStream{
protected InputStream in;
protected FilterInputStream(InputStream in){
this.in = in;
}
...
}
public class BufferedInputStream extends FilterInputStream {
..
}
通过传入参数实现相关的装饰
public class Test {
public static void main(String[] args) {
try {
InputStream in = new FileInputStream(new File("c:\\xxx\\xxxx"));
BufferedInputStream in1 = new BufferedInputStream(in);
LineNumberInputStream in2 = new LineNumberInputStream(in1);
....
} catch (Exception e) {
e.printStackTrace();
}
}
二、适配器模式
定义:
将一个类的接口变成客户端所期待的另一种接口,从而使原本因为接口不匹配而无法在一起工作的两个类能够在一起工作
组成角色:
Target目标接口
该接口就是我们的期待接口,其他接口向这个接口转换
Adaptee源角色
希望被转换的接口
Adapter适配器角色
适配器角色职责是把源角色转为目标接口
需求:现在我们需要将220V的电压适配为5V
代码演示:
public interface Target {
Integer output5v();
}
public class Adaptee {
private final static Integer output220V=220;
public Integer output220V(){
System.out.println("这是"+output220V+"V的电压");
return output220V;
}
}
适配后
public class Adapter extends Adaptee implements Target {
@Override
public Integer output5v() {
Integer output220V = super.output220V()/44;
System.out.println("转化后变成"+output220V+"V");
return output220V;
}
}
public class Test {
public static void main(String[] args) {
Target target=new Adapter();
target.output5v();
}
}
源码使用示例:JDK的IO使用了装饰模式
InputStreamReader和OutputStreamWriter分别继承了Read和Writer接口,创建这两个对象时要传入InputStream和OnputStream类型的实例对象,将字节流转换为字符流。InputStreamReader和OutputStreamWriter就充当了Adapter适配器角色,InputStream和OnputStream类型的实例对象就是Adaptee源角色,而Read和Writer接口就是Target目标接口
public OutputStreamWriter(OutputStream out) {
super(out);
try {
//适配过程是将byte转码为char的编码
se = StreamEncoder.forOutputStreamWriter(out, this, (String)null);
} catch (UnsupportedEncodingException e) {
throw new Error(e);
}
}
//将字节流转化为字符流
public static void main(String[] args) throws FileNotFoundException {
OutputStream outputStream=new FileOutputStream("c:\\xx\\xxx");
OutputStreamWriter outputStreamWriter=new OutputStreamWriter(outputStream);
}
image.png
三、代理模式
为其他对象提供一个代理以控制对某个对象的访问。代理类主要负责为被代理类预处理消息、过滤消息、传递消息给委托类,代理类不现实具体服务,而是利用委托类来完成服务,而完成这些流程不会对被代理类有代码侵入,代理类起到了中介的作用。
其实就是代理类为被代理类预处理消息、过滤消息并在此之后将消息转发给被代理类,之后还能进行消息的后置处理。代理类,代理类本身不实现服务,而是通过调用被代理类中的方法来提供服务。
静态代理
public interface IHello {
void sayHello(String str);
}
public class Hello implements IHello{
@Override
public void sayHello(String str) {
System.out.println("hello "+str);
}
}
public class ProxyHello implements IHello{
private IHello hello;
public ProxyHello(IHello hello) {
super();
this.hello = hello;
}
//加强的方法
@Override
public void sayHello(String str) {
Logger.start();
hello.sayHello(str);
Logger.end();
}
}
public class Logger {
public static void start(){
System.out.println(new Date()+ " say hello start...");
}
public static void end(){
System.out.println(new Date()+ " say hello end");
}
}
public class Test {
public static void main(String[] args) {
IHello hello = new ProxyHello(new Hello());
hello.sayHello("world");
}
}
JDK动态代理
实例1:简单实现
代理工厂
public class ProxyFatory {
//obj是被代理的对象
public static Object getProxyInstance(Object obj){
//对人
MyProxy myProxy=new MyProxy();
myProxy.bind(obj);
//返回的是代理人myProxy的invoke方法所返回的对象
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),myProxy);
}
}
代理人必须是InvocationHandler 方法的子类
public class MyProxy implements InvocationHandler {
private Object obj;
public void bind(Object obj){
this.obj=obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
proxy=obj;
return method.invoke(proxy,args);
}
}
被代理的对象
public class IPhone implements Apple {
private int id;
private String name;
public IPhone() {
}
@Override
public String getIdAndName(int id, String name) {
return name+id;
}
}
测试
public class Test {
@org.junit.Test
public void test1(){
Apple proxyInstance = (Apple) ProxyFatory.getProxyInstance(new IPhone());
String iPhone11 = proxyInstance.getIdAndName(1, "IPhone1");
System.out.println(iPhone11);
}
}
实例2:方法加强
需要被加强的接口类
public interface OrderService {
int insertData(Integer i);
}
需要被加强的接口实现类
public class OrderServiceImpl implements OrderService {
@Override
public int insertData(Integer i) {
System.out.println("执行插入业务");
return i;
}
}
代理类实现:
public class OrderProxy implements InvocationHandler {
private Object target;
public OrderProxy(Object target) {
this.target = target;
}
//前置增强
private void before(Object o){
if (o instanceof Integer) {
System.out.println("OrderService的前置增强....");
}
}
//后置增强
private void after(Object o){
if (o instanceof Integer) {
System.out.println("OrderService后置增强......");
}
}
public Object bind(){
Class aClass = target.getClass();
return Proxy.newProxyInstance(aClass.getClassLoader(),aClass.getInterfaces(),this);
}
//proxy代理类(这个很少用的),method被代理对象需要增强的方法,被代理对象执行方法的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//增强方法的参数,0代表第一个参数
Object arg = args[0];
before(arg);
//需要增强的方法
Object invoke = method.invoke(target, args);
after(arg);
return invoke;
}
}
测试
@org.junit.Test
public void test12() {
OrderService orderService = (OrderService) new OrderProxy(new OrderServiceImpl()).bind();
orderService.insertData(1);
}
CGLIB
jdk的动态代理需要实现接口,CGLIB不需要实现接口
引入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
被代理对象
public class CustomerServiceImpl {
public int insertData(Integer i) {
System.out.println("执行插入业务");
return i;
}
}
代理类
public class CustomerProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Customer的前置增强....");
Object o1 = methodProxy.invokeSuper(o, objects);
System.out.println("Customer后置增强......");
return o1;
}
}
测试
@org.junit.Test
public void test112() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(CustomerServiceImpl.class);
enhancer.setCallback(new CustomerProxy());
CustomerServiceImpl customerService = (CustomerServiceImpl) enhancer.create();
customerService.insertData(1);
}
面试题:
Cglib和jdk动态代理的区别?
- 1、Jdk动态代理:利用拦截器(必须实现InvocationHandler)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理
- 2、Cglib动态代理:利用ASM框架,对代理对象类生成的class文件加载进来,通过修改其字节码生成子类来处理
什么时候用cglib什么时候用jdk动态代理?
- 1、目标对象生成了接口 默认用JDK动态代理
- 2、如果目标对象使用了接口,可以强制使用cglib
- 3、如果目标对象没有实现接口,必须采用cglib库,Spring会自动在JDK动态代理和cglib之间转换
JDK动态代理和cglib字节码生成的区别?
- 1、JDK动态代理只能对实现了接口的类生成代理,而不能针对类
- 2、Cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法的增强,但是因为采用的是继承,所以该类或方法最好不要生成final,对于final类或方法,是无法继承的
Cglib比JDK快?
- 1、cglib底层是ASM字节码生成框架,但是字节码技术生成代理类,在JDL1.6之前比使用java反射的效率要高
- 2、在jdk6之后逐步对JDK动态代理进行了优化,在调用次数比较少时效率高于cglib代理效率
- 3、只有在大量调用的时候cglib的效率高,但是在1.8的时候JDK的效率已高于cglib
- 4、Cglib不能对声明final的方法进行代理,因为cglib是动态生成代理对象,final关键字修饰的类不可变只能被引用不能被修改
Spring如何选择是用JDK还是cglib?(Mybatis使用的是jdk的动态代理)
- 1、当bean实现接口时,会用JDK代理模式
- 2、当bean没有实现接口,用cglib实现
- 3、可以强制使用cglib(在spring配置中加入<aop:aspectj-autoproxy proxyt-target-class=”true”/>)
SpringAOP日志管理实战
https://www.cnblogs.com/jianjianyang/p/4910851.html
装饰器和代理模式的区别
对装饰器模式来说,装饰者和被装饰者都实现一个接口。对代理模式来说,代理类和真实处理的类都实现同一个接口。此外,不论我们使用哪一个模式,都可以很容易地在真实对象的方法前面或者后面加上自定义的方法。
在装饰模式调用者只想要你把他给你的对象装饰一下。而代理模式使用的是代理对象在自己的构造方法里面new的一个被代理的对象,不是调用者传入的。调用者不知道你找了其他人,他也不关心这些事,只要你把事情做对了即可。
-
1、装饰器模式强调的是增强自身,在被装饰之后你能够在被增强的类上使用增强后的功能。增强后你还是你,只不过能力更强了而已;代理模式强调要让别人帮你去做一些本身与你业务没有太多关系的职责(记录日志、设置缓存)。代理模式是为了实现对象的控制,因为被代理的对象往往难以直接获得或者是其内部不想暴露出来。
-
2、装饰模式是以对客户端透明的方式扩展对象的功能,是继承方案的一个替代方案;代理模式则是给一个对象提供一个代理对象,并由代理对象来控制对原有对象的引用;
-
3、装饰模式是为装饰的对象增强功能;而代理模式对代理的对象施加控制,但不对对象本身的功能进行增强;