美文网首页
Android源码设计模式解析与实战

Android源码设计模式解析与实战

作者: jtsky | 来源:发表于2019-08-16 11:47 被阅读0次

    第一章、六大原则

    1.单一职责原则(SRP)

    简单的说就是:一个类中应该是一组相关性很高的函数、数据的封装。两个不一样的功能不应该放在一个类中。

    这个原则没有具体的划分界限,需要根据个人经验,具体业务逻辑而定。这也是优化代码的第一步。试想一下,如果所有的功能写在一个类里,那么这个类会越来越大,越来越复杂,越不易修改维护。那么根据功能,各自独立拆分出来,岂不是逻辑会清晰些。

    2.开闭原则(OCP)

    定义:软件中的对象(类、模块、函数等)应该对于扩展是开放的,但是对于修改是封闭的。

    当软件需要变化时,我们应该尽量通过扩展的方式实现变化,而不是通过修改原有的代码来实现。因为直接的修改,可能会影响已有的正常代码。不利于出现错误时排除问题。当然实际开发中,修改原有代码与扩展代码是同时存在的。但应尽量以扩展为主。

    3.里氏替换原则(LSP)

    定义:所有引用父类的地方,必须能使用子类的对象。简单地说就是将父类替换为他的子类是不会出现问题,反之,未必可以。

    那么里氏替换原则就是依赖于面向对象语言的继承与多态。核心原理是抽象。

    这里列举一下继承的优缺点:

    优点:

    (1)代码重用,减少创建类的成本,每个子类都拥有父类的方法与属性。

    (2)子类与父类基本相似,但与父类又有所区别。

    (3)提高代码的可扩展性。

    缺点:

    (1)继承是侵入性的,只要继承就必须拥有父类所有的属性与方法。

    (2)可能造成子类代码冗余、灵活性降低。

    开闭原则和里氏替换原则是生死相依的、不离不弃的。他们都强调了抽象这一重要的特性。

    4.依赖倒置原则(DIP)

    定义:指代一种特定的解耦方式,使得高层次的模块不依赖于低层次的模块的实现细节的目的。他有一下几个关键点:

    (1)高层模块不依赖于低层模块,应该都依赖其抽象。

    (2)抽象不依赖细节。

    (3)细节应依赖抽象。

    解释:在Java中,抽象就是指接口或者抽象类,两者都是不能直接被实例化的;细节就是实现类,实现接口或者继承抽象类而产生的就是细节,也就是可以加上一个关键字new产生的对象。高层模块就是调用端,底层模块就是具体实现类。

    依赖倒置原则在Java中的表现就是:模块间通过抽象发生,实现类之间不发生直接依赖关系,其依赖关系是通过接口或者抽象类产生的。如果类与类直接依赖细节,那么就会直接耦合,那么当修改时,就会同时修改依赖者代码,这样限制了可扩展性。

    5.接口隔离原则(ISP)

    定义:类间的依赖关系应该建立在最小的接口上,将庞大、臃肿的接口拆分成更小的、更具体的接口。目的是系统的解耦,从而更容易重构、更改和重新部署。

    6.迪米特原则(LOD)

    定义:一个类应该对自己需要耦合或者调用的类知道的最少,类的内部如何实现与调用者或者依赖者没有关系,调用者或依赖者只需知道他需要的方法,其他可以一概不管。这样使得系统具有更低的耦合与更好的可扩展性。

    这六个原则,可以使我们在应用的后续升级、维护中更加方便、轻松应对。让我们的软件更加灵活。

    分类:

    创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

    结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

    行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。


    design.jpg

    第二章、单例模式

    单例模式应该是日常使用最为广泛的一种模式了。他的作用是确保某个类只有一个实例,避免产生多个对象消耗过多的资源。比如对数据库的操作时,就可以使用单例模式。

    1.各种单例

    (1)饿汉模式

    public class Singleton {  
         private static Singleton instance = new Singleton();  
         private Singleton (){
         }
         public static Singleton getInstance() {  
         return instance;  
         }  
     }
    

    这种写法是在类装载时就实例化instance,他避免了多线程的同步问题。但是不能保证有别的方式去装载,没有达到懒加载。

    (2)懒汉模式(线程不安全)

    public class Singleton {  
          private static Singleton instance;  
          private Singleton (){
          }   
          public static Singleton getInstance() {  
          if (instance == null) {  
              instance = new Singleton();  
          }  
          return instance;  
          }  
     }
    

    达到了懒加载,但是在多线程不能正常工作。

    (3)懒汉模式(线程安全)

    public class Singleton {  
          private static Singleton instance;  
          private Singleton (){
          }
          public static synchronized Singleton getInstance() {  
          if (instance == null) {  
              instance = new Singleton();  
          }  
          return instance;  
          }  
     } 
    

    这种写法能够在多线程中很好的工作,但是每次调用getInstance方法都会进行同步,反应稍慢,还会造成不必要的开销,所以者这种不建议使用。

    (4)DCL单例(双重检查锁定)

    public class Singleton {  
          private volatile static Singleton singleton;  
          private Singleton (){
          }   
          public static Singleton getSingleton() {  
          if (singleton == null) {  
              synchronized (Singleton.class) {  
              if (singleton == null) {  
                  singleton = new Singleton();  
              }  
             }  
         }  
         return singleton;  
         }  
     }  
    

    这种写法在getSingleton方法中对singleton进行了两次判空,第一次是为了不必要的同步,第二次是为了在null的情况下创建实例。我们会发现上面代码有一个volatile关键字,因为在这里会有DCL失效问题,原因是Java编译器允许处理器乱序执行。那么为了解决这个问题,在JDK1.5之后,具体化了volatile关键字,只要定义时加上他,可以保证执行的顺序,虽然会影响性能。这种方式第一次加载时会稍慢,在高并发环境会有缺陷,但是一般能够满足需求。

    (5)静态内部类单例模式

    public class Singleton {  
          private Singleton (){
          }
          public static final Singleton getInstance() {  
              return SingletonHolder.INSTANCE;  
          } 
          /**
          *静态内部类
          */
          private static class SingletonHolder {  
          private static final Singleton INSTANCE = new Singleton();  
          }   
      }
    

    这种是推荐使用的单例模式实现方式。当第一次加载Singleton类时并不会初始化INSTANCE,只有在第一次调用getInstance方法时才会导致INSTANCE被初始化。这种方式不仅能够保证线程安全,也能保证单例对象的唯一性,同时也延长了单例的实例化。

    (6)枚举单例

    public enum Singleton {  
         INSTANCE;  
         public void whateverMethod() {  
         }  
     }  
    

    这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。

    (7)使用容器实现单例模式

    public class SingletonManager { 
      private static Map<String, Object> objMap = new HashMap<String,Object>();
    
      private Singleton() { }
      public static void registerService(String key, Objectinstance) {
        if (!objMap.containsKey(key) ) {
          objMap.put(key, instance) ;
        }
      }
    
      public static ObjectgetService(String key) {
        return objMap.get(key) ;
      }
    }
    

    将多种单例类型注入到一个统一的管理类中,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。

    2.Android源码中的单例模式

    Android系统中,我们经常会通过Context获取系统级别的服务,如WindowsManagerService、ActivityManagerService等,更常用的是一个LayoutInflater的类,这些服务会在合适的时候以单例的形式注册在系统中,在我们需要的时候就通过Context的getSystemService(String name)获取。

    3.总结

    优点:

    (1)由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁的创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。

    (2)单例模式可以避免对资源的多重占用,例如一个文件操作,由于只有一个实例存在内存中,避免对同一资源文件的同时操作。

    (3)单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如,可以设计一个单例类,负责所有数据表的映射处理。

    缺点:

    (1)单例模式一般没有接口,扩展很困难,若要扩展,只能修改代码来实现。

    (2)单例对象如果持有Context,那么很容易引发内存泄露。此时需要注意传递给单例对象的Context最好是Application Context。

    第三章、Builder模式

    1.定义

    将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

    2.使用场景

    (1)相同的方法,不同的执行顺序,产生不同的事件结果时。

    (2)多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时。

    (3)产品类非常复杂,或者产品类中的调用顺序不同产生了不同的作用,这个使用建造者模式非常适合。

    (4)当初始化一个对象特别复杂时,如参数多,且很多参数有默认值。

    3.简单实现

    public interface Builder {
    
      //创建部件A  比如创建汽车车轮
      void buildPartA(); 
      //创建部件B   比如创建汽车方向盘
      void buildPartB(); 
      //创建部件C   比如创建汽车发动机
      void buildPartC();
      //返回最后组装成品结果 (返回最后装配好的汽车)
      Product getResult();
    
    }
    //Director 类,负责制造
    public class Director {
    
      private Builder builder;
    
      public Director( Builder builder ) { 
        this.builder = builder; 
      } 
      // 将部件partA partB partC最后组成复杂对象
      //这里是将车轮 方向盘和发动机组装成汽车的过程
      public void construct() { 
        builder.buildPartA();
        builder.buildPartB();
        builder.buildPartC();
    
      }
    
    }
    
    public class ConcreteBuilder implements Builder {
    
      Part partA, partB, partC; 
      public void buildPartA() {
        //这里是具体如何构建partA的代码
      }; 
      public void buildPartB() { 
        //这里是具体如何构建partB的代码
      }; 
       public void buildPartC() { 
        //这里是具体如何构建partB的代码
      }; 
       public Product getResult() { 
        //返回最后组装成品结果
      }; 
    }
    
    public interface Product { }//产品
    
    public interface Part { }//部件
    
    //调用
    ConcreteBuilder builder = new ConcreteBuilder();
    Director director = new Director( builder ); 
    
    director.construct(); 
    Product product = builder.getResult();
    

    从上面可以看到由于Director封装了构建复杂的产品对象过程,对外隐藏了细节。

    现实开发中,Director一般被省略。而直接使用一个Builder来进行对象的组装,这个Builder通常为链式调用,他是将setter方法返回自身。代码大致如下:

    new TestBuilder().setA("A").setB("B").create();
    

    4.Android源码中的Builder模式

    1.AlertDialog.Builder

    源码太长截取部分:

    public static class Builder {
            private final AlertController.AlertParams P;
            private int mTheme;
    
            /**
             * Constructor using a context for this builder and the {@link AlertDialog} it creates.
             */
            public Builder(Context context) {
                this(context, resolveDialogTheme(context, 0));
            }
    
            /**
             * Constructor using a context and theme for this builder and
             * the {@link AlertDialog} it creates.  The actual theme
             * that an AlertDialog uses is a private implementation, however you can
             * here supply either the name of an attribute in the theme from which
             * to get the dialog's style (such as {@link android.R.attr#alertDialogTheme}
             * or one of the constants
             * {@link AlertDialog#THEME_TRADITIONAL AlertDialog.THEME_TRADITIONAL},
             * {@link AlertDialog#THEME_HOLO_DARK AlertDialog.THEME_HOLO_DARK}, or
             * {@link AlertDialog#THEME_HOLO_LIGHT AlertDialog.THEME_HOLO_LIGHT}.
             */
            public Builder(Context context, int theme) {
                P = new AlertController.AlertParams(new ContextThemeWrapper(
                        context, resolveDialogTheme(context, theme)));
                mTheme = theme;
            }
    
            /**
             * Returns a {@link Context} with the appropriate theme for dialogs created by this Builder.
             * Applications should use this Context for obtaining LayoutInflaters for inflating views
             * that will be used in the resulting dialogs, as it will cause views to be inflated with
             * the correct theme.
             *
             * @return A Context for built Dialogs.
             */
            public Context getContext() {
                return P.mContext;
            }
    
            /**
             * Set the title displayed in the {@link Dialog}.
             *
             * @return This Builder object to allow for chaining of calls to set methods
             */
            public Builder setTitle(CharSequence title) {
                P.mTitle = title;
                return this;//**这里返回自身,类似的来设置各种参数。
            }
           // ......省略
              /**
             * Creates a {@link AlertDialog} with the arguments supplied to this builder. It does not
             * {@link Dialog#show()} the dialog. This allows the user to do any extra processing
             * before displaying the dialog. Use {@link #show()} if you don't have any other processing
             * to do and want this to be created and displayed.
             */
            public AlertDialog create() {
                final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false);
                P.apply(dialog.mAlert);//将P中的参数应用到dialog中的mAlert对象中
                dialog.setCancelable(P.mCancelable);
                if (P.mCancelable) {
                    dialog.setCanceledOnTouchOutside(true);
                }
                dialog.setOnCancelListener(P.mOnCancelListener);
                dialog.setOnDismissListener(P.mOnDismissListener);
                if (P.mOnKeyListener != null) {
                    dialog.setOnKeyListener(P.mOnKeyListener);
                }
                return dialog;
            }
    
            /**
             * Creates a {@link AlertDialog} with the arguments supplied to this builder and
             * {@link Dialog#show()}'s the dialog.
             */
            public AlertDialog show() {
                AlertDialog dialog = create();
                dialog.show();
                return dialog;
            }
        }
    

    上面有个AlertController.AlertParams 的成员参数P,我们在Builder设置的title,icon等都储存在他里面。在调用create时在P.apply使用。

    5.总结

    优点:

    (1)良好的封装性,使用建造者模式可以使客户端不必知道产品内部组成细节。

    (2)建造者独立,容易扩展。

    缺点:

    (1)会产生多余的Builder对象及Director对象,消耗内存。

    第四章、原型模式

    1、定义

    用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。被复制的实例就是“原型”,这个原型是可定制的。

    2、使用场景

    (1)类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等,通过原型拷贝避免这些消耗。

    (2)通过new产生的一个对象需要非常繁琐的数据准备或者权限,这时可以使用原型模式。

    (3)一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。

    第五章、工厂方法模式

    1.定义

    定义一个用于创建对象的接口,让子类决定实例化那个类。

    2.使用场景

    在任何需生成复杂对象的地方,都可以使用工厂方法模式。复杂对象适合使用工厂模式,用new就可以完成创建的对象无需使用工厂模式。

    第六章、抽象工厂模式

    1.定义

    为创建一组相关或者是相互依赖的对象提供一个接口,而不需要指定他们的具体实现类。

    2.使用场景

    一个对象族(或是一组没有任何关系的对象)都有相同的约束,则可以使用抽象工厂模式。例如一个文本编辑器和一个图片处理器,都是软件实体,但是Linix下的文本编辑器和WINDOWS下的文本编辑器虽然功能和界面都相同,但是代码实现是不同的,图片处理器也是类似情况,也就是具有了共同的约束条件:操作系统类型,于是我们可以使用抽象工厂模式,产生不同操作系统下的编辑器和图片处理器。

    第七章、策略模式

    通常如果一个问题有多个解决方案时,最简单的就是利用if-else或者switch-case方式根据不同的情景选择不同的解决方案,但是这样耦合性太高 、代码臃肿、难以维护等。这时就可以使用策略模式来解决。

    1.定义

    策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。

    2.使用场景

    1.针对同一类型问题的多种处理方式,仅仅是具体行为有差别时。

    2.需要安全地封装多种同一类型的操作时。

    3.出现同一抽象类有多个子类,而又需要使用if-else或者switch-case来选择具体子类时。

    第八章、状态模式

    1.定义

    状态模式中的行为是由状态来决定,不同的状态下有不同的行为。当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

    2.使用场景

    1.一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为。

    2.代码中包含大量与对象状态有关的条件语句,例如,一个操作中含有大量的多分支语句,且这些分支依赖于该对象的状态。

    第九章、责任链模式

    1.定义

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

    2.使用场景

    1.多个对象可以处理同一请求,但具体由哪个对象处理则在运行时动态决定。

    2.在请求处理者不明确的情况下向多个对象中的一个提交请求。

    3.需要动态指定一组对象处理请求。

    第十章、解释器模式

    解释器模式是一种用的比较少的行为型模式,其提供了一种解释语言的语法或表达式的方式。但是它的使用场景确实很广泛,只是因为我们自己很少回去构造一个语言的文法,所以使用较少。

    1.定义

    给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。(其中语言就是我们需要解释的对象,文法就是这个语言的规律,解释器就是翻译机,通过文法来翻译语言。)

    2.使用场景

    1.如果某个简单的语言需要解释执行而且可以将该语言中的语句表示为一个抽象的语法树时可以考虑使用解释器模式。

    2.在某些特定的领域出现不断重复的问题时,可以将该领域的问题转化为一种语法规则下的语句,然后构建解释器来解释该语句。

    第十一章、命令模式

    命令模式是行为型模式之一。总体来说并不难理解,只是比较繁琐,他会将简单的调用关系解耦成多个部分,增加类的复杂度,但是即便如此,命令模式的结构依然清晰。

    1.定义

    将一个请求封装成一个对象,从而让用户使用不同的请求把客户端参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。

    2.使用场景

    (1)需要抽出待执行的动作,然后以参数的形式提供出来。

    (2)在不同的时刻指定、排列和执行请求。一个命令对象可以有与初始请求无关的生存期。

    (3)需要支持操作取消。

    (4)支持修改日志功能,这样当系统崩溃时,这些修改可以被重做一遍。

    (5)需要支持事务操作。

    第十二章、观察者模式

    观察者模式是一个使用率非常高的模式,它最常用在GUI系统、订阅–发布系统。因为这个模式的一个重要作用就是解耦,将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。比如安卓的开源项目EventBus、Otto、AndroidEventBus等事件总线类的和RxJava响应式编程其核心都是使用观察者模式。

    1.定义

    观察者模式是一种行为类模式,它定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

    2.使用场景

    (1)关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系。

    (2)事件多级触发场景。

    (3)跨系统的消息交换场景,如消息队列、事件总线的处理机制。

    第十三章、备忘录模式

    备忘录模式是一种行为模式,该模式用于保存对象当前的状态,并且在之后可以再次恢复到此状态,有点像是我们平常说的”后悔药”。

    1.定义

    在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样,以后就可将该对象恢复到原先保存的状态。

    2.使用场景

    (1)需要保存一个对象在某一个时刻的状态或部分状态。

    (2)如果用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性,一个对象不希望外界直接访问其内部状态,通过中间对象可以间接访问其内部状态。

    第十四章、迭代器模式

    迭代器模式,又叫做游标模式,是行为型设计模式之一。我们知道对容器对象的访问必然会涉及遍历算法,我们可以将遍历的方法封装在容器中,或者不提供遍历方法,让使用容器的人自己去实现去吧。这两种情况好像都能够解决问题。

    然而在前一种情况,容器承受了过多的功能,它不仅要负责自己“容器”内的元素维护(添加、删除等等),而且还要提供遍历自身的接口;而且由于遍历状态保存的问题,不能对同一个容器对象同时进行多个遍历。第二种方式倒是省事,却又将容器的内部细节暴露无遗。

    正因于此,迭代器模式应运而生,在客户访问类与容器体之间插入一个第三者–迭代器,很好的解决了上述弊端。

    1.定义

    提供一种方法顺序访问一个容器对象中的各个元素,而又不需要暴露该对象的内部表示。

    2.使用场景

    遍历一个容器对象时。

    第十五章、模板方法模式

    模板方法模式是结构最简单的行为型设计模式,也是所有模式中最为常见的几个模式之一,是基于继承的代码复用的基本技术。在其结构中只存在父类与子类之间的继承关系。

    1.定义

    定义一个操作中的算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

    2.模板方法模式中的方法

    1.模板方法

    一个模板方法是定义在抽象类中的,把基本操作方法组合在一起形成一个总算法或一个总行为的方法。这个模板方法定义在抽象类中,并由子类不加以修改地完全继承下来。所以模板方法大多会定义为final类型,指明主要的逻辑功能在子类中不能被重写。模板方法是一个具体方法,它给出了一个顶层逻辑框架,而逻辑的组成步骤在抽象类中可以是具体方法,也可以是抽象方法。由于模板方法是具体方法,因此模板方法模式中的抽象层只能是抽象类,而不是接口。

    2.基本方法

    (1)抽象方法:一个抽象方法由抽象类声明,由具体子类实现。在Java语言里抽象方法以abstract关键字标示。

    (2)钩子方法:一个钩子方法由抽象类声明并实现,而子类会加以扩展。子类可以通过扩展钩子方法来影响模板方法的逻辑。

    3.使用场景

    (1)多个子类有公有的方法,并且逻辑基本相同。

    (2)重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能由各个子类实现。

    (3)重构时,模板方法是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子方法约束其行为。

    第十六章、访问者模式

    访问者模式是一种行为型模式,它是23种设计模式中最复杂的一个,虽然使用频率不高,但是并不代表可以忽略,在合适的地方,它会带来意想不到的灵活性。访问者模式,顾名思义使用了这个模式后就可以在不修改已有程序结构的前提下,通过添加额外的“访问者”来完成对已有代码功能的提升。

    1.定义

    封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。

    2.使用场景

    (1)对象结构比较稳定,但经常需要在此对象结构上定义新的操作。

    (2)需要对一个对象结构中的对象进行很多不同的且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。

    3.UML类图


    04133344530.jpeg

    (1)Visitor:接口或者抽象类,它定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法数理论上来讲与元素个数是一样的,因此,访问者模式要求元素的类族要稳定,如果经常添加、移除元素类,必然会导致频繁地修改Visitor接口,如果这样则不适合使用访问者模式。

    (2)ConcreteVisitor1、ConcreteVisitor2:具体的访问类,它需要给出对每一个元素类访问时所产生的具体行为。

    (3)Element:元素接口或者抽象类,它定义了一个接受访问者的方法(Accept),其意义是指每一个元素都要可以被访问者访问。

    (4)ConcreteElementA、ConcreteElementB:具体的元素类,它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。

    (5)ObjectStructure:定义当中所说的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素供访问者访问。

    第十七章、中介者模式

    中介者模式也称为调解者模式或调停者模式,是一种行为型模式。

    1.定义

    中介者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使它们可以松散耦合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。

    2.使用场景

    当对象之间的交互操作很多且每个对象的行为操作都依赖彼此时,为防止在修改一个对象的行为时,同时涉及很多其他对象的行为,可使用中介者模式。

    3.UML类图


    05161950662.jpeg

    (1)Mediator:抽象中介者角色,定义了同事对象到中介者对象的接口,一般以抽象类的方式实现。

    (2)ConcreteMediator:具体中介者角色,继承于抽象中介者,实现了父类定义的方法,它从具体的同事对象接受消息,向具体同事对象发出命令。

    (3)Colleague:抽象同事类角色,定义了中介者对象的接口,它只知道中介者而不知道其他的同事对象。

    (4)ConcreteColleague1、ConcreteColleague2:具体同事类角色,继承于抽象同事类,每个具体同事类都知道本身在小范围的行为,而不知道在大范围内的目的。

    第十八章、代理模式

    代理模式也称委托模式,是结构型设计模式之一。是应用广泛的模式之一。

    1.定义

    为其他对象提供一种代理以控制对这个对象的访问。

    2.使用场景

    当无法或不想直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口。

    3.UML类图

    08101259761.jpeg

    (1)Subject:抽象主题类,声明真实主题与共同接口方法,该类可以是抽象类或接口。

    (2)RealSubject:真实主题类(被委托类),尤其执行具体的业务逻辑方法。

    (3)Proxy:代理类(委托类),该类持有一个对真实主题类的引用,在其所实现的接口方法中调用真实主题类中相应的接口方法执行,以此起到代理作用。

    第十九章、组合模式

    组合模式也称为部分-整体模式,结构型设计模式之一。

    1.定义

    将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

    2.使用场景

    (1)表示对象的部分-整体层次结构时。

    (2)从一个整体中能够独立出部分模块或功能的场景。

    3.UML类图

    11093252993.jpeg

    (1)Component:抽象根节点,为组合中的对象声明接口。在适当的情况下,实现所有类共有接口的缺省行为。声明一个接口用于访问和管理Component的子节点。可在递归结构中定义一个接口,用于访问一个父节点,并在合适的情况下实现它。

    (2)Composite:定义有子节点的那些枝干节点行为,存储子节点,在Component接口中实现与子节点有关的操作。

    (3)Leaf:在组合中表示叶子节点对象,叶子节点没有子节点,在组合中定义节点对象的行为。

    (4)Client:通过Component接口操纵组合节点的对象。

    如图这种将组合所使用的方法全部定义在抽象类的方式称为透明的组合模式,如果将Component中的Add、Remove、GetChild去除,只在Composite中单独添加,这种方式称为安全的组合模式。然而后者违背了依赖倒置原则。

    第二十章、适配器模式

    适配器模式是结构型设计模式之一,它在我们的开发中使用率极高,比如ListView、GridView以及RecyclerView都需要使用Adapter。

    1.定义

    适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配无法在一起工作的两个类可以在一起工作。

    2.使用场景

    (1)系统需要使用现有的类,但此类的接口不符合系统的需要,即接口不兼容。

    (2)想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。

    (3)需要一个统一的输出接口,而输入端的类型不可预知。

    3.UML类图

    适配器模式分为两种,即类适配器模式与对象适配器模式。以下是类适配器的UML图。

    11154535015.jpeg

    (1)Target:目标角色,也就是所期待得到的接口。注意:由于这里讨论的是类适配器模式,因此目标不可以是类。

    (2)Adaptee:现在需要适配的接口。

    (3)Adapter:适配器角色,也就是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。

    第二十一章、装饰模式

    装饰模式也称为包装模式,是结构型设计模式之一。装饰模式是一种用于替代继承技术的一种方案。

    1.定义

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

    2.使用场景

    (1)需要透明且动态地扩展类的功能时。且在不影响其他对象的情况下。

    (2)当不能采用继承对系统进行扩展时可以使用装饰模式。比如final类。

    3.UML类图

    14102620209.jpeg

    (1)Component:抽象组件。可以是一个接口或抽象类,其充当的就是被装饰的原始对象。

    (2)ConcreteComponent:组件具体实现类,该类是Component类的基本实现,也是我们装饰的具体对象。

    (3)Decorator:抽象装饰者,其职责就是装饰我们的组件对象,通过其子类扩展该方法以达到装饰的目的。其内部一定要有一个指向组件对象的引用。在大多数情况下,该类为抽象类,需要根据不同的装饰逻辑实现不同的具体子类。

    (4)ConcreteDecoratorA、ConcreteDecoratorB:装饰着具体实现类。负责向构件添加新的职责。

    第二十二章、享元模式

    享元模式是结构型设计模式之一,是对对象池的一种实现。就像它的名字一样,共享对象,避免重复的创建。我们常用的String 就是使用了共享模式,所以String类型的对象创建后就不可改变,如果当两个String对象所包含的内容相同时,JVM只创建一个String对象对应这两个不同的对象引用。

    1.定义

    采用一个共享来避免大量拥有相同内容对象的开销。使用享元模式可有效支持大量的细粒度对象。

    2.使用场景

    (1)系统中存在大量的相似对象。

    (2)细粒度的对象都具备较接近的外部状态,而且内部状态与环境不关,也就是说对象没有特定身份。

    (3)需要缓冲池的场景。

    PS:内部状态与外部状态:在享元对象内部并且不会随着环境改变而改变的共享部分,可以称之为享元对象的内部状态,反之随着环境改变而改变的,不可共享的状态称之为外部状态。

    3.UML类图

    15100651309.jpeg

    享元模式分为单纯享元模式和复合享元模式,上图是复合享元模式。

    (1)Flyweight:享元对象抽象基类或者接口。

    (2)ConcreateFlyweight:具体的享元对象,如果有内部状态的话,必须负责为内部状态提供存储空间。

    (3)UnsharadConcreateFlyweight:复合享元角色所代表的对象是不可以共享的,并且可以分解成为多个单纯享元对象的组合。单纯享元模式没有此项,这也是两者在结构上的区别。

    (4)FlyweightFactoiy:享元工厂,负责管理享元对象池和创建享元对象。

    (5)Client:维护对所有享元对象的引用,而且还需要存储对应的外蕴状态。

    第二十三章、外观模式

    外观模式是结构型设计模式之一,它在开发中的运用频率非常高,是我们封装API的常用手段。我们经常使用的三方SDK基本都使用的外观模式,这样可以对用户屏蔽很多实现细节,降低用户使用成本。

    1.定义

    要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。外观模式提供一个高层次的接口,使得子系统更易于使用。

    2.使用场景

    (1)为复杂子系统提供一个简单接口,对外隐藏子系统的具体实现、隔离变化。

    (2)当你需要构建一个层次结构的子系统时,使用外观模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,你可以让它们仅通过外观接口进行通信,从而简化了它们之间的依赖关系。

    3.UML类图

    18105550026.jpeg

    (1)Facade:系统对外的统一接口,系统内部系统地工作。

    (2)其他分支:子系统接口。

    可以看出外观模式结构很简单,但是如果没有封装,那么用户就要操作几个子系统的交互逻辑,容易出现错误。

    第二十四章、桥接模式

    桥接模式也称为桥梁模式,是结构型设计模式之一。桥接模式中体现了“单一职责原则”、“开闭原则”、“里氏替换原则”、“依赖倒置原则”等。同时它也是很实用的一种模式。

    1.定义

    将抽象部分与现实部分分离,使它们都可以独立地进行变化。

    2.使用场景

    (1)如果一个系统需要在构建的抽象化角色和具体角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系。

    (2)对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,也可以考虑使用桥接模式。

    (3)一个类存在两个独立变化的维度,且这两个维度都需要扩展。

    3.UML类图

    19155426472.jpeg

    (1)Abstraction:抽象部分,该类保持一个对实现部分对象的引用,抽象部分中的方法需要调用实现部分的对象来实现,该类一般为抽象类。

    (2)RefinedAbstraction:优化抽象部分,抽象部分的具体实现,该类一般是对抽象部分的方法进行完善和扩展。

    (3)Implementor:实现部分。可以为接口或抽象类,其方法不一定要与抽象部分中的一致,一般情况下是由现实部分提供基本操作,而抽象部分定义的则是基于实现部分这些基本操作的业务方法。

    (4)ConcreteImplementorA、ConcreteImplementorB:实现部分的具体实现。完善实现部分中的方法定义的具体逻辑。

    相关文章

      网友评论

          本文标题:Android源码设计模式解析与实战

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