22.《Android源码设计模式解析与实战》笔记

一、面向对象的六大原则
单一职责Single Responsibility Principle,缩写SRP。定义:对一个类而言,应该仅有一个引起它变化的原因。简单来说,一个类中应该是一组相关性很高的函数。

开闭原则Open Close Principle,缩写OCP。定义:软件中的对象(类、模块、函数等)应该对于扩展是开放的,对于修改是封装的。

里氏替换Liskov Substitution Principle,缩写LSP。定义:所有引用基类的地方必须能透明地使用其子类的对象。

依赖倒置Dependence Inversion Principle,缩写DIP。定义:模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的。

接口隔离原则Interface Segregation Principle,缩写ISP。定义:客户端不应该依赖它不需要的接口。另一种定义是:类间的依赖关系应该建立在最小的接口上。

迪米特原则Law of Demeter,缩写LOD,也称最少知识原则(Least Knowledge Principle)。定义:一个对象应该对其他对象有最少的了解。

  • 设计模式分类:
    • 创建型:Singleton、Builder、Prototype、Factory Method、Abstract Factory
    • 结构型:Proxy、Composite、Adapter、Decorator、Flyweight、Facade、Bridge
    • 行为:Strategy、State、Chain of Responsibility、Interpreter、Command、Observer、Memento、Iterator、Template Method、Visitor、Mediator

二、应用最广的模式——单例模式
定义:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

使用场景:比如创建一个对象需要消耗的资源过多。

关键点:构造函数不对外开放,一般为Private;通过一个静态方法或者枚举返回单例类对象;确保单例类的对象有且只有一个,尤其是在多线程环境下;确保单例类对象在反序列化时不会重新构建对象。

简单示例:公司人员构成,单个CEO

实现方式
懒汉模式:声明一个静态对象,并且在用户第一次调用getInstance时进行初始化,而上述饿汉模式(CEO类)是在声明麦太太对象时就已经初始化。
Double Check Lock(DCL)实现单例:

      public static Singleton getInstance() {
        if (mInstance == null) {
          synchronized  (Singleton.class) {
            if (mInstance == null) {
              sInstance = new Singleton();
            }
          }
        }
        return sInstance;
      }

静态内部类单例模式:

      public static Singleton getInstance() {
        return SingletonHolder.sInstance;
      }
      private static class SingletonHolder{
        priveate static final Singleton sInstance = new Singleton();
      }

枚举单例:

public enum SingletonEnum {
        INSTANCE;
        public void doSomething() {
          System.out.println("do sth.");
        }
      }

使用容器实现单例模式:

 public class SingletonManager {
        prinvate static Map<String,Object> objMap = new HashMap<String,Object>();
      }

在Android源码中:通常我们使用LayoutInflater.from(Context)来获取LayoutInflater服务,下面看看LayoutInflater.from(Context)的实现。

public static LayoutInflater from(Context context) {
  LayoutInflater layoutInflater = (LayoutInflater)context.getSystemServer(Context.LAYOUT_INFLATER_SERVICE);
  if(layoutInflater == null) {
    throw new AssertonError("LayoutInflater not found."):
  }
  return layoutInflater;
}

深入理解LayoutInflater

  • 小结
    • 优点:减少内存开支;减少系统的性能开销;避免对资源的多重占用;在系统设置全局的访问点,优化和共享资源访问。
    • 缺点:扩展困难;单例对象如果持有Context,容易引发内存泄漏,传递给单例对象的Context最好是Application Context。

三、自由扩展你的项目——Builder模式
介绍:Builder模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程。该模式是为了将构建复杂对象的过程和它的部件解耦,使得构建过程和部件的表示隔离开来。

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

使用场景:相同的方法,不同的执行顺序,产生不同的事件结果时;多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时;产品类非常复杂,或者产品类中的调用顺序不同产生了不同的什么用,这个时候使用建造者模式非常合适;当初始化一个对象特别复杂,如参数多,且很多参数都具有默认值时。

UML类图
Product——产品的抽象类。
Builder——抽象Builder类,规范产品的组建,一般是由子类实现具体的邪恶v地方过程;
ConcreteBuilder——具体的Builder类;
Director——统一组装的过程。

简单实现
计算机的组装过程

在Android源码中
在Android源码中,最常用到的Builder模式就是AlertDialog.Builder,使用该Builder来构建复杂的AlertDialog对象。

public class AlertDialog extends Dialog implements DialogInterface{
  private AlertContraller mAlert;
  public void setTitle(char Sequence title) { super...;mAlert.setTitle(title);}
  public static class Builder{
    private final AlertController.AlertParams p;
    public Builder setTitle(...) {
      p.mTitle = title; return this;
    }
    public AlertDialog create() {
      final AlertDialog dialog = new AlertDialog(p.mContext,mTheme,false);
      p.apply(dialog.mAlert);
      return dialog;
      }
  }
}

深入了解WindowManager

实战

小结
优点:良好的封装性;建造者独立,容易扩展
缺点:会产生多余的Builder对象以及Director对象,消耗内存。

四、使程序运行更高效——原型模式
介绍:多用于创建复杂的或者构造耗时的实例,在这种情况下,复制一个已经存在的实例可使程序运行更高效

定义:用原型实例指定创建对象的种类,并通过复制这些原型创建新的对象。

使用场景:1.类初始化需要消耗非常多的资源,通过原型复制避免这些消耗。2.通过new产生一个对象需要非常繁琐的数据准备或访问权限,这时可以使用原型模式。3.一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式复制多个对象供调用者使用,即保护性拷贝。

UML类图
Client:客户端用户。
Prototype:抽象类或者接口,声明具备clone能力。
ConcretePrototype:具体的原型类

简单实现:文档拷贝

浅拷贝和深拷贝:深拷贝对引用型的字段也采用拷贝的形式(也用上clone函数嘛)。

在Android源码中:Intent

Intent的查找与匹配

实战

小结
优点:原型模式是在内存中二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环内产生大量的对象时,原型模式可以更好地体现其优点。
缺点:减少了约束。

五、应用最广泛的模式——工厂方法模式
定义:定义一个用于创建对象的接口,让子类决定实例化哪个类。

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

UML类图
Product:定义工厂方法所创建的对象的接口
ConcreteProduct:实现Product接口
Creator:声明工厂方法,该方法返回一个Product类型的对象;调用工厂方法以创建一个Product对象
ConcreteCreator:重定义工厂方法以返回一个ConcreteProduct实例

简单实现:组装汽车

    public <T extends AudiCar> T createAudiCar(Class<T> clz) {
      AudiCar car = null;
      try {
        car = (AudiCar) Class.forName(clz.getName()).newInstance();
      } catch (Exception e) {
        e.printStackTrace();
      }
      return (T) car;
    }

在Android源码中:iterator

关于onCreate方法:对于一个app来说,入口在ActivityThread类中。这是一个final类,一个app对应一个ActivityThread,当Zygote进程孵化出一个新的进程后,会执行ActivityThread的main方法,在main方法中做一些比较常规的逻辑,比如准备Looper和消息队列,然后调用attach方法将其绑定到ActivityManagerService中。

实战

小结
缺点:导致类结构复杂化

六、创建型设计模式——抽象工厂模式
定义:为创建一组相关或者是相互依赖的对象提供一个接口,而不需要指定它们的具体类。

使用场景:一个对象族有相同的约束时可以使用抽象工厂模式。

UML类图
AbstractFactory:抽象工厂角色,它声明了一组用于创建一种产品的方法,每一个方法对应一种产品。
ConcreteFactory:具体工厂角色,它实现了在抽象工厂中定义的创建产品的方法,生成一组具体产品,这些产品构成了一个产品种类,每一个产品都位于某个产品等级结构中。
AbstractProduct:抽象产品种类,它为每种产品声明接口。
ConcreteProduct:具体产品角色,它定义具体工厂生产的具体产品对象,实现抽象产品接口中声明的业务方法。

简单实现:汽车工厂生产部件

在Android源码中:Android底层对MediaPlayer的创建。

在Android开发中的应用:主题树

小结
优点:分离接口与实现
缺点:类文件的爆炸性增加;不太容易扩展新的产品类

七、时势造英雄——策略模式
定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以互相替换。

使用场景
针对同一类型问题的多种处理方法,仅仅是具体行为有差别时
需要安全地封装多种同一类型的操作时
出现同一抽象类有多个子类,而又需要使用if-else或者switch-case来选择具体子类时

UML类图
Context——用来操作策略的上下文环境
Stragety——策略的抽象
ConcreteStragetyA、ConcreteStragetyB——具体的策略实现

简单实现:公共交通费用计算

在Android源码中:时间插值器

深入属性动画

实战

小结
优点:结构清晰明了、使用简单直观;耦合度相对而言较低,扩展方便;操作封装也更为彻底、数据更为安全
缺点:随着策略的增加,子类也会变得繁多。

八、随遇而安——状态模式
定义:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

使用场景:一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为;代码中包含大量与对象状态有关的条件语句,例如,一个操作中含有庞大的多分支语句,且这些分支依赖于该对象的状态;

UML类图
Context:环境类,定义客户感兴趣的接口,维护一个State子类的实例,这个实例定义了对象的当前状态。
State:抽象状态类或者状态接口,定义一个或者一组接口,表示该状态下的行为。
ConcreteStateA、ConcreteStateB:具体状态类,每一个具体的状态类实现抽象State中定义的接口,从而达到不同状态下的不同行为。

简单示例:电视遥控器

状态模式:WIFI管理

实战:登录系统

小结
优点:State模式将所有与一个特定的状态相关的行为都放入一个状态对象中,它提供了一个更好的方法来组织与特定状态相关的代码,将烦琐的状态判断转换成结构清晰的状态类族,在避免代码膨胀的同时也保证了可扩展性与可维护性。
缺点:增加系统类和对象的个数。

九、使编程更有灵活性——责任链模式
定义:使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。

使用场景:多个对象可以处理同一请求,但具体由哪个对象处理则在运行时动态决定;在请求处理者不明确的情况下向多个对象中的一个提交一个请求;需要动态指定一组对象处理请求。

UML类图
Handler:抽象处理者角色,声明一个请求处理的方法,并在其中保持一个对下一个处理节点Handler对象的引用。
ConcreteHandler:具体处理者角色,对请求进行处理,如果不能处理则将该请求转发给下一个节点上的处理对象。

简单实现:报销费用

在Android源码中:ViewGroup中将事件派发到子View

实战

小结
优点:对请求者和处理者关系解耦,提高代码的灵活性。
缺点:对链中请求处理者的遍历太多,会影响性能。

十、化繁为简的翻译机——解释器模式
介绍:Interpreter Pattern是一种用得比较少的行为型模式,其提供了一种解释语言的语法或表达式的方式,该模式定义了一个表达式接口,通过该接口解释一个特定的上下文。

定义: 给定一个语言,定义它的方法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。

使用场景:如果某个简单的语言需要解释执行而且可以将该语言中的语句表示为一个抽象语法树时,可以考虑使用解释器模式;在某些特定的领域出现不断重复的问题时,可以将该领域的问题转化为一种语法规则下的语句,然后构建解释器来解释该语句。

UML类图
AbstractExpression:抽象表达式
TerminalExpression:终结符表达式
NonterminalExpression:非终结符表达式
Context:上下文环境类
Client:客户类

简单实现:数学运算

在Android源码中:PackageParser

关于PackageManagerService

小结
优点:灵活的扩展性
缺点:生成大量的类,对于过于复杂的方法,构建其抽象语法树会显得异常烦琐。

十一、让程序畅通执行——命令模式
定义:将一个请求封装成一个对象,从而让用户使用不同的请求把客户端参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。

使用场景:需要抽象出待执行的动作,然后以参数的形式提供出来——类似于过程设计中的回调机制,而命令模式正是回调机制的一个面向对象的替代品。
在不同的时刻指定、排列和执行请求。一个命令对象可以有与初始请求无关的生存期。
需要支持取消操作。
支持修改日志功能,这样当系统崩溃时,这些修改可以被重做一遍。
需要支持事务操作。

UML类图
Receiver:接收者角色
Command:命令角色
ConcreteCommand:具体命令角色
Invoker:请求者角色
Client:客户端角色

简单实现:俄罗斯方块

在Android源码中:在Android事件机制中底层连加对事件的转发处理,Android的每一种事件在屏幕上产生后都会经由底层逻辑将其转换为一个NotifyArgs对象。

Android事件输入系统介绍

命令模式实战

十二、解决解耦的钥匙——观察者模式
介绍:观察者模式是一个使用率非常高的模式,它最常用的地方是GUI系统、订阅——发布系统。

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

使用场景
关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系;
事件多级触发场景;
跨系统的消息交换场景,如消息队列、事件总线的处理机制。

UML类图
Subject:抽象主题题
ConcreteSubject:具体主题
Observer:抽象观察者
ConcreteObserver:具体的观察者

简单实现:发布——订阅过程

public class ObserverTest {

    public static class Coder implements Observer {
        public String name;
        
        public Coder(String name) {
            this.name = name;
        }
        
        @Override
        public void update(Observable o,Object args) {
            System.out.println("Hi, " + name + ",It's update: " + args);
        }
        
        @Override
        public String toString() {
            return "Coder: " + name;
        }
    }
    
    public static class DevTechFrontier extends Observable {
        public void postNewPublication(String content) {
            setChanged();
            notifyObservers(content);
        }
    }
    
    public static void main(String[] args) {
        DevTechFrontier devTechFrontier = new DevTechFrontier();
        
        Coder coder1 = new Coder("coder1");
        Coder coder2 = new Coder("coder2");
        Coder coder3 = new Coder("coder3");
        
        devTechFrontier.addObserver(coder1);
        devTechFrontier.addObserver(coder2);
        devTechFrontier.addObserver(coder3);
        
        devTechFrontier.postNewPublication("hahahahahahhhhahahahha");
    }
}

Observer和Observable是JDK中内置类型,Observer是抽象的观察者角色,Coder是具体的观察者角色;Observable是抽象的被观察者角色,DevTechFrontier是具体的被观察者角色。当DevTechFrontier有更新时,会遍历所有观察者(这里是Coder),然后给这些观察者发布一个更新的消息,即调用Coder的update方法,这样就达到了一对多的通知功能。在这个过程中,通知系统都是依赖Observer和Observable这些抽象类,因此,对于Coder和DevTechFrontier完全没有耦合,保证了订阅系统的灵活性,可扩展性。

在Android源码中:ListView的Adapter

观察者模式的深入拓展: BroadcastReceiver

实战

小结
优点:观察者和被观察者之间是抽象耦合,应对业务变化;增强系统灵活性、可扩展性
缺点:执行效率低

十三、编程中的“后悔药”——备忘录模式
介绍:是一种行为模式,该模式用于保存对象当前状态,并且在之后可以再次恢复到此状态。

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

使用场景
需要保存一个对象在某一个时刻的状态或部分状态
如果用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性,一个对象不希望外界直接访问其内部状态,通过中间对象可以间接访问其内部状态。

UML类图
Originator:负责创建一个备忘录,可以记录、恢复自身的内部状态。
Memento:备忘录角色,用于存储Originator的内部状态,并且可以子上Originator以外的对象访问Memento。
Caretaker:负责存储备忘录,不能对备忘录的内容进行操作和访问,只能将备忘录传递给其他对象

简单示例:游戏存档

在Android源码中: Activity中的状态保存。

深度拓展

实战

小结
优点:给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态;实现了信息的封装,使得用户不需要关心状态的保存细节。
缺点:消耗资源

十四、解决问题的“第三者”——迭代器模式
定义:提供一种方法顺序访问一个容器对象中的各个元素,而又不需要暴露该对象的内部表示。

使用场景:遍历一个容器对象时

UML类图
Iterator:迭代器接口
Concrete Iterator:具体迭代器类
Aggregate:容器接口
Concrete Aggregate:具体容器类
Client:客户类

简单实现:员工遍历

在Android源码中:数据库查询使用的Cursor

小结
优点:支持以不同的方式去遍历一个容器对象
缺点:类文件的增加

十五、抓住问题核心——模板方法模式
定义:定义一个操作中的算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

使用场景
多个子类有公有的方法,并且逻辑基本相同时;
重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现
重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数约束其行为

UML类图
AbsTemplate:抽象类,定义了一套算法框架
ConcreteImplA:具体实现类A
ConcreteImplB:具体实现类B

简单示例:码农和军事计算机开机

在Android源码中:AsyncTask

尝试拓展:Activity的生命周期函数

实战

小结
优点:封装不变部分,扩展可变部分;提取公共部分代码,便于维护
缺点:代码阅读难度提高

十六、访问者模式
介绍:是一种将数据操作与数据结构分享的设计模式,是23种最复杂的一个,但使用频率并不高。

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

使用场景
对象结构比较稳定,但经常需要在此对象结构上定义新的操作
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。

UML类图
Visitor:接口或者抽象类,它定义了对每一个元素访问的行为
ConcreteVisitor:具体的访问者,它需要给出对每一个元素类访问时所产生的具体行为
Element:元素接口或者抽象类,它定义了一个接收访问者(accept)的方法,其意义是指每一个元素都要可以被访问者访问
ElementA、ElementB:具体的元素类,它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法
ObjectStructure:定义当中所提到的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素供访问者访问

简单示例:老板、CTO对员工的访问

在Android源码中:编译时注解

实战:实现APT

小结
优点:各角色职责分享,符合单一职责原则;具体优秀的扩展性;使得数据结构和什么用于结构上的操作解耦,使得操作集合可以独立变化;灵活性
缺点:具体元素对访问者公布细节,违反迪米特原则;具体元素变更时导致修改成本大;违反了依赖倒置原则,为了达到“区别对待”而依赖了具体类,没有依赖抽象

十七、“和事佬”——中介者模式
定义:中介者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用

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

UML类图
Mediator:抽象中介者角色,定义了同事对象到中介者对象的接口,一般以抽象类的方式实现
ConcreteMediator:具体中介者角色,继承于抽象中介者,实现了父类定义的方法,它从具体的同事对象接收消息,向具体同事对象发出命令
Colleague:抽象同事类角色,定义了中介者对象的接口,它只知道中介者而不知道其他的同事对象
ConcreteColleagueA/B:具体同事类角色,继承于抽象同事类,每个具体同事类都知道本身在小范围内的TF哟里,而不知道它在大范围内的目的

简单实现:计算机的组成

在Android源码中: Keyguard锁屏

实战

小结
优点:如果类间的依赖关系如网关般复杂,对依赖关系进行解耦使逻辑结构清晰
缺点:如果依赖关系不复杂,使用中介者模式反而使得原本不复杂的逻辑结构变得复杂

十八、编程好帮手——代理模式
定义:为其他对象提供一种代理以控制对这个对象的访问

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

UML类图
Subject:抽象主题类
RealSubject:真实主题类
ProxySubject:代理类
Client:客户类

简单实现:代理律师

在Android源码中:ActivityManagerProxy代理类

Android中的Binder跨进程通信机制与AIDL

实战 :对于不同布局通知选择

小结
缺点:类的增加

十九、物以类聚——组合模式
介绍:Composite Pattern也称为部分整体模式(Part-Whole Pattern),结构型设计模式,它将一组相似的对象看作一个对象处理,并根据一个树状结构来组合对象,然后提供一个统一的方法去访问相应的对象,以此忽略掉对象与对象集合之间的差别。

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

使用场景
表示对象的部分——整体层次结构时。
从一个整体中能够独立出部分模块或功能的场景。

UML类图
Component:抽象根节点,为组合中的对象声明接口
Composite:定义有子节点的那些枝干节点的行为,存储子节点,在Component接口中实现与子节点有关的操作
Leaf:在组合中表示叶子节点对象,叶子节点没有子节点,在组合中定义节点对象的行为
Client:通过Component接口操纵组合节点的对象

简单实现:文件和文件夹

在Android源码中:View与ViewGroup的嵌套组合

为什么ViewGroup有容器的功能

小结
优点:可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让高层模块忽略了层次的差异,方便对整个层次结构进行控制;高层模块可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了高层模块的代码;增加新的枝干构件和叶子构件都很方便,无须对现有类库进行任何修改,符合“开闭原则”;为树形结构的面向对象实现提供了一种灵活的解决方案,通过路子对象和枝干对象的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。
缺点:在新增构件时不好对枝干中的构件类型进行限制,不能依赖类型系统来施加这些约束,因为在大多数情况下,它们都来自于相同的抽象层,此时,必须进行类型检查来实现,这个实现过程较为复杂。

二十、得心应手的“粘合剂”——适配器模式
定义:适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。

使用场景
系统需要使用现有的类,而此类的接口不符合系统的需要,即接口不兼容
想要建立一个可以重复使用的类,用于一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作
需要一个统一的输出接口,而输入端的类型不可预知

UML类图
Target:目标角色
Adaptee:现在需要适配的接口
Adapter:适配器角色,

简单示例:220V电压转换为5V

在Android源码中: ListView的Adapter

尝试拓展

实战演示

小结
优点:更好的复用性、更好的扩展性
缺点:过多使用适配器,会让系统非常零乱,不易整体把握

二十一、装饰模式
介绍:其使用一种对客户端透明的方式来动态地扩展对象的功能,同时它也是继承关系的一种替代方案之一。

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

使用场景:需要透明且动态地扩展类的功能时

UML类图
Component:抽象组件
ConcreteComponent:组件具体实现类
Decorator:抽象装饰者
ConcreteDecoratorA:装饰者具体实现类

简单实现:穿衣服

在Android源码中:Context类

Context与ContextImpl

模式实战

小结

二十二、对象共享,避免创建多对象——享元模式
定义: 使用共享对象可有效地支持大师的细粒度的对象

使用场景
系统中存在大量的相似对象
细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,也就是说对象没有特定身份
需要缓冲池的场景

UML类图
Flyweight:享元对象抽象基类或者接口
ConcreateFlyweight:具体的享元对象
FlyweightFactory:享元工厂

简单示例:买票软件

在Android源码中:Handler、Looper

深度拓展

小结
优点:大幅度地降低内存中对象的数量
缺点:使得系统更加复杂;将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长

二十三、统一编程接口——外观模式
介绍:Facade在第三方SDK中应用广泛,通过一个外观类使得整个系统的接口只有一个统一的高层接口。

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

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

UML类图
Facade:系统对外的统一接口,系统内部系统地工作
SystemA、SystemB、SystemC:子系统接口

简单示例:手机

在Android源码中:ContextImpl

深度拓展

实战

小结
优点:对客户程序隐藏子系统细节,因而减少了客户对于子系统的耦合,能够拥抱变化;外观类对子系统的接口封装,使得系统更易于使用
缺点:外观类接口膨胀;外观类没有遵循开闭原则,当业务出现变更时,可能需要直接修改外观类

二十四、连接两地的交通枢纽——桥接模式
定义:将抽象部分与实现部分分离,使它们都可以独立地进行变化

使用场景
任何多维度变化类或者说多个树状类之间的耦合都可以使用桥接模式来实现解耦
如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,可能通过桥接模式使它们在抽象层建立一个关联关系
对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,也可以考虑使用桥接模式
一个类存在两个独立变化的维度,且这两个维度都需要进行扩展

UML类图
Abstraction:抽象部分
RefinedAbstraction:优化的抽象部分
Implementor:实现部分
ConcreteImplementorA/ConcreteImplementorB:实现部分的具体实现
Client:客户类

简单实现:咖啡大小杯是否加糖

在Android源码中:Window与WindowManager

关于WindowManagerService

实战

小结
优点:分离抽象与实现、灵活的扩展、对客户来说透明的实现
缺点:不容易设计

二十五、MVC的介绍与实战
MVC在Android中的实现
ListView与Adapter
图片显示实例:

public class Model {
    private final Handler mHandler = new Handler();
 private OnStateChangeListener mListener;
private Bitmap mBitmap;
    private Context mContext;
public interface OnStateChangeListener {
        void onStateChanged(Bitmap image);
    }
public Model(Context context) {
        mContext = context;
        mBitmap = BitmapFactory.decodeResource(context.getResources(),R.mipmap.ic_launcher);
    }
public void setOnStateChangeListener(OnStateChangeListener listener) {
        mListener = listener;
    }
public void loadImage() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                    mBitmap = BitmapFactory.decodeResource(mContext.getResources(),R.mipmap.ic_launcher);
                    if (null != mListener)
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                mListener.onStateChanged(mBitmap);
                            }
                        });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }).start();
    }
public void clear() {
        mBitmap = null;
        if (null != mListener)
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mListener.onStateChanged(null);
                }
            });
    }
public Bitmap getBitmap() {
        return mBitmap;
}
}
public class MainActivity extends AppCompatActivity implements Model.OnStateChangeListener{
    private ImageView mIVImage;
    private Model mModel;
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
mModel = new Model(this);
        mModel.setOnStateChangeListener(this);
mIVImage = (ImageView) findViewById(R.id.main_image_iv);
        mIVImage.setImageBitmap(mModel.getBitmap());
    }
public void onClick(View view) {
        switch (view.getId()) {
            case R.id.main_load_btn:
                mModel.loadImage();
                break;
            case R.id.main_clear_btn:
                mModel.clear();
                break;
        }
    }
@Override
    public void onStateChanged(Bitmap image) {
        mIVImage.setImageBitmap(image);
    }
}

二十六、MVP应用架构模式
MVP模式的三个角色
Presenter——交互中间人:主要作为沟通View和Model的桥梁,它从Model层检索数据后,返回给View层,使得View和Model之间没有耦合,也将业务逻辑从View角色上抽离出来。
View——用户界面:通常指Activity、Fragment或者某个View控件,它含有一个Presenter成员变量。通常View需要实现一个逻辑接口,将View上的操作通过会转交给Presenter进行实现,最后,Presenter调用View逻辑接口将结果返回给View元素。
Model——数据的存取:对于一个结构化的App来说,Model角色主要是提供数据的存取功能。Presenter需要通过Model层存储、获取数据,Model就像一个数据仓库。更直白地说,Model是封装了数据库DAO或者网络获取数据的角色,或者两种数据获取方式的集合。

NavigationView中的MVP模式:NavigationView实际上是一个FrameLayout,在这个FrameLayout中又包含了一个RecyclerView。如果用户设置了Header布局,那么NavigationView就把这个Header作为RecyclerView的第一个Item View,在Header的下面就是菜单列表。

MVP的实现:开发技术前线APP

MVP与Activity、Fragment的生命周期:
由于Presenter持有了MainActivity的强引用,如果在请求结束之前Activity被销毁,那么由于网络请求还没有返回,会导致内存泄露。可以通过弱引用和Activity、Fragment的生命周期来解决这个问题。首先建立一个Presenter抽象类,View类型通过这个抽象类的泛型传递进来,Presenter对这个View持有弱引用。然后创建一个MVPBaseAcitivity基类,通过这个基类的生命周期函数来控制它与Presenter的关系。

public abstract class BasePresenter<T> {
  protected Reference<T> mViewRef;
  public void aatchView(T view) {
    mViewRef = new WeakReference<T>(view);
  }
  protected T getView() {
    return mViewRef.get();
  }
  public boolean isViewAttached() {
    return mViewRef != null && mviewRef.get != null;
  }
  public void detachView() {
    if(mViewRef != null) {
      mViewRef.clear();
      mViewRef = null;
    }
  }
}

public abstract class MVPBaseActivity<V,Textends BasePresenter<V>extends Activity {
  protected T mPresenter;
  @SuppressWarnings("unchecked")
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mPresenter = createPresenter();
    mPresenter.accachView((V) this);
  }
  @Override
  protected void onDestroy() {
    super.onDestroy();
    mPresenter.detachView();
  }
  protected abstract T cratePresenter();
}

二十七、MVVM应用浅析
介绍:MVVC与MVC、MVP的本质是一样的,都是将逻辑分层解耦的产物,与MVP不同的是,在ViewModel中有一个叫做Binder或DataBinding Engine的东西,之间在MVP中由Presenter负责的View和Model之间的数据逻辑操作就交给这个Binder去处理,而在View中我们只需要声明View上显示的内容是Model的哪一块数据,将其与之绑定在一起即可,最后当ViewModel对Model层的相关数据进行更新的时候,Binder会将更新后的数据传递给View,View则根据新的数据刷新自己,同样地,当用户对View层操作的时候,如果涉及对数据的更新,Binder同样也会把数据更新到Model层。

MVVM的基本结构
Model:MVVM的Model层与MVC、MVP没太大区别,也主要是封装数据存储或操作的一些逻辑,与两者不同的是Model会提供一系列的实体类用作与UI进行绑定,ViewModel则会在修改这些数据后将数据变化告诉View层并使UI刷新
View:View层与MVC、MVP一样,都是用于处理界面的逻辑且不参与业务逻辑相关的操作,只负责显示由ViewModel提供的数据
ViewModel:本质是视图模型与视图状态的全称,其主要职责就是为View层提供一个可供其显示的数据模型并且同时搜集、处理这些数据

View与ViewModel之间的交互:Binder的实现除第三方框架外,还有Command命令模式,View和ViewModel分别可以看作是:Invoker和Receiver

MVVM在Android中的应用与DataBinding使用浅析:

//集成DataBinding,在Module的build.gradle文件中添加如下代码:
dataBinding {
        enabled = true
    }

//修改布局文件
<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <import type="com.lewanjiang.mvvmtest.User" />
        <variable
            name="user"
            type="com.lewanjiang.mvvmtest.User" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:gravity="center">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@(User.fistName)"
            android:textSize="20sp"/>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@(user.lastName)"
            android:textSize="25sp"/>
    </LinearLayout>
</layout>

//定义实体类
public class User {
    private String firstName;
    private String lastName;

    public User(String firstName,String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}

//修改MainActivity
public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding mActivityMainBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mActivityMainBinding = DataBindingUtil.setContentView(this,R.layout.activity_main);
        User user = new User("bob","Jiang");
        mActivityMainBinding.setUser(user);
    }
}

MVVM的优缺点
优点:相对于MVP而言,不需要手动去处理大师的View和Model相关的操作,因为双向绑定,只需要保证Model正确,那么View就一定是正确的,让测试变得简单

MVC、MVP与MVVM的异同

二十八、易混淆的设计模式
简单工厂、工厂方法、抽象工厂、Builder模式的区别
简单工厂:一个工厂方法创建不同类型的对象
工厂方法:一个具体的工厂类负责创建一个具体对象类型
抽象工厂、一个具体的工厂类负责创建一系列相关的对象
Builder:对象的构建与表示分享,它更注重对象的创建过程

代理与装饰模式、桥接模式
代理模式:为其他对象提供一种代理以控制对这个对象的访问
装饰模式:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活
桥接模式:将抽象和实现解耦,使得两者可以独立地变化

外观模式与中介模式
外观模式:要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。外观模式提供一个高层次的接口,使得子系统更易于使用。
中介模式:用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

策略与状态模式、命令模式
策略模式:定义一组算法,将每个算法都封装进来,并且使它们之间可以互换
状态模式:当一个对象内在状态改变时允许其改变行为,这个对象看起来介改变了其类。
命令模式:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

推荐阅读更多精彩内容