从Android再来认识23种设计模式

概况来看本文章的内容

创建型:5个

单例模式
Builder
原型模式
工厂方法
抽象工厂

行为型: 11个

策略模式
状态模式

观察者模式
中介者模式
访问者模式
迭代器模式
模板方法

备忘录模式
命令模式
解释器模式
职责链模式

结构型:7个

组合模式
代理模式
装饰模式
外观模式

享元模式
桥接模式
适配器模式

关于面向对象

面向对象的六大原则

谈到设计模式,不得不说说面向对象的六大原则

1. 单一原则

单一原则通俗的讲就是一个类只表达一个概念、一个方法只做一件事情。将一组相关性很高的函数、数据封装到一个类中。换句话说,一个类应该有职责单一。

2. 开闭原则

开闭原则就是一个类对于扩展是开发的,但是对于修改是封闭的。这也是六大原则中最难的,通常开闭都是短暂的美好,但在业务升级与拓展的状态下,原理的开闭是无法满足。即使是这样,也要尽可能的扩展而不是修改。

3. 里氏替换原则

所有引用基类的地方必须能透明地使用其子类对象。看着定义很是抽象,但是通俗的理解就是由子类实例化的父类引用,在使用这个引用时,感觉就像是使用了父类一样。一个简单的例子:

public class T{
  private class A{...}
  class B extends A{...}
  public static void main(String[] args){
    A a = new B();
    // 类型是A,但实际是B类型实例化的
    a.method();
  }
}

4. 依赖倒置原则

依赖倒置主要是为了解耦。具体来说:

  1. 高层模块不应该依赖底层模块,二者都应该依赖其抽象
  2. 抽象不应该依赖细节
  3. 细节应该依赖抽象

5. 接口隔离原则

类之间的依赖关系应该建立在最小的接口上其原则是将繁杂的功能接口拆分成更加单一职责的接口,防止接口内部的耦合。

6. 迪米特原则

一个对象应该对其他的对象有最少的了解 保留关键的共有方法,其他次要的都是私有方法,不应该被外部了解。

Android来表达面向对象的六大原则

1. 单例模式

定义:确保单例类只有一个实例,并且这个单例类提供一个函数接口让其他类获取到这个唯一的实例。

单例模式的意义是显而易见的,能极大的节省资源,提高代码运行效率。缺点也是很明确的,就是不能发生状态的变化,以提供统一的功能。

单例模式有懒汉式与饿汉式,实现方式有静态成员变量法、枚举法、容器法等。单例需要注意的就是懒汉模式下的多线程安全问题。这里给出懒汉模式比较优雅的实现代码,其原理就是类加载器ClassLoader保证了并发的安全性。

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

Android系统下的单例模式有

//获取WindowManager服务引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);  

2. Builder模式

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

AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setIcon(R.drawable.icon)
  .setTitle("title")
  .setMessage("message")
  .setPositiveButton("OK", null)
  // create之前的set方法操作的都是AlertDialog.Builder而已
  .create()
  // create之后就创建出一个真实的AlertDialog了,将复杂的创建过程都隐藏在create方法内部
  .show();

从上述代码中可以非常直观的看出Builder模式就是先设置好各种参数,然后再通过一个方法的调用构建出来一个复杂的对象。

3. 原型模式

原型模式很简单,使用一个实例拷贝后的实例。
在java中拷贝存在深拷贝与浅拷贝,如果类中的成员变量都是基本数据类型,则系统自动实现这些基本数据类型的深拷贝。
那何时要用到原型模式呢?防止自己的实例在传入别的模块后,发生意外的修改。进行一次拷贝,再将该实例当做参数传递给别人,是非常明智的操作,尤其是这个实例是某个类的成员变量。

Uri uri = Uri.parse("smsto:10086");
Intent shareIntent = new Intent(Intent.ACTION_SENDTO, uri);
// 这里举例说明intent是提供了一次clone方法的
Intent intent = shareIntent.clone();
// 假设startActivity方法会修改intent
startActivity(intent);
// 然后接着使用shareIntent进行其他操作
// 如果不clone,鬼知道shareIntent会变成什么样子
shareIntent.putExtra("key", "extra")
...

4. 工厂方法模式

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

public abstract class Product{
    public abstract void method();
} 
public  abstract class Factory{
    public abstract Product createProduct();
}

public class ConcreteProductA extends Prodect{
    public void method(){
        System.out.println("我是产品A!");
    }
}
public class ConcreteProductB extends Prodect{
    public void method(){
        System.out.println("我是产品B!");
    }
}
// 类的实例化被推迟到了子类
public class MyFactory extends Factory{
    public Product createProduct(){
        return new ConcreteProductA();
    }
}

Android下的例子有BitmapFactory

public class BitmapFactory {
    public static Bitmap decodeFile(String pathName, Options opts) {
        Bitmap bm = null;
        ......
        return bm;
    }
}

5. 抽象工厂模式

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

public abstract class AbstractProductA{
    public abstract void method();
}
public abstract class AbstractProdectB{
    public abstract void method();
}
// 这个工厂一旦创建,就天生必须拥有两条生产线,具体生产什么,将由子类决定
public abstract class AbstractFactory{
    public abstract AbstractProductA createProductA();
    public abstract AbstractProductB createProductB();
}

public class ConcreteProductA1 extends AbstractProductA{
    public void method(){System.out.println("具体产品A1的方法!");}
}
public class ConcreteProductA2 extends AbstractProductA{
    public void method(){System.out.println("具体产品A2的方法!");}
}
public class ConcreteProductB1 extends AbstractProductB{
    public void method(){System.out.println("具体产品B1的方法!");}
}
public class ConcreteProductB2 extends AbstractProductB{
    public void method(){System.out.println("具体产品B2的方法!");}
}

public  class ConcreteFactory1 extends AbstractFactory{
    public  AbstractProductA createProductA(){return new ConcreteProductA1();}
    public  AbstractProductB createProductB(){return new ConcreteProductB1();}
}
public  class ConcreteFactory2 extends AbstractFactory{
    public  AbstractProductA createProductA(){return new ConcreteProductA2();}
    public  AbstractProductB createProductB(){return new ConcreteProductB2();}
}

抽象工厂与工厂方法的区别,表面上看就是生产线是多条,还是有一条。但其设计模式的思想是表达了抽象工厂表示一组关联的生产线,而不仅仅是表示同一类型的生产线。

0.0 简单工厂

这并不是一个真正的模式,但是和抽象工厂和工厂方法模式一样,它经常被用于封装创建对象的代码

// 根据传入的参数决定给出哪个Service
public Object getSystemService(String name) {
    if (getBaseContext() == null) {
        throw new IllegalStateException("System services not available to Activities before onCreate()");
    }
    //........
    if (WINDOW_SERVICE.equals(name)) {
         return mWindowManager;
    } else if (SEARCH_SERVICE.equals(name)) {
        ensureSearchManager();
        return mSearchManager;
    }
    //.......
    return super.getSystemService(name);
  }

6. 策略模式

定义:有一系列的算法,将每个算法封装起来(每个算法可以封装到不同的类中),各个算法之间可以替换,策略模式让算法独立于使用它的客户而独立变化。

public abstract class AbstractSortAlgorithm {
  public abstract sort(List list);
}
public class CustomList extends List {
  private AbstractSortAlgorithm algorithm;
  public void setSortAlgorithm(AbstractSortAlgorithm algorithm){this.algorithm = algorithm;}
  public void sortList() {algorithm.sort(this);}
}

Android的属性动画中使用时间插值器,就是策略模式。

7. 状态模式

允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

public abstract class Actor {
    public abstract void act();
}

class GirlActor extends Actor{
    public void act(){System.out.println("GirlActor");}
}
class BoyActor extends Actor{
    public void act(){System.out.println("BoyActor");}
}

class Stage{
    private Actor actor;
    public void firstFrame(Actor actor){this.actor = new GirlActor();}
    public void secondFrame(Actor actor){this.actor = new BoyActor();}
    // 在不同的状态下会有不同的输出
    public void performPlay(){
        actor.act();
    }
}

状态模式在Android下使用的还是很多的,比如WIFI在开启时,自动扫描周围的接入点,然后以列表展示,当WIFI关闭时,清空列表。

8. 责任链模式

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

最好的一个例子就是View的测量,定位,绘制,事件传递,事件分发等。这些例子都是从父View开始依次向子View进行,是一个特别典型的责任链模式。特别要提到的是,savedInstance的处理也是责任链模式。

9. 解释器模式

定义:给定一个语言,定义它的语法,并定义个解释器,这个解释器用于解析语言。

通俗的来讲,就是自定义一个格式文件,然后解析它。

AndroidManifest.xml是典型的一个自定义xml文件语言,layout目录下的xml文件,都是具有特殊语法的文件。他们都会被XmlPullParser这个解析器进行解释。

10. 命令模式

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

在Android底层的事件机制中,底层逻辑对事件的转发处理。每次的按键事件会被封装成NotifyKeyArgs对象,通过InputDispatcher封装具体的事件操作。

11. 观察者模式

定义:定义了对象之间的一对多的关系,其实就是1:n,当“1”发生变化时,“n”全部得到通知,并更新。

观察者模式比较典型的场景就是发布消息通知订阅者。没错Android下的广播就是观察者模式,在FrameWork层,状态发生变化后(比如WIFI状态),会遍历全部的register。

12. 备忘录模式

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

通俗的讲就是一个提前备份,一旦出现问题,能尽可能的恢复到最接近之前的状态。

Android下的onSaveInstanceState就在时不时的进行着备忘,在onRestoreInstanceState时,就能取出最后的记录。

13. 迭代器模式

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

这个迭代器再好理解不过了,我们天天用的Iterator正是这种迭代器模式。

在Android中SQLiteDatabase的query也是迭代器模式

cursor = database.query(...);
while(cursor.moveToNext()){
    cursor.get...;
}

14. 模板方法模式

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

直接看代码

public class CustomActivity extends Activity {
    // Activity留给用户自定义某些特定步骤
    protected void onCreate(Bundle savedInstanceState){
      super.onCreate(savedInstanceState);
      // ... ...
    }
}

可以看出模板方法就是留出适当的方法等待子类来实现。不管如何重写父类,父类的主逻辑将不会被改变。

15. 访问者模式

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

Android中很少使用访问者模式,ButterKnife中到是使用了。
这里只给出一个案例:
场景:一群人到你家,一起喂食宠物

  1. 抽象宠物角色(被访问者):动物抽象类
  2. 抽象访问者角色(访问者):给宠物喂食的人
  3. 具体被访问者:狗,猫
  4. 结构对象角色:主人家
  5. 具体访问者:主人、其他人

下面就具体来看实现:

  1. 创建抽象节点--宠物
interface Animal {
    void accept(Person person);
}
  1. 创建抽象访问者接口
interface Person {
    void feed(Cat cat);
    void feed(Dog dog);
}
  1. 创建Animal接口的具体节点
class Dog implements Animal {
    @Override public void accept(Person person){
        person.feed(this);
        System.out.println("旺旺");
    }
}
class Cat implements Animal {
    @Override public void accept(Person person){
        person.feed(this);
        System.out.println("喵喵");
    }
}
  1. 创建具体访问者角色
class Owner implements Person {
    @Override public void feed(Cat cat) {System.out.println("主人喂食猫");}
    @Override public void feed(Dog dog) {System.out.println("主人喂食狗");}
}
class Someone implements Person {
    @Override public void feed(Cat cat) {System.out.println("客人喂食猫");}
    @Override public void feed(Dog dog) {System.out.println("客人喂食狗");}
}
  1. 具体的结构对象,访问者与被访问者的交互空间,准确地说是Person访问Animal
class Home {
    private List<Animal> nodeList = new ArrayList<>();
    void add(Animal animal) {nodeList.add(animal);}

    void action(Person person) {
        for (Animal node : nodeList) {node.accept(person);}
    }
}
  1. 最后进行测试
public class Test {
    public static void main(String[] args) {
        Home home = new Home();
        home.add(new Dog());
        home.add(new Cat());

        Owner owner = new Owner();
        home.action(owner);

        Someone someone = new Someone();
        home.action(someone);
    }
}

16. 中介者模式

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

中介者类似于电脑主板,主板接着显示器,硬盘键盘等等设备,设备之间的通信全部经过主板协调。

在Android中的Binder驱动也是一个中介者,所有的Service在通信之前都会向ServiceManger查询出目标Service,也就是说,进程直接不是直接跨进程,而是由ServiceManager来管理的。所以说Binder启动就是一个中介者模式。

17. 代理模式

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

AIDL文件会生成一个代理类,在跨进程通信传递数据时,Parcelable对象的序列化与transct写入对方进程的内存地址,这一系列的操作都被代理类进行了隐藏。所以代理模式能很好的隐藏一些复杂的操作。

18. 组合模式

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

Android中的ViewGroup便是树形结构,每个ViewGroup包含一系列View,而ViewGroup本身就是View。这便是组合模式。

19. 适配器模式

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

适配器使用的频率特别高,每个ListView和RecycleView都需要适配器来给出每个ItemView。至于如何给出ItemView,可能不尽相同,但是Adapter给出了一个统一的接口规范,只需要进行一次转换即可。

20.装饰模式

定义:动态的给一个对象添加额外的指责。就增加功能来说,装饰模式比子类集成的方式更灵活。

Android中的ContextWrapper(ContextThemeWrapper的父类(Acrtivity的父类))。是一个地地道道的装饰者

public class ContextWrapper extends Context {
    Context mBase;
    // 由构造函数传入一个content
    public ContextWrapper(Context base) {
        mBase = base;
    }
}

21. 享元模式

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

享元可以说一直都在被使用,比如Java的常量池,线程池等。主要是为了重用对象。

在Android中,Handler机制中有个postMessage方法,可以通过Message.obtain()来从消息池中取出可复用Message,避免产生大量Message对象。

22. 外观模式

定义:要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。

外观模式核心在于子系统与外部通信,比如当你sendBroadcast、startActivity、bindService等等,系统内部的实现非常复杂,但是一个统一的对象context把子系统全部隐藏起来。

23. 桥接模式

定义:将抽象部分与实现部分分离,使他们独立地进行变化。

一个类在多个维度上要进行变化,比如列表View在列表如何展示与数据来源上的两个维度的变化。具体来说就是AbsListView的两个维度的变化,AbsListView的子类有ListView、GridView、CustomListView等等,而ListAdapter也是一个变化的维度。

24. MVC、MVP、MVVM等组合设计模式

MVC

全称是Model-View-Controller,如下图:
MVC模式

在Android下,layout.xml就是View层。数据层可以由数据库与网络数据构成Model,但Model层要完全表达数据的输入与输出。Activity就是Controller层,在比较复杂的Activity中,往往不能很好的完成表达成Controller层,往往混合了View层代码与Model层代码,这也是Activity的尴尬之处。

MVP

Model-View-Presenter,如下图:


mvp模式

这个模式主要是为了解决Activity在MVC模式下的尴尬处境,把Activity完全表示为一个View层。这样Activity就可以很单纯的处理View的问题了,处理View显示问题也是Activity的特长。

MVVM

Model-View-ViewModel,如下图:
MVVM模式

这个模式优化的显示层的问题,View单向的访问ViewModel,ViewModel与Model之间进行交互。在RecycleView中的ViewHolder正是这种模式。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342