《Android进阶之光》读书笔记(五)

Posted by alonealice on 2017-07-25

设计模式六大原则

1.单一职责原则:就一个类而言,应该仅有一个引起它变化的原因。
人话:我们不应该让一个类承担过多的责任,责任越多,耦合就越多,一个职责的变化会抑制或削弱其他职责的能力。

2.开放封闭原则:类、模块、函数等应该是可以扩展的,但是不可修改。
人话:我们在面对需求改变的时候要尽可能的保证相对稳定,尽量通过拓展的方式来实现变化,而不是通过修改原有代码的方式。

3.里氏替换原则:所有引用基类(父类)的地方必须能透明的使用其子类的对象。
人话:基类对象替换为子类对象时,程序将不能不会产生任何错误和异常;反之则不成立。所以在程序中尽可能的使用基类类型来对对象定义,在运行时再确定其子类类型。
注意:子类的所有方法都必须在基类中声明,或子类必须实现父类中声明的所有方法。

4.依赖倒置原则:高层模块不应该依赖低层模块,两者都应该依赖于抽象,抽象不应该依赖细节,细节应该依赖抽象。
人话:抽象指的是接口或抽象类,细节指的是具体的实现类;高层模块指的是调用端,低层模块指的是具体的实现端。这句话的意思就是两个实现类之间不能直接发生依赖,应该通过接口或者抽象类产生依赖。

5.迪米特原则:一个软件实体应该尽可能少的与其他实体发生相互作用。
人话:两个对象之间要尽量避免相互作用,如果一个对象要调用另一个对象,那么要尽量使用第三者来调用。通过引入一个合理的第三者可以降低现有对象之间的耦合度。

6.接口隔离原则:一个类对另一个类的依赖应该建立在最小的接口上。
人话:接口要尽可能的小,不要建立臃肿的接口,接口中的方法要尽量的少。

设计模式分类

创建型设计模式:单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。
结构型设计模式:适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

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

单例模式

  1. 饿汉模式
1
2
3
4
5
6
7
8
9
10
11
public class Singleton{
private static Singleton singleton=new Singleton();

private Singleton(){

}

public static Singleton getInstance(){
return singleton;
}
}

类加载时就初始化,加载会慢,但是获取对象会快。同时避免了线程同步的问题,但是如果没有使用会存在内存浪费的问题。

  1. 懒汉模式(线程不安全版)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Singleton{
private static Singleton singleton;

private Singleton(){

}

public static Singleton getInstance(){
if(singleton==null){
singleton=new Singleton();
}

return singleton;
}
}

节约了资源,但是第一次时会比较慢,同时多线程下也会不安全。

  1. 懒汉模式(线程安全)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Singleton{
private static Singleton singleton;

private Singleton(){

}

public static synchronized Singleton getInstance(){
if(singleton==null){
singleton=new Singleton();
}

return singleton;
}
}

解决了线程安全问题,但是同步时会造成不必要的开销,所以不建议使用。

  1. 双重检查模式(DCL)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Singleton{
private static volatile Singleton singleton;

private Singleton(){

}

public static Singleton getInstance(){
if(singleton==null){
synchronized(singleton){
if(singleton==null){
singleton=new Singleton();
}
}
}

return singleton;
}
}

资源利用率高,只有在第一次的时候才会有同步和创建问题,但是还是会有突然失效问题。

  1. 内部静态类模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Singleton{

private Singleton(){

}

public static Singleton getInstance(){


return SingletonHolder.mSingleton;
}

private static class SingletonHolder{
private static final Singleton mSingleton=new Singleton();
}
}

这种模式在加载类时不会初始化mSingleton,只会在第一次调用时初始化,同时也避免了同步问题。推荐使用。

  1. 枚举单例
1
2
3
4
5
6
7
public enum Singleton{
SINGLETON;

public void doSomething(){

}
}

默认枚举实例的创建是线程安全的,并且在任何情况下都是单例。枚举单例最大的有点就是简单,但是枚举用到的情况很少。

在反序列化时,这些模式下都会重新创建对象,避免方法如下 重写readResolve方法:

1
2
3
private Object readResolve() throws ObjectStreamException{
return singleton;
}

系统中单例使用的地方有:LocalBroadcastManager,InputMethodManager以及各种服务的注册。

简单工厂模式

主要有三个角色:

IProduct:抽象产品类,里面定义实例所有的公共方法。

Product:具体产品类,继承抽象产品类,实现相应的逻辑。

Factory:工厂类,实现所有的创建产品类的逻辑,里面的方法可以被外界调用,即根据不同的情况创建各个Product。

使用场景:

工厂类负责创建的对象较少;客户只需要知道传入工厂的参数,无须关心创建对象的逻辑。
优缺点:避免了直接实例化类,降低了耦合;可实例化的类型在编译期间就已经确定,如果要增加,需要修改工厂,这违背了开放封闭原则;子类过多或子类层次过多时不适合使用。

工厂方法模式

主要有三个角色:

Product:抽象产品类,里面定义实例所有的公共方法。

ConcreteProduct:具体产品类,继承抽象产品类,实现相应的逻辑。

Factory:抽象工厂类,该方法返回一个Product类型的对象。

ConcreteFactory:具体工厂类,通过反射的方式创建返回ConcreteProduct实例。

优缺点:添加产品时不需要修改工厂类,可以直接通过添加类的方式完成。

也可以理解为继承实现抽象,但是实现的方法中返回或操作了不同的子类,那个该方法就是工厂方法。

具体的示例有:activity的oncreate,它会操作不同的View的子类;如ArrayList、HashMap等的interator,会返回不同的子类。

建造者模式

定义:将一个复杂对象的构建与它的表示分离。一般会使用一个内部类Builder来创建对象。
使用场景:对象一般比较复杂,有很多的变量需要传入的情况。

常见情况:AlertDialog

装饰模式

角色如下:

Component:抽象组件,接口或是抽象类。

ConcreteComponent:组件的具体实现,继承Component。

Decorator:抽象装饰者,实现Component,同时又Component变量。

ConcreteDecorator:装饰者实现,同时添加自己的相关逻辑功能。

实现时先回创建组件对象添加到装饰者中,装饰者即调用组件的逻辑,有调用自己的相关逻辑,从而实现功能的添加。

优缺点:通过组合而非继承的方式扩展对象的功能,增加了灵活性,组件和装饰者都可以独立的变化;但是如果抽象组件变化则所有的都要变化,同时装饰层数不能过多,否则会影响效率。

外观模式

定义:要求一个子系统的外部与内部通信都必须通过一个对象。即所有子系统对象的方法调用都要通过另一个类。

优缺点:减少系统的相互依赖,子系统之间的依赖,可以通过外观类相互通信,隐藏了子系统,加强了安全性;缺点是不符合开放封闭原则,业务变更时需要直接修改外观类。

享元模式

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

对象由3个,一个是抽象享元角色,定义的方法的接口,第二个是实现了该接口的类,即具体享元角色,第三个是享元工厂,在里面有一个享元角色的容器(Map),有静态方法根据参数返回容器里面的角色或者创建新对象返回并添加到容器。

享元模式的使用场景是系统中存在大量的相似对象,而且需要缓存池。

策略模式

定义:定义一系列算法,把每个算法封装起来,使他们可以互相替换。其实就是把一些不同情况下的具体的执行代码各自封装起来,避免大量代码功能相似的代码堆在一起。

优缺点:可以避免多重条件语句,添加策略时只需要在添加一个类实现方法接口就好了;缺点是复用性小,上层模块必须要知道有哪些策略。

使用场景:动画的插值器TimeInterpolator

模块方法模式

实际上就是父类封装了固定的流程,同时有一些具体实现方法为抽象方法。子类继承后实现具体方法。

优缺点:去除了子类中的部分重复代码。

使用场景:AsyncTask中的方法,activty的生命周期的方法

观察者模式

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

即需要有一个被观察者,同时多个观察者创建都注册到被观察者,当被观察者改变时,向所有的观察者发送通知,更新观察者。

优缺点:观察者和被观察者都是抽象耦合,容易扩展。

使用场景:各种listener,adapter中的notifyDataSetChanged()

(补充的设计模式)

原型模式

定义:原型模式是用户从一个样板对象中复制一个内部属性一致的对象即clone。被复制的对象就是原型,这种情况一般用在创建和初始化对象比较复杂的情况,其本质就是对象拷贝。

常见情况ArrayList

深拷贝和浅拷贝

如果一个对象进行clone后,里面的所有字段都会重新构造一遍,而是引用的原始文档的字段,所以当改变字段内容后,两边都会改动。这种情况时浅拷贝。为避免这种情况,应该使用深拷贝,深拷贝是处理拷贝该对象外,还要将里面的需要拷贝的对象也都拷贝。ArrayList除了会拷贝object外,还会重新构建内部存储的数据,Intent的clone方法是重新new一个对象。

状态模式

定义:一个对象的内在状态改变时,其具体的行为也会改变。想过与状态改变时,某些方法不执行等,其结构与策略模式一样,但是具体执行哪个策略与状态有关。

使用场景:WifiSetting中的wifi管理

责任链模式

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

使用场景:touch事件的传递

解释器模式

定义:给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。举个例子,比如说计算器,输入一个表达式,要解析里面的符号和数字,进行计算。

使用场景:PacketParser,该类会解析AndroidManifest.xml的内容,将其转换为相应的类。

命令模式

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

就是讲具体执行的逻辑封装在一个请求对象中,在将这个对象放到请求者中,请求者请求时执行请求对象,请求对象执行具体的逻辑代码。

备忘录模式

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

使用场景:onSaveInstancState和onRestoreInstance

迭代器模式

定义:提供一种方法顺序访问一个容器对象中的各个元素,而又不需要暴露该对象的内部表示。迭代器主要是用在遍历容器对象时使用。

使用场景:Map,List中的Iterator

访问者模式

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

其基本的想法是:一个由许多对象构成的、比较稳定的对象结构,这些对象都有一个accept方法来接收访问者对象的访问,访问者是一个接口,有visit方法,这个方法对访问到的对象中的不同类型的元素做出不同的处理。在对象的一次访问中,我们遍历所有的元素,对每个元素都进行accept方法,在accept方法中都调用visit方法,从而是访问者可以处理所有的元素,我们可以根据不同的访问者类来完成不同的操作,达到区别对待的结果。

使用场景:ButterKnife