《设计模式之禅》学习及源码示例---装饰模式\适配器模式\代理模

2020-12-31  本文已影响0人  笔记本一号

一、装饰模式

定义:

动态的给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更加灵活

组成角色:
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动态代理的区别?
什么时候用cglib什么时候用jdk动态代理?
JDK动态代理和cglib字节码生成的区别?
Cglib比JDK快?
Spring如何选择是用JDK还是cglib?(Mybatis使用的是jdk的动态代理)
SpringAOP日志管理实战

https://www.cnblogs.com/jianjianyang/p/4910851.html

装饰器和代理模式的区别

对装饰器模式来说,装饰者和被装饰者都实现一个接口。对代理模式来说,代理类和真实处理的类都实现同一个接口。此外,不论我们使用哪一个模式,都可以很容易地在真实对象的方法前面或者后面加上自定义的方法。

在装饰模式调用者只想要你把他给你的对象装饰一下。而代理模式使用的是代理对象在自己的构造方法里面new的一个被代理的对象,不是调用者传入的。调用者不知道你找了其他人,他也不关心这些事,只要你把事情做对了即可。

上一篇下一篇

猜你喜欢

热点阅读