设计模式-结构型

本文详细解读了代理模式、适配器模式和装饰者模式,展示了如何在实际项目中利用这些设计模式扩展功能、解耦组件。通过实例演示,理解如何创建静态代理、动态代理,以及如何使用适配器让不兼容组件协同工作,装饰器如何灵活添加功能到对象上。
摘要由CSDN通过智能技术生成

目录

 

结构型模式

1、代理模式

1.1 代理模式定义

1.2 代理模式优缺点

1.3 结构与实现

1.4 代码实现

1.5 代理模式使用场景

1.6 代理模式分类

1.7 JDK动态代理

1.8 CGLIB动态代理

1.10 静态代理和动态代理区别

2、适配器模式

2.1 定义与特点

2.2 优点和解决的问题

2.3 结构

2.4 案例

2.5 代码实现

3、装饰器模式

3.1 定义

3.2 结构角色

3.3 应用案例


结构型模式

关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。

1、代理模式

1.1 代理模式定义

代理模式又叫委托模式,是为某个对象提供一个代理对象,并且由代理对象控制对原对象的访问。提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.

举个例子来说明代理的作用:假设我们想邀请一位明星,那么并不是直接连接明星,而是联系明星的经纪人,来达到同样的目的.明星就是一个目标对象,他只要负责活动中的节目,而其他琐碎的事情就交给他的代理人(经纪人)来解决.这就是代理思想在现实中的一个例子

1.2 代理模式优缺点

优点:

  • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;

  • 代理对象可以扩展目标对象的功能;

  • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;

缺点:

  • 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;

  • 增加了系统的复杂度;

1.3 结构与实现

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2h1eGlhbmcxOTg1MTExNA==,size_16,color_FFFFFF,t_70

模式的结构 :

  • 抽象主题(Subject):通过接口或抽象类声明真实主题和代理对象实现的业务方 法。

  • 真实主题(RealSubject):实现了抽象主题中的具体业务,是代理对象所代表的真 实对象,是最终要引用的对象。

  • 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用, 它可以访问、控制或扩展主题的功能。

1.4 代码实现

抽象主题

/**
 * @Author:Shane
 * @Date 2020/7/22 19 52
 */
public interface Subject {
    public void request();
}

真实主题

/**
 * @Author:Shane
 * @Date 2020/7/22 19 53
 */
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("访问真实主题方法");
    }
}

代理类

/**
 * @Author:Shane
 * @Date 2020/7/22 19 53
 */
public class Proxy implements Subject {
    private RealSubject realSubject;
    @Override
    public void request() {
        if(realSubject == null){
            realSubject = new RealSubject();
        }
        preRequest();
        realSubject.request();
        postRequest();
    }
​
    private void postRequest() {
        System.out.println("访问真实主题之后的后续处理");
    }
​
    private void preRequest() {
        System.out.println("访问真实主题之前的预处理");
    }
}

测试类

/**
 * @Author:Shane
 * @Date 2020/7/22 19 57
 */
public class ProxyTest {
    public static void main(String[] args) {
        Proxy proxy = new Proxy();
        proxy.request();
    }
}

1.5 代理模式使用场景

代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是通过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公 共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。

1.6 代理模式分类

静态代理

实现方式:代理类和被代理类要实现同一主题接口,而且在代理类中要有一个被代理类的属性(target),这样才能把核心业务逻辑交还给被代理类完成,而一些与核心业务逻辑无关 的逻辑,并且需求是多变的,那么这些逻辑就可以交给代理类来完成。

动态代理

动态代理跟静态代理一个最大的区别就是:动态代理是在运行时刻动态的创建出代理类及其 对象。静态代理是在编译的时候就确定了代理类的具体实例,如果有多个类需要代理,那么就得创建多个。如果Subject中新增了一个方法,那么对应得实现接口得类中也要响应得实现这些方法。

JDK动态代理的做法:在运行时刻,可以动态创建出一个实现了多个接口的代理类。每个代理类 的对象都会关联一个表示内部处理逻辑的InvocationHandler接口的实现,当使用者调用了 代理对象所代理的接口中的方法的时候,这个调用的信息会被传递给InvocationHandler的 invoke方法。在 invoke方法的参数中可以获取到代理对象、方法对应的Method对象和调 用的实际参数。invoke方法的返回值被返回给使用者。这种做法实际上相 当于对方法调用进行了拦截。

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2h1eGlhbmcxOTg1MTExNA==,size_16,color_FFFFFF,t_70

动态代理实现过程:

  • 通过实现InvocationHandler接口创建自己的调用处理器;

  • 通过Proxy类指定ClassLoader对象和一组interface来创建动态代理类;

  • 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;

  • 通过构造函数创建动态代理类的实例,构造时调用处理器对象作为参数被传入。

1.7 JDK动态代理

1.7.1 案例

抽象主题

/**
 * @Author:Shane
 * @Date 2020/7/22 21 48
 */
public interface Subject {
    public void subjectShow();
}

真实主题

/**
 * @Author:Shane
 * @Date 2020/7/22 21 49
 */
public class RealSubject implements Subject {
    @Override
    public void subjectShow() {
        System.out.println("杀人是我指示的,我是幕后黑手!..."+getClass());
    }
}

动态代理类

/**
 * @Author:Shane
 * @Date 2020/7/22 21 50
 */
public class MyProxy implements InvocationHandler {
    private Object proxied;
    public MyProxy(Object proxied){
        this.proxied = proxied;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("准备工作...");
        Object object = method.invoke(proxied,args);
        System.out.println("工作已经做完了....");
        return object;
    }
}

测试

/**
 * @Author:Shane
 * @Date 2020/7/22 21 53
 */
public class DynamicProxyTest {
    public static void main(String[] args) {
        RealSubject real = new RealSubject();
        Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
                new Class[]{Subject.class},new MyProxy(real));
        proxySubject.subjectShow();
    }
}

1.7.2 源码分析

Proxy

// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器,比如上面代码中的ProxyHandler
static InvocationHandler getInvocationHandler(Object proxy)
// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
static Class getProxyClass(ClassLoader loader, Class[] interfaces)
// 方法 3:该方法用于判断指定类对象是否是一个动态代理类
static boolean isProxyClass(Class cl)
// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

重点看看newProxyInstance方法:

//这个注解在下面Reflection.getCallerClass()作为判断,内核是C语言
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler invocationHandler){
    //判断代理类是否实现了InvocationHandler接口,没有的话就啥也别干了,抛空指针异常
    Objects.requireNonNull(h);
    //将接口clone,之后对此clone类进行操作
    final Class<?>[] intfs = interfaces.clone();
    //进行Java安全管理器权限检查,因为生成的代理对象类执行对于系统来说是一个位置的Java程序
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
          //这个比较复杂,主要是JDK环境变量是否配置,接口是否开放,类加载器是否为目标对象类或者抽象接口
          checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
     }
    //查找/生成代理类
    Class<?> cl = getProxyClass0(loader, intfs);
    try {
        if (sm != null) {
            //执行权限检查调用方是否在代理类的不同运行时包中
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }
        //拿到代理类构造器
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        //如果该代理类语言修饰符不是public,需要先设置其构造方法执行是不执行权限检查(暴力反射)
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        //创建返回代理对象实例
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}
  • loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象 进行加载

  • interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一 组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态)

  • h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时 候,会关联到哪一个InvocationHandler

  • 从上面JDK源码中可以看出getProxyClass0方法才是newProxyInstance方法中最重 要的,该方法负责为一组接口动态地生成代理类类型对象

​
private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    //接口长度不能超过2个字节最大保存的数值(十进制)
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }
​
    // 如果代理类被指定的类加载器loader定义了,并实现了给定的接口interfaces,返回缓存的代理对象;否则,它将通过ProxyClassFactory创建代理类
    return proxyClassCache.get(loader, interfaces);
}

后面的代码逻辑已经太底层了,我们看最核心的即可!

 //这里的BiFunction<T, U, R>是个函数式接口,可以理解为用T,U两种类型做参数,得到R类型的返回值
private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names
        //所有代理类名字的前缀
        private static final String proxyClassNamePrefix = "$Proxy";
        
        // next number to use for generation of unique proxy class names
        //用于生成代理类名字的计数器
        private static final AtomicLong nextUniqueNumber = new AtomicLong();
​
        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
              
            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            //验证代理接口,可不看
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }
            //生成的代理类的包名 
            String proxyPkg = null;     // package to define proxy class in
            //代理类访问控制符: public ,final
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
​
            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            //验证所有非公共的接口在同一个包内;公共的就无需处理
            //生成包名和类名的逻辑,包名默认是com.sun.proxy,类名默认是$Proxy 加上一个自增的整数值
            //如果被代理类是 non-public proxy interface ,则用和被代理类接口一样的包名
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }
​
            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }
​
            /*
             * 获得一个编号,线程安全
             */
            long num = nextUniqueNumber.getAndIncrement();
            //代理类的完全限定名,如com.sun.proxy.$Proxy0.calss
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
​
            /*
             * Generate the specified proxy class.
             */
            //核心部分,生成代理类的字节码
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                //把代理类加载到JVM中,至此动态代理过程基本结束了
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

1.8 CGLIB动态代理

使用基于cglib为目标对象创建代理对象时,目标对象可以不实现接口。cglib是一个强大的 高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOP为他们提供方法 的interception(拦截),CGLIG底层使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成java的字节码。当然不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

1.8.1 案例

导入cglib库

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

实现一个业务类(计算sql调用总时间),注意,这个业务类并没有实现任何接口:

/**
 * @Author:Shane
 * @Date 2020/7/23 14 10
 * 创建一个需要被代理的类SqlService
 */
​
public class SqlService {
    public void executeSql(){
        System.out.println("sql开始执行");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sql执行结束.....");
    }
}

创建代理类:SqlServiceCglibProxy

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
​
import java.lang.reflect.Method;
​
/**
 * @Author:Shane
 * @Date 2020/7/23 14 17
 */
public class SqlServiceCglibProxy implements MethodInterceptor {
    //被代理对象
    private Object target;
​
    public SqlServiceCglibProxy(Object target){
        this.target = target;
    }
​
    /**
     * 实现回调方法
     * @param obj:代理的对象
     * @param method:被代理对象的方法
     * @param args:参数集合
     * @param proxy:生成代理类的方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 开始执行时间
        Long startTime = System.currentTimeMillis();
        //调用业务类(父类)的方法
        Object result = proxy.invokeSuper(obj, args);
        //执行结束
        Long endTime = System.currentTimeMillis();
        System.out.println(target.getClass().getName()+"执行executeSql耗时:"+(endTime - startTime)+"ms");
        return result;
    }
}

测试

import net.sf.cglib.proxy.Enhancer;
​
/**
 * @Author:Shane
 * @Date 2020/7/23 14 24
 */
public class CglibTest {
    public static void main(String[] args) {
        //创建目标对象
        SqlService sqlService = new SqlService();
        SqlServiceCglibProxy s = new SqlServiceCglibProxy(sqlService);
​
        //创建代理对象
        //创建增强器对象
        Enhancer e = new Enhancer();
        //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类
        e.setSuperclass(sqlService.getClass());
        //设置回调:对于代理类上所有方法的调用,都会调用callBack,而CallBack则需要实现intercept方法进行拦截
        e.setCallback(s);
        //创建动态代理类对象并返回
        SqlService sqlServiceProxy = (SqlService) e.create();
        //调用
        sqlServiceProxy.executeSql();
    }
}

1.8.2 源码解析

实现CGLIB动态代理必须实现MethodInterceptor(方法拦截器)接口,源码如下:

 public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                           MethodProxy proxy) throws Throwable;

这个接口只有一个intercept()方法,这个方法有4个参数:

  • obj表示增强的对象,即实现这个接口类的一个对象;

  • method表示要被拦截的方法;

  • args表示要被拦截方法的参数;

  • proxy表示要触发父类的方法对象;

通过Enhancer.create()方法创建代理对象,create()方法的源码:

public Object create() {
    classOnly = false;
    argumentTypes = null;
    return createHelper();
}

该方法含义就是如果有必要就创建一个新类,并且用指定的回调对象创建一个新的对象实 例,使用的父类的参数的构造方法来实例化父类的部分。核心内容在createHelper()中,源码如下:

private Object createHelper() {
    //校验:回调类型吗,比如方法拦截器,回调过滤器(比如Enhancer加强器)
    preValidate();
    //创建代理对象Key,可以用于获取缓存的代理对象,数据格式为:com.ydt.design.cglib.SqlService, null, null, {Lnet/sf/cglib/proxy/MethodInterceptor;}, true, true, null
    Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
            ReflectUtils.getNames(interfaces),
            filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
            callbackTypes,
            useFactory,
            interceptDuringConstruction,
            serialVersionUID);
    this.currentKey = key;
    Object result = super.create(key);
    return result;
}
protected Object create(Object key) {
    try {
        ClassLoader loader = getClassLoader();
        Map<ClassLoader, ClassLoaderData> cache = CACHE;
        //通过类加载器获取缓存对象
        ClassLoaderData data = cache.get(loader);
        if (data == null) {
            //考虑并发情况下缓存对象的覆盖问题
            synchronized (AbstractClassGenerator.class) {
                cache = CACHE;
                data = cache.get(loader);
                if (data == null) {
                    Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);
                    data = new ClassLoaderData(loader);
                    newCache.put(loader, data);
                    CACHE = newCache;
                }
            }
        }
        this.key = key;
        Object obj = data.get(this, getUseCache());
        if (obj instanceof Class) {
            return firstInstance((Class) obj);
        }
        //如果没有缓存的代理对象,创建之
        return nextInstance(obj);
    } catch (RuntimeException e) {
        throw e;
    } catch (Error e) {
        throw e;
    } catch (Exception e) {
        throw new CodeGenerationException(e);
    }
}
protected Object nextInstance(Object instance) {
    EnhancerFactoryData data = (EnhancerFactoryData) instance;

    if (classOnly) {
        return data.generatedClass;
    }

    Class[] argumentTypes = this.argumentTypes;
    Object[] arguments = this.arguments;
    if (argumentTypes == null) {
        argumentTypes = Constants.EMPTY_CLASS_ARRAY;
        arguments = null;
    }
    //后面都是反射操作了,传入参数类型,参数值,设置回调对象
    return data.newInstance(argumentTypes, arguments, callbacks);
}

1.10 静态代理和动态代理区别

  • 静态代理类:由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。

  • 动态代理类:在程序运行时,运用反射机制动态创建而成。静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。

  • 静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。

  • 动态代理是实现JDK里的InvocationHandler接口的invoke方法,但注意的是代理的 是接口,也就是你的业务类必须要实现接口,通过Proxy里的newProxyInstance得 到代理对象。

  • 另外:CGLIB动态代理,代理的是类,不需要业务类继承接口,通过派生的子类来 实现代理。通过在运行时,动态修改字节码达到修改类的目的。

 

2、适配器模式

2.1 定义与特点

适配器模式(Adapter)的定义如下:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能正常一起工作的哪些类能一起工作。适配器模式分为类结构型模式和对象结构型模式两种,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,应用相对较少些。

2.2 优点和解决的问题

优点:

  • 客户端通过适配器可以透明的调用目标接口

  • 复用了现存的类,程序员不需要修改原有的代码而重用现有的适配者类。

  • 将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。

解决的问题

需要开发的具有某种业务功能的组件在现有的组件库中已经存在,但它们与当前系统的接口规范不兼容,如果重新开发这些组件成本又很高,这时用适配器模式能很好地解决这些问题。

2.3 结构

类适配器模式

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2h1eGlhbmcxOTg1MTExNA==,size_16,color_FFFFFF,t_70

对象适配器模式

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2h1eGlhbmcxOTg1MTExNA==,size_16,color_FFFFFF,t_70

适配器模式(Adapter)包含以下主要角色:

  • 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。

  • 适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。

  • 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。

2.4 案例

用适配器模式模拟新能源骑车发动机

分析:新能源骑车的发动机有电能发动机(Electric Motor)和光能发动机(Optical Motor)等,各种发动机的驱动方法不同,例如,电能发动机的驱动方法electricDrive()是用电能驱动,而光能发动机的驱动方法opticalDrive()是用光能驱动,他们是适配器模式中被访问的适配者。

客户端希望用同一的发动机驱动方法drive()方法访问两种发动机,所以必须定义一个统一的目标接口Motor,然后再定义电能适配器(Electric Adapter)和光能适配器(Optical Adapter)去适配这两种发动机。

2.5 代码实现

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2h1eGlhbmcxOTg1MTExNA==,size_16,color_FFFFFF,t_70

目标:发动机

public interface Motor {
    public void drive();
}

适配者1:电能发动机

public class ElectricMotor {
    public void electircDrive(){
        System.out.println("电能发动机驱动骑车");
    }
}

适配者2:光能发动机

public class OpticalMotor {
    public void opticalDrive(){
        System.out.println("光能发动机驱动骑车");
    }
}

电能适配器

public class ElectricAdapter implements Motor {
    private ElectricMotor emotor;

    public ElectricAdapter(ElectricMotor emotor){
        this.emotor = new ElectricMotor();
    }
    @Override
    public void drive() {
        emotor.electircDrive();
    }
}

光能适配器

public class OpticalAdapter implements Motor {
    private OpticalMotor omotor;

    public OpticalAdapter(OpticalMotor omotor){
        this.omotor = new OpticalMotor();
    }
    @Override
    public void drive() {
        omotor.opticalDrive();
    }
}

测试

public class MotorAdapterTest {
    public static void main(String[] args) {
        System.out.println("适配器模式测试:");
        ElectricMotor emotor = new ElectricMotor();
        Motor motor=new ElectricAdapter(emotor);
        motor.drive();
    }
}

3、装饰器模式

3.1 定义

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构,它是作为现有的类的一个包装。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能,所以又叫包装模式

主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。

何时使用:在不想增加很多子类的情况下扩展类。

使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销

3.2 结构角色

主要包含以下角色:

  • 抽象构建(Component)角色:定义一个抽象接口以规范准备接受附加责任的对象。

  • 具体构建(Concrete Component)角色:实现抽象构建,通过装饰角色为其添加一些职责。

  • 抽象装饰(Decorator)角色:继承抽象构建,并包含具体构建的实例,可以通过其子类扩展具体构建的功能。

  • 具体装饰(ConreteDecorator)角色:实现抽象装饰的相关方法,并给具体构建对象添加附加的责任

类图结构

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2h1eGlhbmcxOTg1MTExNA==,size_16,color_FFFFFF,t_70

3.3 应用案例

不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。

抽象构建角色:画

/**
 * 抽象构建角色:画
 */
public interface Draw {
   void show();
}

具体构建角色:油画,山水画

/**
 * 具体构建角色:油画
 */
public class OIlDraw implements Draw {
    @Override
    public void show() {
        System.out.println("这是一副油画!");
    }
}
/**
 * 具体构建角色:山水画
 */
public class LandscapeDraw implements Draw {
    @Override
    public void show() {
        System.out.println("这是一副山水画!");
    }
}

抽象装饰(Decorator)角色:画框,实现画的原功能

/**
 * 抽象装饰(Decorator)角色:画框,实现画的原功能
 */
public abstract class DrawDecorator implements Draw{
​
    private Draw draw;
​
    public DrawDecorator(Draw draw) {
        this.draw = draw;
    }
​
    @Override
    public void show() {
        draw.show();
    }
}

具体装饰(Decorator)角色:油,山水画框,继承抽象装饰角色基本功能,增加新的装饰行为

/**
 * 具体装饰(Decorator)角色:油画框,继承抽象装饰角色基本功能,增加新的装饰行为
 */
public class OilDrawDecorator extends DrawDecorator{
​
    public OilDrawDecorator(Draw draw) {
        super(draw);
    }
​
    @Override
    public void show() {
        super.show();
        addGlass();
    }
​
    private void addGlass(){
        System.out.println("增加玻璃覆盖,避免潮湿");
    }
}
/**
 * 具体装饰(Decorator)角色:山水画框,继承抽象装饰角色基本功能,增加新的装饰行为
 */
public class LandscapeDrawDecorator extends DrawDecorator{
​
​
    public LandscapeDrawDecorator(Draw draw) {
        super(draw);
    }
​
    @Override
    public void show() {
        super.show();
        anticorrosive();
    }
​
    private void anticorrosive(){
        System.out.println("添加杀虫剂,防腐防虫");
    }
}

测试

public class DecoratorTest {
​
    public static void main(String[] args) {
        Draw draw1 = new OIlDraw();
        Draw draw2 = new LandscapeDraw();
​
        DrawDecorator drawDecorator1 = new OilDrawDecorator(draw1);
        drawDecorator1.show();
        System.out.println("\n");
        DrawDecorator drawDecorator2 = new LandscapeDrawDecorator(draw2);
        drawDecorator2.show();
    }
}

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值