移动架构01-设计模式

作者: 最爱的火 | 来源:发表于2018-05-30 08:33 被阅读41次

    移动架构01-设计模式

    设计模式是一套设计标准,用来指导实际的设计工作。作用是提高工作效率。

    Android常见的设计模式:工厂模式、建造者模式、单例模式、观察者模式、代理模式、AOP。

    一、工厂模式

    工厂,顾名思义就是批量生成产品的车间。

    工厂模式是指根据要求生成相同或相似的对象的设计模式。作用是统一管理相同接口的对象的实例化。具体来说,一个接口通常有很多实现类,如果创建这些实现类需要先做一些逻辑判断或初始化操作,这个时候就可以用工厂模式来统一创建。

    1.工厂模式的分类

    关于工厂模式的分类,有两种说法:

    1. 分成简单工厂模式和抽象工厂模式,比如:百度百科;
    2. 分成简单工厂模式、工厂方法模式和抽象工厂模式,比如:各大博客网站;

    个人比较倾向与第一张说法,理由有二:

    1. 工厂方法模式中工厂类使用的是抽象类,却一定要叫工厂方法模式,太过牵强;
    2. 工厂方法模式中只能生成一种对象,可是连简单工厂模式都能生产多种对象,工厂方法模式却不能,不太合理。

    下面,按照第一种说法来介绍工厂模式!

    2.简单工厂模式

    简单工厂模式就是只有一个工厂类的工厂模式。

    //工厂类
    public class Factory {
        public static Api create(int type){
            switch (type){
                case 1:
                    return new ImplA();
                case 2:
                    return new ImplB();
                default:
                    return new ImplA();
            }
        }
    }
    
    //产品接口
    public interface Api {
        void operator();
    }
    //具体的产品A
    public class ImplA implements Api {
        @Override
        public void operator() {
            System.out.println("完成了一种操作A");
        }
    }
    //具体的产品B
    public class ImplB implements Api {
        @Override
        public void operator() {
            System.out.println("完成了一种操作B");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            //通过简单工厂模式创造ImplB对象
            Api api = Factory.create(2);
            api.operator();
        }
    }
    

    3.抽象工厂模式

    抽象工厂模式就是工厂类抽象化的工厂模式。

    为什么要使用抽象工厂模式呢?当出现新的产品时,如果使用简单工厂模式,就需要修改原先的工厂类,这样就违反了Java的开闭原则。这个时候,就使用抽象工厂模式,创意一个新的工厂类来生产新的产品。

    //工厂类接口(抽象的工厂类)
    public interface IFactory {
       Api create(int type);
    }
    //新的工厂类
    public class Factory2 implements IFactory{
        public Api create(int type){
            switch (type){
                case 1:
                    return new ImplA();
                case 2:
                    return new ImplB();
                case 3:
                    return new ImplC();
                default:
                    return new ImplC();
            }
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            //通过抽象工厂模式创造ImplC对象
            Api api = new Factory2().create(3);
            api.operator();
        }
    }
    

    二、建造者模式

    建造者模式是用来生产复杂对象的设计模式。作用是将一个复杂对象的创建过程转化为多个简单的创建过程。

    //建造者:先分步设置临时对象的属性,然后创建实际的对象,并统一设置属性。
    public class WorkBuilder {
        private RoomParams params;
    
        public WorkBuilder() {
            this.params = new RoomParams();
        }
    
        public WorkBuilder makeWindow(String window) {
            params.window = window;
            return this;
        }
    
        public WorkBuilder makFloor(String floor) {
            params.floor = floor;
            return this;
        }
    
        public Room build() {
            Room room = new Room();
            room.apply(params);
            return room;
        }
    
        // 用来设置Room的属性
        class RoomParams {
            public String window;
            public String floor;
        }
    }
    //需要创建的复杂对象
    public class Room {
        private String window;
        private String floor;
    
        public void apply(WorkBuilder.RoomParams parmas) {
            window = parmas.window;
            floor = parmas.floor;
        }
    
        @Override
        public String toString() {
            return "Room{" + "window='" + window + '\'' + ", floor='" + floor + '\'' + '}';
        }
    }
    public class Test {
        public void test(){
            //使用建造者模式创建复杂对象
            Room room=new WorkBuilder().makeWindow("法式").makFloor("英式").build();
            System.out.println(room);
        }
    }
    

    三、单例模式

    单例模式是内存中只有一个实例的设计模式。作用有2点:

    1. 减小频繁创建与销毁对象带来的开销;
    2. 保证核心对象的唯一性。

    单例模式的分类:饿汉式、懒汉式、双重检查式、静态内部类。

    另外,可以通过Enum(枚举)来实现单例模式,不过Android一般不这么用。

    1.饿汉式

    饿汉式是在类加载的时候就创建对象。使用懒汉式创建对象时,无法传递参数。

    public class SingletonHungry {
        //持有私有静态实例,防止被引用,此处直接赋值
        private static SingletonHungry instance = new SingletonHungry();  
      
        // 私有构造方法,防止被实例化  
        private SingletonHungry() {  
        }  
      
        // 静态工程方法,创建实例  
        public static SingletonHungry getInstance() {  
            return instance;  
        }  
    }
    

    2.懒汉式

    懒汉式是在获取对象时创建对象。如果一个单例对象从来都不使用,这个时候使用饿汉式(类加载的时候就会创造对象)就会浪费内存,就需要使用懒汉式。

    public class SingletonLazy {
        //持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 
        private static SingletonLazy instance = null;  
      
        // 私有构造方法,防止被实例化  
        private SingletonLazy() {  
        }  
      
        // 静态工程方法,创建实例  
        public static SingletonLazy getInstance() {  
            if (instance == null) {  
                instance = new SingletonLazy();  
            }  
            return instance;  
        }  
    }
    

    3.双重检查式

    在多线程使用懒汉式,会出现安全问题。如果直接对getInstance方法加synchronized关键字,那么每次调用getInstance()时都会降低性能,所以需要使用双重检查式。

    双重检查式是在获取对象时,先判空,然后使用synchronized代码块来创建对象。synchronized代码块内部也需要判空,因为可能在外部判空之后,对象刚好创建好了。

    /**
     * 双重检查式
     */
    public class SingletonLazy2 {
        // 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载
        private static SingletonLazy2 instance = null;
    
        // 私有构造方法,防止被实例化
        private SingletonLazy2() {
        }
    
        // 静态工程方法,创建实例
        public static SingletonLazy2 getInstance() {
            if (instance == null) {
                synchronized (instance) {
                    if (instance == null) {
                        instance = new SingletonLazy2();
                    }
                }
            }
            return instance;
        }
    }
    

    使用双重检查式也存在隐患,看下面的情况:在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。但是由于JVM的指令重排机制,JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就可能出错了,我们以A、B两个线程为例:

    1. A、B线程同时进入了第一个if判断
    2. A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();
    3. 由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。
    4. B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。
    5. 此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。

    为了解决上面的问题,需要使用volatile关键字,来保证对象的可见性和有序性(禁止该对象的指令重排)。

    private static volatile SingletonLazy2 instance = null;
    

    4.静态内部类

    使用volatile关键字的双重检查式存在一个问题:性能差。

    所以使用静态内部类来实现,JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,这样我们就不用担心上面的问题。同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题。这样我们暂时总结一个完美的单例模式:

    public class Singleton {  
      
        /* 私有构造方法,防止被实例化 */  
        private Singleton() {  
        }  
      
        /* 此处使用一个内部类来维护单例 */  
        private static class SingletonFactory {  
            private static Singleton instance = new Singleton();  
        }  
      
        /* 获取实例 */  
        public static Singleton getInstance() {  
            return SingletonFactory.instance;  
        }  
    }  
    

    四、观察者模式

    观察者模式是一种订阅与通知的设计模式。作用是当对象变化时,统一通知其依赖对象。

    观察者模式,是一种被动的观察,类似于邮件订阅。观察者先订阅被观察者,当被观察者发生变化,就会通知观察者。

    //观察者接口
    public interface IObserver {
        public void update(); 
    }
    
    //观察者1
    public class Observer1 implements IObserver {
        public void update() {
            System.out.println("this is Observer1");
        }
    }
    
    //观察者接口2
    public class Observer2 implements IObserver {
        public void update() {
            System.out.println("this is Observer2");
        }
    }
    
    //被观察者接口
    public interface ISubject {  
        /*增加观察者*/  
        public void add(IObserver observer);  
          
        /*删除观察者*/  
        public void del(IObserver observer);  
          
        /*通知所有的观察者*/  
        public void notifyObservers();  
    } 
    
    //被观察者
    public  class Subject implements ISubject {  
        private Vector<IObserver> vector = new Vector<IObserver>();  
        
        @Override  
        public void add(IObserver observer) {  
            vector.add(observer);  
        }  
      
        @Override  
        public void del(IObserver observer) {  
            vector.remove(observer);  
        }  
      
        @Override  
        public void notifyObservers() {  
            Enumeration<IObserver> enumo = vector.elements();  
            while(enumo.hasMoreElements()){  
                enumo.nextElement().update();  
            }  
        }  
    } 
    
    public class Test {
        public static void main(String[] args) {
            ISubject sub = new Subject();
            //观察者订阅被观察者
            sub.add(new Observer1());
            sub.add(new Observer2());
            //被观察者通知观察者
            sub.notifyObservers();
        }
    }
    

    五、代理模式

    代理模式是通过代理对象对原对象进行扩展的设计模式。优点是不用直接修改原对象。

    代理模式分为两种:静态代理和动态代理。

    1.静态代理

    静态代理是代理对象和被代理对象要实现统一接口的代理模式。

    //代理接口
    public interface IUserDao {
        void save();
    }
    //被代理对象
    public class UserDao implements IUserDao {
        @Override
        public void save() {
            System.out.println("----已经保存数据!----");      
        }
    }
    //代理对象
    public class UserDaoProxy implements IUserDao {
    
        // 接收保存目标对象
        private IUserDao target;
    
        public UserDaoProxy(IUserDao target) {
            this.target = target;
        }
    
        public void save() {
            System.out.println("开始事务...");
            target.save();// 执行目标对象的方法
            System.out.println("提交事务...");
        }
    
    }
    public class Test {
        public static void main(String[] args) {
            // 被代理对象
            UserDao target = new UserDao();
            // 代理对象,把被代理对象传给代理对象,建立代理关系
            UserDaoProxy proxy = new UserDaoProxy(target);
            // 执行的是代理的方法
            proxy.save();
        }
    }
    

    2.动态代理

    动态代理是是通过反射实现对原对象扩展的代理模式。

    // 创建动态代理对象 动态代理不需要实现接口,但是需要指定接口类型
    public class ProxyFactory {
    
        // 维护一个被代理对象
        private Object target;
    
        public ProxyFactory(Object target) {
            this.target = target;
        }
    
        // 给被代理对象生成代理对象
        // Proxy.newProxyInstance()有3个参数,1是ClassLoader,用来创建对象;2是被代理对象的所有实现接口,用来给代理对象实现;3是InvocationHandler,用来对被代理的方法进行扩展
        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("开始事务2");
                            // 执行被代理对象方法
                            Object returnValue = method.invoke(target, args);
                            System.out.println("提交事务2");
                            return returnValue;
                        }
                    });
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            // 被代理对象
            IUserDao target = new UserDao();
            // 给被代理对象创建代理对象
            IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
            // 执行方法   【代理对象】
            proxy.save();
        }
    }
    

    六、AOP

    AOP(面向切面编程)是一种对多个类统一扩展的设计模式。AOP通过预编译方式和运行期动态代理实现程序功能的统一维护,优点是扩展性比代理模式更强,也更加灵活。

    AOP使用aspectjrt框架来实现。

    实现步骤

    1. 引入aspectjrt框架,将aspectjrt.jar添加到libs目录,并在gradle中添加引用:implementation files('libs/aspectjrt.jar');

    2. 配置gradle,使用aspectjrt作为类编译器,从而在编译修改class文件,实现动态扩展;

      buildscript {
          repositories {
              mavenCentral()
          }
          dependencies {
              classpath 'org.aspectj:aspectjtools:1.8.8'
              classpath 'org.aspectj:aspectjweaver:1.8.8'
          }
      }
      import org.aspectj.bridge.IMessage
      import org.aspectj.bridge.MessageHandler
      import org.aspectj.tools.ajc.Main
      
      final def log = project.logger
      final def variants = project.android.applicationVariants
      
      variants.all { variant ->
          if (!variant.buildType.isDebuggable()) {
              log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
              return;
          }
      
          JavaCompile javaCompile = variant.javaCompile
          javaCompile.doLast {
              String[] args = ["-showWeaveInfo",
                               "-1.8",
                               "-inpath", javaCompile.destinationDir.toString(),
                               "-aspectpath", javaCompile.classpath.asPath,
                               "-d", javaCompile.destinationDir.toString(),
                               "-classpath", javaCompile.classpath.asPath,
                               "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
              log.debug "ajc args: " + Arrays.toString(args)
      
              MessageHandler handler = new MessageHandler(true);
              new Main().run(args, handler);
              for (IMessage message : handler.getMessages(null, true)) {
                  switch (message.getKind()) {
                      case IMessage.ABORT:
                      case IMessage.ERROR:
                      case IMessage.FAIL:
                          log.error message.message, message.thrown
                          break;
                      case IMessage.WARNING:
                          log.warn message.message, message.thrown
                          break;
                      case IMessage.INFO:
                          log.info message.message, message.thrown
                          break;
                      case IMessage.DEBUG:
                          log.debug message.message, message.thrown
                          break;
                  }
              }
          }
      }
      
    3. 自定义注解

      @Target(ElementType.METHOD)
      @Retention(RetentionPolicy.RUNTIME)
      public @interface BehaviorTrace {
          String value();
      }
      
    4. 配置切面

      /**
       * 使用Aspect注解来定义切面,
       */
      @Aspect
      public class BehaviorTraceAspect {
          /**
           * 使用Pointcut注解来定义切入点,指定需要进行切面处理的方法
           * Pointcut的参数必须使用指定格式execution(@注释名   注释所在的类 注释所在的方法).*代表所有
           */
          @Pointcut("execution(@gsw.aop.annotation.BehaviorTrace * *(..))")
          public void methodAnnotatedWithBehaviorTrace() {
          }
      
          /**
           * 对切入点进行处理,可以使用三种方式注解(参数为上面使用Pointcut注解的方法):
           * Before注解:自动在切入点之前运行,不能调用ProceedingJoinPoint.process();
           * After注解: 自动在切入点之后运行,不能调用ProceedingJoinPoint.process();
           * Around注解:必须调用ProceedingJoinPoint.process()来执行切入点的方法,process()之前的代码在切入点之前执行,process()之后的代码在切入点之后执行;
           *
           * @param joinPoint 切入点
           */
          @Around("methodAnnotatedWithBehaviorTrace()")
          public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
              MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
              String className = methodSignature.getDeclaringType().getSimpleName();
              String methodName = methodSignature.getName();
              String funName = methodSignature.getMethod().getAnnotation(BehaviorTrace.class).value();
      
              //统计时间
              long begin = System.currentTimeMillis();
              //执行切入点的方法
              Object result = joinPoint.proceed();
              long duration = System.currentTimeMillis() - begin;
              Log.d("jett", String.format("功能:%s,%s类的%s方法执行了,用时%d ms", funName, className, methodName, duration));
              return result;
          }
      }
      
    5. 给需要扩展的方法添加自定义注解,然后编译运行即可。

      /**
      * 添加自定义注解,aspect框架会自动进行切入处理。可以添加多个注解。
      */
      @BehaviorTrace("摇一摇")
      public void mShake(View view) {
       SystemClock.sleep(new Random().nextInt(2000));
      }
      

      运行结果:功能:摇一摇,ActivityAOP类的mShake方法执行了,用时647 ms

    最后

    代码地址:https://gitee.com/yanhuo2008/Common/tree/master/app/src/main/java/gsw/common/aop

    移动架构专题:https://www.jianshu.com/nb/25128604

    喜欢请点赞,谢谢!

    相关文章

      网友评论

        本文标题:移动架构01-设计模式

        本文链接:https://www.haomeiwen.com/subject/ybzgrftx.html