设计模式简单记录

面向对象的六大原则

  • 单一职责原则(SRP):一个类应该仅有一个引起它变化的原因。
  • 开闭原则(OCP):软件中的对象应该对于扩展是开放的,对于修改是封闭的。
  • 里氏替换原则(LSP):所有引用基类的地方必须能透明的使用其子类。
  • 依赖倒置原则(DIP):模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或者抽象类产生的。
  • 接口隔离原则(ISP):客户端不应该依赖它不需要的接口。
  • 迪米特原则(LOD):一个对象应该对其他对象有最少的了解。

在开发过程中把握平衡点,使系统可以具有高稳定性、高可扩展性并且高内聚、低耦合,在不断迭代中完善架构。

单例模式

定义:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

场景:对于创建一个对象需要消耗过多的资源,可以使用此模式,如:访问数据库、网络、IO。

单例模式特点:

  • 构造函数私有
  • 通过一个静态方法或者枚举返回单例类对象
  • 确保单利类的对象有且只有一个
  • 确保单利类对象在反序列化时不会重新创建对象

一般最常用的为静态内部类单例模式,可以确保线程安全、对象唯一、同时也延迟实例化。
Android中使用单例模式最直观则是Application,但如果应用为多进程时,则onCreate方法执行多次。单例模式持有Context时应持有Application Context,避免内存泄漏。

Builder模式

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

场景:

  • 相同方法,不同执行顺序,产生不同的事件结果
  • 多个参数装配到一个对象中,产生的结果不相同
  • 对象复杂,参数多且有默认值

Android中最常见的为AlertDialog.Builder,虽然不是经典实现,但灵活运用设计模式则十分重要。在实际开发中很多时候为了与应用设计统一,会替换系统AlertDialog样式,这时可以运用Builder模式,构建统一Dialog,方便调用。Intent有时需要携带很多参数在页面间传递,这时使用Builder模式也会更加清晰。

原型模式

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

场景:

  • 类初始化需要消耗很多资源
  • new一个对象需要非常繁琐的数据准备或者访问权限
  • 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值,考虑使用原型模式拷贝多个对象提供给调用者

当对象实现Cloneable接口时,其构造函数不会执行。为减少错误,尽量使用深拷贝,避免操做副本时影响原始对象。

工厂模式

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

场景:生成复杂对象的地方

清晰定义一个产品的层级结构,实践中可以将具有相同模式的Activity、View、数据存储等进行封装。

抽象工厂模式

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

场景:一个对象族有相同的约束时可以使用

清晰定义一个产品族的层级结构。

策略模式

定义:定义一系列算法,并将其封装且可以互相替换。

场景:

  • 针对同一类型问题多种处理方式
  • 需要安全的封装多种同一类型的操作时
  • 同一抽象类有多个子类,且需要使用if-else或者switch-case选择具体子类时

可以在相通的抽象下实现不同的策略,如对于各种第三分享的调用可以使用策略模式。

状态模式

定义:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

场景:

  • 一个对象的行为取决于它的状态,并且他必须在运行时更具状态改变他的行为
  • 代码中包含大量与对象有关的条件语句

不同的状态下对于同一行为有不同的响应,如用户登录状态,复杂界面View的显示与隐藏,用多态替换条件语句。

责任链模式

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

场景:

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

Android对于事件的分发处理则类似于责任链模式,若处理者太多则会影响性能。

解释器模式

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

场景:

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

Android中AndroidManifest的解析则可以看作为对解释器模式的运用。

命令模式

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

场景:

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

当一起请求操做较多的对象时可以考虑使用命令模式,使代码条理清晰,增强代码可读性。

观察者模式

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

场景:

  • 关联行为场景,关联行为是可拆分,而不是组合关系
  • 事件多级触发场景
  • 跨系统的消息交换场景

Android中BroadcastReceiver与ListView数据更新这是典型的观察者模式。

备忘录模式

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

场景:

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

Android中Activity、Fragment、View的状态保存为备忘录模式,对于需要回滚操作使此模式可以很好的支持。

迭代器模式

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

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

Android中数据库查询会使用到Cursor,但项目通常使用第三方ORM封装,几乎很少使用其模式。

模板方法模式

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

场景:

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

Android中Activity、Fragment和AsyncTask,都是对流程进行封装,便于调用。重构时可以将子类中相同的方法上移到父类中。

访问者模式

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

场景:

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

将数据结构和数据操作分离,实际开发中很少使用该模式。

中介者模式

定义:定义一个中介对象来封装系列对象之间的交互。中介者使各个对象不需要显示的相互引用,从而使其耦合性松散,而且可以独立改变他们之间的交互。

场景:

  • 一组定义良好的对象,需要进行复杂的通信
  • 定制一个分布在多个类中的行为,而不想生成太多的子类

Android中Binder机制也使用到了中介者模式,开发中对复杂页面的View状态改变也可以使用此模式,让代码更清晰表达。

代理模式

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

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

Android中AMS也用到了此模式,最常用的Retrofit也用到了动态代理,此模式在应用过程中最为常见。

组合模式

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

场景:

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

Android中View与ViewGroup则是组合模式的提现,自定义View也可使用此模式。

适配器模式

定义:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。

场景:

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

ListView和RecyclerView均为此模式的应用,开发中较少用到此模式,在调用一些第三方时使用此模式,例如分享到不同平台具体方式不同,可以使用转换成同一个接口,方便调用替换。

装饰模式

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

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

Android里ContextWrapper则应用了装饰模式,对于类的一些初始化方法可以看作是此模式的实践。

享元模式

定义:使用共享对象可有效地支持大量地细粒度的对象。

场景:

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

Message应用此模式,所有的对象比较是否相等时必须使用equals,如果使用==则会出现由于常量池引起的不相等。

外观模式

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

场景:

  • 为一个复杂子系统提供一个简单接口,对外隐藏子系统的具体实现、隔离变化
  • 当需要构建一个层次结构的子系统时,使用外观模式定义子系统的每层入口点,从而简化了他们之间的依赖关系

ContextImpl应用了外观模式,对于复杂的模块使用Module解耦也可以看做是外观模式。

桥接模式

定义:将抽象部分和实现部分分离,使它们都可以独立地变化。

场景:

  • 当一个对象有多个变化因素的时候,通过抽象这些变化因素,将依赖具体实现,修改为依赖抽象
  • 当某个变化因素在多个对象中共存时
  • 当我们期望一个对象的多个变化因素可以动态的变化,而不影响客户的程序的使用时

Android中音视频播放就类似桥接模式,使应用层api可以调用底层。