Android 设计模式面试题

1.1请简要谈一谈单例模式?

借助类加载机制,可以在不使用synchronized等内容的情况下,最高效的实现单例。

    public class Singleton {
        public static Singleton getInstance() {
            // 1. 调用该方法时,才会访问LazyHolder.INSTANCE这个静态类的静态变量
            return LazyHolder.INSTANCE;
        }

        private static class LazyHolder {
            // 2. 访问 LazyHolder.INSTANCE才会触发Singleton的初始化
            static final Singleton INSTANCE = new Singleton();
        }

        private Singleton() {
        }
    }
  1. 参考自类加载规范的:访问静态字段时,才会初始化静态字段所在的类
  2. 因为类初始化是线程安全的,并且只会执行一次。因此在多线程环境下,依然能保证只有一个Singleton实例。
  3. 解释:getInstance()调用了LazyHolder的静态字段INSTANCE,所以会触发LazyHolder的加载和初始化。
    LazyHolder的初始化阶段会对静态字段INSTANCE进行赋值,也就是new Singleton(),此外初始化阶段是线程安全的且只执行一次,因此就算是多线程,也只会创建一个Singleton对象。从而实现单例。

1.2 对于面向对象的六大基本原则了解多少?

单一职责原则(Single Responsibility Principle):

对于一个类来说,它里面包含的应该是相关性很高的函数或者变量。比如,两个不相关的业务功能不应该放在一个类中处理,我们应该尽可能的针对于具体的业务功能对类进行拆分,以达到“每个类只负责自己需要关心的内容,其他的与我无关”的目的。

开闭原则(Open Close Principle):

开闭原则是我们程序设计过程中最基本的原则之一。在我们的程序里,我们所熟知的对象,对于扩展应该是开放的,而对于修改应该是封闭的。什么意思呢?其实很好理解。
我们平常在开发的时候可能会经常碰到一个问题:今天写了一个类,里面封装了很多功能方法,但是过了没多久,业务功能改动了,我们又不得不修改这个类里面已存在的代码,以满足当前业务需求。
然而,修改已存在的代码会带来很大问题,倘若这个类在很多其他类中用到,而且耦合度较高,那么即使我们对该类内部代码做很小的改动,可能都会导致与之相关(引用到该类)的部分的改动,如果我们不注意这个问题,可能就会一直陷入无止境修改和烦躁当中,这就违背了开闭原则。推荐的做法是:为了防止与之相关类(引用到该类)的改动,我们对于该类的修改应该是封闭的,我们可以提供对于该类功能的扩展途径。
那么该如何扩展呢?可以通过接口或者抽象类来实现,我们只需要暴露公共的方法(接口方法),然后由具体业务决定的类来实现这方法,并在里面处理具体的功能代码,至于对外具体怎么用,用户无需关心。
其实,开闭原则旨在指导用户,当我们业务功能需要变化时,应该尽量通过扩展的方式来实现,而不是通过修改已有代码来达到目的。只有这样,我们才能避免代码的冗余和腐化,才能使系统更加的稳定和灵活。

里氏替换原则(Liskov Substitution Principle):

对于一个系统来说,所有引用基类的地方必须同样能够透明地替换为其子类的对象。看下面这张图应该就能理
解什么意思了:

这里简单模拟了一下 Android 系统中的 Window 与 View 的关系,Window显示视图的时候只需要传一个 View 对象,但在具体的绘制过程中,传过来的可以是 View 的子类 TextView 或者是 ImageView 等等。依赖倒置原则(Dependence Inversion Principle):
在Java中可以这样理解:模块之间应该通过接口或者抽象来产生依赖关系,而实现类之间不应该发生直接的依赖关系。这也就是我们常说的面向接口/抽象编程。举个例子:

    interface Hobby {
        fun playGame()
    }
    class Boy : Hobby {
        override fun playGame() {
            System.out.print("男孩子喜欢枪战游戏.")
        }
    }
    class Girl : Hobby {
        override fun playGame() {
            System.out.print("女孩子喜欢看书和打扮.")
        }
    }
    class Survey {
        fun studyChildrenHobby(hobby: Hobby) {
            hobby.playGame()
        }
    }
    fun test(){
        val survey = Survey()
        survey.studyChildrenHobby(Girl())
    }

从上面简单的小例子可以看出,Survey 类依赖的是接口 Hobby,并没有依赖于具体的实现类。

接口隔离原则(Interface Segregation Principle):

接口隔离原则提倡类之间的依赖关系应该建立在最小接口上,提倡将复杂、臃肿的接口拆分为更加具体和细小的接口,以达到解耦的目的。这里的"最小接口"指的也是抽象的概念,将具体实现细节隔离,降低类的耦合性。

迪米特原则(Law of Demeter):

一个对象应该尽可能的对其他对象有最少的了解。即类与类之间应该尽可能的减小耦合度,否则一个类变化,它对另一个类的影响越大。
个人感觉,其实这几大原则来来回回阐述的都是"抽象"的概念,通过抽象来让我们的系统更加稳定、灵活和易维护。

1.3 请列出几种常见的工厂模式并说明它们的用法?

    //简单工厂模式:
    public interface MyAbInterca {
        public void a();
    }

    public class MyInterceImplement implements MyAbInterca {
        .......
    }

    //工厂方法模式:
    public abstract class Product {
        public abstract void method();
    }

    public class ConcreteProductA extends Product {
        @Override
        public void method() {
            System.out.println("产品A");
        }
    }

    public class ConcreteProductB extends Product {
        @Override
        public void method() {
            System.out.println("产品B");
        }
    }

    //抽象工厂模式:
    public abstract void method();
/**
 * 具体产品类A
 */
public class ConcreteProductA extends Product {
    @Override
    public void method() {
        System.out.println("我是具体的产品A");
    }
}

/**
 * 具体产品类B
 */
public class ConcreteProductB extends Product {
    @Override
    public void method() {
        System.out.println("我是具体的产品B");
    }
}

1.4 说说项目中用到的设计模式和使用场景?

单例模式
常见应用场景:网络请求的工具类、sp存储的工具类、弹窗的工具类等

工厂模式
常见应用场景:activity的基类等

责任链模式
常见应用场景:OKhttp的拦截器封装

观察者模式
常见应用场景:Rxjava的运用

代理模式
常见应用场景:AIDL的使用

建造者模式
常见应用场景:Dialog的创建

1.5 什么是代理模式?如何使用?Android源码中的代理模式?

  1. 代理模式
    代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
    这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法(那这个思想就能让我们做很多事情了)——代理模式是面向对象编程中比较常见的设计模式。
    举个例子来说明代理的作用:我们在生活中见到过的代理,大概最常见的就是商场里的商品。商品从工厂生产之后进入大型超市,之后消费者再来购买。

用图表示如下:

代理模式的关键点是:代理对象与目标对象,代理对象是对目标对象的扩展,并会调用目标对象。

1.1.静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类.
下面举个案例来解释:
模拟保存动作,定义一个保存动作的接口:IUserDao.java,然后目标对象实现这个接口的方法UserDao.java,此时如果使用静态代理方式,就需要在代理对象(UserDaoProxy.java)中也实现IUserDao接口。调用的时候通过调用代理对象的方法来调用目标对象。
需要注意的是,代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法
代码示例:

    public interface IUserDao {
        void addRecord();
    }

    public class UserDao implements IUserDao {
        @Override
        public void addRecord() {
            System.out.println("UserDao--为您添加一条新记录");
        }
    }

    public class UserDaoProxy implements IUserDao {
        private IUserDao target;

        public UserDaoProxy(IUserDao target) {
            this.target = target;
        }

        @Override
        public void addRecord() {
            System.out.println("开始执行任务。。。。");
            target.addRecord();
            System.out.println("提交执行任务。。。。");
        }
    }

    public static void staticProxy() {
        UserDaoProxy userDaoProxy = new
                UserDaoProxy(new UserDao());
        userDaoProxy.addRecord();
    }
开始执行任务。。。。
UserDao--为您添加一条新记录
提交执行任务。。。。
Process finished with exit code 0

静态代理总结:

优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展。
缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多。同时,一旦接口增加方法,目标对象与代理对象都要维护。

如何解决静态代理中的缺点呢?答案是可以使用动态代理方式

1.2.动态代理

动态代理有以下特点:

  1. 代理对象,不需要实现接口
  2. 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
  3. 动态代理也叫做:JDK代理,接口代理

JDK中生成代理对象的API

代理类所在包:ava.lang.reflect.Proxy
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:

static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:
ClassLoader loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的
Class[] interfaces:目标对象实现的接口的类型,使用泛型方式确认类型
InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入

上一节代码中 UserDaoProxy 类是代理,我们需要手动编写代码让 UserDaoProxy 实现 IUserDao 接口,而在动态代理中,我们可以让程序在运行的时候自动在内存中创建一个实现 IUserDao接口的代理,而不需要去定义UserDaoProxy这个类。这就是它被称为动态的原因。
也许概念比较抽象。现在实例说明一下情况。柳胥街有很多饭店,口味独特,分量十足,有一个饭店卖红烧爆鱼,我们用Java代码模拟。

    public interface Restaurant {
        void eatFan();
    }

    public class SauerkrautRest implements Restaurant {
        @Override
        public void eatFan() {
            System.out.println("来我家吃酸菜鱼啦");
        }
    }

    public class PlaceForSell implements InvocationHandler {
        private Object target;

        public PlaceForSell(Object service) {
            this.target = service;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("this is a place for restaurant ,please coming");
            method.invoke(target, args);
            System.out.println("please seat down and have fun");
            return null;
        }
    }

    public static void dynamicProxy() {
        SauerkrautRest sauerkrautRest = new SauerkrautRest();
        InvocationHandler invocationHandler = new PlaceForSell(sauerkrautRest);
        Restaurant sauerkRest = (Restaurant) Proxy.newProxyInstance(
                SauerkrautRest.class.getClassLoader(),
                SauerkrautRest.class.getInterfaces(),
                invocationHandler);
        //Proxy.newProxyInstance(target.getClass.getClassLoader(), 
        // target.getClass.getInterfaces(),
        // invocationHandler); 第一二个参数皆为target的
        sauerkRest.eatFan();
    }
this is a place for restaurant ,please coming
来我家吃酸菜鱼啦
please seat down and have fun

InvocationHandler

InvocationHandler 是一个接口,官方文档解释说,每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。

    /**
     * {@code InvocationHandler} is the interface
     * implemented by
     * the invocation handler of a proxy
     * instance.
     *
     * 

Each proxy instance has an associated * invocation handler. * When a method is invoked on a proxy * instance, the method * invocation is encoded and dispatched to * the {@code invoke} * method of its invocation handler. * * @author Peter Jones * @see Proxy * @since 1.3 */ public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }

InvocationHandler 内部只是一个 invoke() 方法,正是这个方法决定了怎么样处理代理传递过来的方法调用。

proxy 代理对象
method 代理对象调用的方法
args 调用的方法中的参数

因为,Proxy 动态产生的代理会调用 InvocationHandler实现类,所以 InvocationHandler 是实际执行者。

所以我们可以进一步将其封装为一个ProxyFactory,节省时间与复用代码,follow me AV8D
    public class ProxyFactory {
        private Object target;

        public ProxyFactory(Object target) {
            this.target = target;
        }

        public Object getProxyInstance() {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(), new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            System.out.println("开始事务--" + target.getClass().getSimpleName() + "-----");
                            //执行目标对象方法
                            Object returnValue = method.invoke(target, args);
                            System.out.println("提交事务--" + target.getClass().getSimpleName() + "-----");
                            return returnValue;
                        }
                    });
        }
    }

动态代理原理探究

一定有同学对于为什么 Proxy 能够动态产生不同接口类型的代理感兴趣,我的猜测是肯定通过传入进去的接口然后通过反射动态生成了一个接口实例。
比如 Restaurant 是一个接口,那么Proxy.newProxyInstance() 内部肯定会有,那么我们进入源码查看。

    /**
     * Returns an instance of a proxy class for
     * the specified interfaces
     * that dispatches method invocations to the
     * specified invocation
     * handler.
     *
     * 

{@code Proxy.newProxyInstance} throws * {@code IllegalArgumentException} for the * same reasons that * {@code Proxy.getProxyClass} does. * * @param loader the class loader to define * the proxy class * @param interfaces the list of interfaces * for the proxy class * to implement * @param h the invocation handler to * dispatch method invocations to * @return a proxy instance with the * specified invocation handler of a * proxy class that is defined by * the specified class loader * and that implements the specified * interfaces * @throws IllegalArgumentException if any * of the restrictions on the * parameters that may be passed to * {@code getProxyClass} * are violated * @throws SecurityException if a security * manager, s, is present * and any of the following * conditions is met: *

    *
  • the given {@code loader} is * {@code null} and * the caller's class loader is * not {@code null} and the * invocation of {@link * SecurityManager#checkPermission * s.checkPermission} with * {@code * RuntimePermission("getClassLoader")} * permission * denies access;
  • *
  • for each proxy interface, * {@code intf}, * the caller's class loader is * not the same as or an * ancestor of the class loader * for {@code intf} and * invocation of {@link * SecurityManager#checkPackageAccess * s.checkPackageAccess()} * denies access to {@code intf};
  • *
  • any of the given proxy * interfaces is non-public and the * caller class is not in the * same {@linkplain Package runtime package} * as the non-public interface * and the invocation of * {@link * SecurityManager#checkPermission * s.checkPermission} with * {@code * ReflectPermission("newProxyInPackage.{package * name}")} * permission denies access. *
  • *
* @throws NullPointerException if the * {@code interfaces} array * argument or any of its elements * are {@code null}, or * if the invocation handler, {@code * h}, is * {@code null} */ @CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated * proxy class. */ Class cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the * designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } final Constructor cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction() { 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); } }

newProxyInstance 的确创建了一个实例,它是通过 cl 这个 Class 文件的构造方法反射生成。cl 由getProxyClass0() 方法获取。

   /**
     * Generate a proxy class. Must call the
     * checkProxyAccess method
     * to perform permission checks before
     * calling this.
     */
    private static Class getProxyClass0(ClassLoader loader, Class... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        /*如果给定的加载程序定义的代理类给定的接口存在,这将简单地返回缓存的副本;否则,它将通过代理类工厂创建代理类。*/
        return proxyClassCache.get(loader, interfaces);
    }

    /**
     * A factory function that generates, defines
     * and returns the proxy class given
     * the ClassLoader and array of interfaces.
     */
    private static final class ProxyClassFactory implements BiFunction[], 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, 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
            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.
             */
            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 + ".";
            }
            /*生成代理类从这里开始
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
            /*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
            try {
                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());
            }
        }
    }

通过指定的 ClassLoader 和 接口数组 用工厂方法生成proxy class。 然后这个 proxy class 动态生成的代理类名称是包名+$Proxy+id序号。

long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;

Cglib代理

上面的静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理

Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。

  • JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现。
  • Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
  • Cglib包的底层是通过使用一个小而快的字节码处理框架ASM来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

Cglib子类代理实现方法:

  1. 需要引入cglib的jar文件即可
  2. 引入功能包后,就可以在内存中动态构建子类
  3. 代理的类不能为final,否则报错
  4. 目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法。
    public class CgLibProxyFactory implements MethodInterceptor {
        private Object target;

        public CgLibProxyFactory(Object target) {
            this.target = target;
        }

        //给目标对象创建一个代理对象
        public Object getProxyInstance() {
            //1.工具类
            Enhancer en = new Enhancer();
            //2.设置父类
            en.setSuperclass(target.getClass());
            //3.设置回调函数
            en.setCallback(this);
            //4.创建子类(代理对象)
            return en.create();
        }

        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("开始事务--" + o.getClass().getSimpleName() + "-----");
            //执行目标对象方法
            Object returnValue = method.invoke(target, objects);
            System.out.println("提交事务--" + target.getClass().getSimpleName() + "-----");
            return returnValue;
        }
    }

    public class Animal {
        public void ying() {
            System.out.println("this is a animal");
        }
    }

    public static void dynamicCglibProxy() {
        Animal animal = new Animal();
        Animal proxy = (Animal) new CgLibProxyFactory(animal).getProxyInstance();
        proxy.ying();
    }
开始事务--Animal$$EnhancerByCGLIB$$cc10e0b0-----
this is a animal
提交事务--Animal-----

这种代理模式非JDK提供

代理的作用

主要作用,还是在不修改被代理对象的源码上,进行功能的增强。
这在 AOP 面向切面编程领域经常见。
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
主要功能
日志记录,性能统计,安全控制,事务处理,异常处理等等。
上面的引用是百度百科对于 AOP 的解释,至于,如何通过代理来进行日志记录功能、性能统计等等,这个大家可以参考 AOP 的相关源码,然后仔细琢磨。

同注解一样,很多同学可能会有疑惑,我什么时候用代理呢?

总结

代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理(JDK),文中Cglib代理就不需要实现任何接口
动态代理,代理类通过 Proxy.newInstance() 方法生成。
静态代理和动态代理的区别是在于要不要开发者自己定义Proxy 类。
动态代理通过 Proxy 动态生成 proxy class,但是它也指定了一个 InvocationHandler 的实现类。
代理模式目的是为了增强增加现有代码的功能。

1.6 谈一谈单例模式,建造者模式,工厂模式的使用场景?如何合理选择?

单例模式,一般是指将消耗内存、属性和对象支持全局公用的对象,应该设置为单例模式,如持久化处理(网络、文件等)

建造者模式,一般见于开发的框架或者属性时可以直接链式设置属性,比如我们看到的AlertDialog,一般用在某些辅助类(如BRVAH的BaseViewHolder)或者开发的框架的时候方便连续设置多个属性和调用多个方法。

工厂模式,一般用于业务的实体创建,在创建的过程中考虑到后期的扩展。在Android源码中比较常见的有BitmapFactory LayoutInflater.Factory,在实体编码的过程中,比如BRVAH的多布局,如果数据类型比较多或者后期需要扩展,则可以通过工厂布局的方式,将实现MultiItemEntity接口的实体通过工厂模式创建:

    object MultiItemEntityFactory {
        val TEXT = 1
        val IMG = 2
        val TEXT_IMG = 3
        fun createBean(type: Int): MultiItemEntity {
            when(type){
                TEXT -> return BeanA()
                IMG -> return BeanB()
                TEXT_IMG -> return BeanC()
                else -> return BeanA()
            }
        }
    }
    class MultipleItemQuickAdapter(data: List<*>): BaseMultiItemQuickAdapter(data) {
        init {
            addItemType(MultipleItem.TEXT, R.layout.text_view)
            addItemType(MultipleItem.IMG, R.layout.image_view)
        }
        override fun convert(helper: BaseViewHolder, item: MultipleItem) {
            ...
        }
    }

1.7 谈谈你对原型模式的理解?

  1. 定义
    用原型对象的实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。

  2. 使用场景
    (1)类初始化需要消耗比较多的资源,通过原型拷贝可以避免这些消耗
    (2)当new一个对象需要非常繁琐的数据准备等,这时可以使用原型模式
    (3)当一个对象需要提供给其他调用者使用,并且各个调用者都可能修改其值时,通过原型模式拷贝多个对象供调用者使用,保护性拷贝

Android 源码中例子:
Intent,Intent的查找与匹配原型模式实质上就是对象拷贝,要注意深拷贝和浅拷贝问题。
还有就是保护性拷贝,就是某个对象对外是只读的,为了防止外部对这个只读对象修改,通常可以通过返回一个对象的拷贝来实现只读的限制。

优点:
原型模式是在内存中的二进制流的拷贝,性能要比new一个对象好的多,减少了约束。
缺点:
直接在内存中拷贝,构造函数是不会执行的

1.8 请谈谈策略模式原理及其应用场景?

策略模式很像简单工厂模式,后者是根据业务条件创建不同的对象,前者是根据业务条件去使用不同的策略对象。
区别看下图
简单工厂模式

策略模式

主要区别在工厂类和CashContext这个两个类
工厂类

    public class ShapeFactory {
        public static Shape createShape(String shape){
            switch (shape){
                case "circle":
                    return new Circle();
                case "rectangle":
                    return new Ractangle();
            }
            return null;
        }
    }

CashContext

    class CashContext{
        Strategy strategy;
        public CashContext(Strategy strategy ){
            this.strategy = strategy;
        }
        //操作业务
        public void method(){
            strategy.method;
        }
    }

区别:
1 工厂没有持有具体类的引用,策略构造器(CashContext)持有了具体类的引用,
2 工厂根据条件创建不同的对象,此时的策略构造器(CashContext)要在activity中做条件判断(很明显这里可以和简单工厂模式结合)

可知简单工厂一旦新增了具体类,工厂类就要做修改,但是策略模式不用修改CashContext类,他直接替换具体类。
但是当我们需要根据条件创建具体类的时候,策略模式会使得要在activity中做条件判断来确定创建什么具体类。这里又想到了使用工厂来优化。

结合简单工厂模式后的策略构造器(CashContext)

    class CashContext{
        Strategy strategy;
        public CashContext(string condition){
            switch (condition){
                case "circle":
                    strategy = new CicleStrategy();
                    break;
                case "rectangle":
                    strategy = new RectangleStrategy();
                    break;
            }
        }
        //操作业务
        public void method(){
            strategy.method;
        }
    }

1.9 静态代理和动态代理的区别,什么场景使用?

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

动态代理类:在程序运行时,运用反射机制动态创建而成。

静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。
动态代理是实现JDK里的InvocationHandler接口的invoke方法,但注意的是代理的是接口,也就是你的业务类必须要实现接口,通过Proxy里的newProxyInstance得到代理对象。

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

1.10 谈一谈责任链模式的使用场景?

责任连模式定义: 将多个对象连成一条链,并沿着这条链传递该请求,只到有对象处理该请求为止。使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。

Android中责任链场景:

  1. Android 源码中对于事件分发是基于该模式,Android 会将事件包装成一个事件对象从ViewTree的顶部至上而下的分发传递,只到有View处理该事件为止。
  2. OkHttp 的拦截器也是基于责任链模式,用户请求和服务器返回数据,会经过内置拦截器链逐级的处理。

你可能感兴趣的:(Android 设计模式面试题)