2019-01-24 2.14 Android组件、模块化与架构

2 minute read

2.14.1 MVC & MVP & MVVM

  1. MVC
    • Controller - 转发请求, 处理请求, 控制View & Model, Android上对应Activity, 处理数据、业务和UI
    • View - 展示 & 交互, Android上对应XML布局文件
    • Model - 程序功能(算法等)、数据(数据库相关), Android上对应实体模型(数据获取、存储、数据状态变化)
    • 优点
    • 简化程序修改和扩展
    • 提高复用
    • 重用行高 - 缺点
    • 定义/规范不明确
    • iOS Controller 类容易过大, 容易混淆View和Controller

MVC架构

  1. MVP
    • Presenter - 事件处理, 检索Model数据, 与View沟通, Model & View桥梁, Android上对应对应View和Model间的交互和业务逻辑
    • View - 展示, Passive View, Android上对应Activity和XML, 负责绘制和用户交互
    • Model - 业务逻辑 & 数据模型, Android上对应实体模型
    • 优点
    • 简化程序修改和扩展
    • 提高复用
    • View & Model 完全分离, View更加松散
    • 容易单元测试, 因为和View的交互以接口形式 - 缺点
    • Presenter容易臃肿

MVP架构

  1. MVVM
    • ViewModel 显示数据 & 处理用户交互, Android上对应完成View和Model间的交互, 负责业务逻辑
    • View - 展示, Android上对应Activity和XML
    • Model - 存储内容, Android上对应实体模型
    • Binder - 声明性数据和命令绑定

MVVM架构

2.14.2 设计原则

原则 基本概念 解决问题 基本实现
开闭原则(OCP, Open-Closed Principle) 对扩展开放, 对修改关闭 实现热拔插, 解耦 接口抽象
里氏替换原则(LSP, Liskov Substitution Principle) 子类是父类的具体抽象, 抽象可代表父类(Is-A)), 子类可以扩展父类的功能, 但不能改变父类原有的功能 解释抽象化的原则 继承, 抽象
依赖倒转原则(DIP, Dependency inversion Principle) 面向接口编程, 不面向实现编程 易于扩展 接口编程时类型使用基类, 而不使用具体实现的子类
单一职责原则(SRP, Single Responsibility Principle) 一个类应该有且仅有一个引起它变化的原因, 否则类应该拆分 降低类复杂度, 提高类可读性, 提高系统可维护性, 变更引起的风险降低 拆分类
接口隔离原则(ISP) 使用多个隔离接口, 使用单个接口要好 降低耦合 封装接口的时候, 尽量用不同接口解决不同问题, 尽量不要合用一个接口
迪米特法则(LoD, Law of Demeter) 一个对象对其它对象保持最少的了解 降低耦合 写一个系统架构, 或模块的时候, 尽量少的对外依赖
合成复用原则(CRP) 优先使用合成/聚合, 而非继承 可以通过引入抽象类更加灵活, 相互耦合变小, 更加简单 尽量将已有对象纳入到新对象中, 成为新对象的一部分, 而不使用继承的方式进行复用, 如ClassLoader中双亲委派架构

2.14.3 设计模式

  • 创建型模式 (Singleton, Prototype, FactoryMethod, AbstractFactory, Builder)
    1. 工厂模式(简单工厂, 抽象工厂)
      • 优点: 1). 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程, 2). 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则
      • 缺点: 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。
    2. 单例模式(饿汉、懒汉等)
// 懒汉/懒加载..
// 性能高(无锁), 线程安全, JVM ClassLoader保证安全的模型
public class Sample {
  private static class LazyHolder {
    private static final Sample INSTANCE = new Sample();
  }

  public static Sample getInstance() {
    return LazyHolder.INSTANCE;
  }
}
  1. 建造者模式
- 用途: 构建复杂/混合类型对象
- 优点: 1). 各个具体的建造者相互独立,有利于系统的扩展。2). 客户端不必知道产品内部组成的细节,便于控制细节风险。
- 缺点: 1). 产品的组成部分必须相同,这限制了其使用范围。2). 如果产品的内部变化复杂,该模式会增加很多的建造者类。
  1. 原型模式(适用于) 提供拷贝方法

浅拷贝 Object#clone只拷贝基本数据类型

深拷贝

1. 实现`Serializable`序列化
2. `Object#clone`逐个变量深拷贝
  • 结构型模式 (Proxy, Adapter, Bridge, Decorator, Facade, FlyWeight, Composite)
    1. 适配器模式
      • 用途: 解决两个接口/类不兼容问题
      • 优点: 1). 客户端通过适配器可以透明地调用目标接口; 2). 复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类; 3). 将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题
      • 缺点: 1). 对类适配器来说,更换适配器的实现过程比较复杂
    • 类适配器(对象适配器) class Aclass B接口”不兼容”, 使用Adapter可以访问A类和B类的方法

    Adapter类图

    • 接口适配器

    解决不想实现所有接口…

    interface_Adapter类图

    1. 装饰模式
    • 优点: 1). 采用装饰模式扩展对象的功能比采用继承方式更加灵活; 2). 可以设计出多个不同的具体装饰类,创造出多个不同行为的组合
    • 缺点: 1). 装饰模式增加了许多子类,如果过度使用会使程序变得很复杂
    A a = new A();
    Decorator decorator = new Decorator(a);
    decorator.doA();
    
    

    装饰模式类图

    1. 代理模式(中介作用和保护目标对象, 代理对象可以扩展目标对象, 降低系统耦合度)
    • 用途: 对AImpl(RealSubject)进行访问权限控制…
    • 优点: 1). 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用; 2). 代理对象可以扩展目标对象的功能; 3). 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度
    • 缺点: 1). 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢; 2). 增加了系统的复杂度

    Proxy模式类图

    1. 外观(Facade)模式

    优点降低耦合度, 缺点是不符合开闭原则, 修改麻烦

    外观模式类图

    1. 桥接模式
      • 优点: 1). 由于抽象与实现分离,所以扩展能力强; 2). 其实现细节对客户透明
      • 缺点: 1). 由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,这增加了系统的理解与设计难度

    桥接模式类图

    1. 享元模式

    实现内存共享, 减少内存的使用..

    享元模式类图

    1. 组合模式
      • 优点: 1). 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码; 2). 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”
      • 缺点: 1). 设计较复杂,客户端需要花更多时间理清类之间的层次关系; 2). 不容易限制容器中的构件; 3). 不容易用继承的方法来增加构件的新功能

    图片来源于设计模式 组合模式类图, 图片来源于设计模式

  • 行为模式 (Template Method, Strategy, Command, Chain of Responsibility, State, Observer, Mediator, Iterator, Visitor, Memento, Interpreter)

    1. 策略模式

    灵活替换不同策略, 即实现(算法); 满足开闭原则

    策略者模式类图

    1. 观察者模式

    订阅对象操作/状态, 发生变化时, 可以收到通知.

    图片来源于设计模式 观察者模式类图 图片来源于设计模式

    1. 模板方法模式

    定义: 定义算法/操作的骨架; 满足开闭原则 优点: 1). 封装不变部分, 扩展可变部分; 2). 提供公共部分代码, 便于代码复用; 3). 部分方法由子类实现, 子类可扩展增加相应功能, 符合开闭原则

    图片来源于设计模式 模板方法模式类图 图片来源于设计模式

    1. 命令模式

    定义: 请求封装为一个对象, 使发出请求的责任和执行请求的责任分割开; 满足开闭原则 优点: 1). 降低系统的耦合度; 2). 增加命令或删除命令非常方便

    图片来源于设计模式 命令模式类图 图片来源于设计模式

    1. 职责链模式

    定义: 避免请求发送者与多个请求处理者耦合, 将所有请求的处理者连成链, 并传递; 满足开闭原则, 单一职责原则 优点: 1). 降低对象的耦合度; 2). 增强系统的可扩展性; 3). 增强了给对象指派职责的灵活性; 4). 职责链简化对象之间的连接; 5). 责任分担

    图片来源于设计模式 职责链模式类图 图片来源于设计模式

    1. 状态模式

    定义: 对有状态的对象, 把复杂的”判断逻辑”提取到不同的状态对象中, 允许状态对象在其内部状态发生变化时改变其行为; 满足单一职责原则 优点: 1). 状态模式将与特定状态相关的行为局部化到一个状态, 将不同状态的行为分割开, 满足”单一职责原则”; 2). 减少对象间的依赖; 3). 有利于程序的扩展

    图片来源于设计模式 状态模式类图 图片来源于设计模式

    1. 中介者模式

    定义: 定义一个中介对象来封装一系列对象之间的交互, 使原有对象之间的耦合松散, 迪米特法则的典型应用 优点: 1). 降低对象之间的耦合度, 使对象易于独立被复用; 2). 将对象间的一对多关联转变为一对一的关联, 提高系统的灵活性

    图片来源于设计模式 中介者模式类图 图片来源于设计模式

    1. 迭代器模式

    定义: 提供一个对象来顺序访问聚合对象中的一系列数据, 而不暴露聚合对象的内部表示; 满足单一职责原则和开闭原则 优点: 1). 访问聚合对象的内容无需暴露它的内部细节; 2). 遍历任务交由迭代器完成, 简化聚合类; 3). 它支持以不同方式遍历一个集合; 4). 增加新的聚合类和迭代器类比较方便; 5). 封装性良好

    图片来源于设计模式 迭代器模式类图 图片来源于设计模式

    1. 访问者模式

    定义: 将作用于某个数据结构中的各个元素的操作分离出来封装成独立的类, 使其在不改变数据结构的前提下可以添加作用于这些元素的新操作, 为数据结构中的每个元素提供多种访问方式 优点: 1). 扩展性好; 2). 复用性好; 3). 灵活性好

    图片来源于设计模式 访问者模式 图片来源于设计模式

    1. 备忘录模式

    定义: 在不破坏封装性的情况下, 捕获一个对象的内部状态, 并在该对象之外保存这个状态, 以便以后当需要时能将对象恢复到原先保存的状态, 满足单一职责原则 优点: 1). 提供一种可恢复状态的机制; 2). 实现了内部状态的封装; 3). 简化发起方

    图片来源于设计模式 备忘录模式类图 图片来源于设计模式

    1. 解释器模式

    定义: 给分析对象定义一个语言, 并定义该语言的文法表示, 再设计一个解析器解析语言的句子 优点: 1). 扩展性好, 2). 容易实现

    解释器模式类图 图片来源于涉及模式

2.14.4 封装、继承、多态

  • 封装
    • 隐藏对象属性和细节, 仅对外公开接口, 控制在程序中属性的读、修改的访问
    • 目的: 增强安全性和简化编程, 使用者不需要关心具体细节
  • 继承
    • 当有多个具有相同特征的属性或行为时, 即可以将相同的部分抽到父类, 子类继承
    • 目的: 代码复用
  • 多态
    • 调用相同方法, 子类通过方法重写或成员覆盖, 表现行为不同
    • 目的: 程序的扩展性和可维护性增强

参考