【Spark】常见设计模式概览、相似对比及最佳实践

一、常见设计模式概述

1. 单例模式

必要性:静态变量在程序一开始就需要创建好对象,如果这个对象非常耗费资源,并且这次程序执行没有使用它,造成不必要的浪费(伪需求)

适合场景:线程池、缓存、路由表、默认设置等

分类:饱汉/饿汉、线程安全/线程不安全

要点:私有构造
–> 如何防止反射攻击:1.static记录标识位 2.枚举实现

实现方式:静态内部类、双加锁、枚举

  1. 饿汉式是线程安全的:
    –> 前提:同一类加载器,一个类只会被初始化一次;
    有些仅有主动引用(new, getstatic, putstatic, invokestatic字节码指令),如果类没有进行过初始化,由需要先触发其初始化 --> <cinit>() 阻塞执行

  2. 静态内部类实现饱汉且线程安全原理:

    饱汉:外部类加载的时候,内部类不会被加载,静态内部类只是调用的时候用了外部类的名字而已(被动引用)
    线程安全:被动引用执行的初始化,即<cinit>()也是阻塞执行

  3. 枚举为什么是最佳实践?(支持自家方法)

    1.反射内部屏蔽了枚举
    2.序列化/反序列化安全 -> 序列化前后两个对象相等
    3.写法简单

  4. 双加锁演进历程: 加锁 -> 避免串行化设计(双加锁) -> volatile(禁止指令重排序)
    -> 禁止重排序可引申:Java创建对象的三步曲(分配空间、调用构造函数初始化、指向分配好的空间);Sync保证的是同步块和外部代码的有序性和原子性,内部还是会重排序,故会出现“半个对象”问题;C++(new的三种形态)

  5. 单例引起的内存泄露:GCRoot

引申思考:
7. 饿汉的加载时机?所有类是应用启动的时候就load的?
–> 当然是用到的时候初始化,你以为是变形金刚?充能100%然后启动,黄花菜都凉了。除非这个单例类设计的不合理,有很多和该对象无关的方法被提前调用(饱汉和饿汉本质上差别就是:饿汉在类加载的时候就创造了对象)。所以一直觉得饱汉饿汉是个伪需求,为了加速业务启动,甚至懒加载预启动更合适。
–> 冷知识:IDE默认实现的单例是饿汉线程不安全的,kt的object也是

  1. 对比C++智能指针的实现(多个地方用到了一个对象,减少构造析构)

2. 工厂模式

静态工厂模式、工厂方法模式、抽象工厂方式的联系和区别:
(简单的new一个产品类) 静态工厂
–> (开闭原则,new推迟到对应的子类工厂) 工厂方法
–> (打破工厂和产品一对一关系) 抽象工厂

抽象工厂是工厂方法模式的扩展,但因为抽象工厂里定义了生产不同产品族的接口(扩展需要增删接口),又重新违反了开闭原则

3. 适配器模式

对象适配器:把适配工作交给适配器,低耦合 --> 转换
类适配器:通过多继承让类具备多种功能 --> 功能增强
(Java单继承,只能通过接口曲线救国;C++注意菱形继承问题/钻石问题)
缺点:过多使用适配器,会让系统非常零乱,不易于整体把握。例如:明明调用的是A接口,内部却被适配成类B接

4. 命令模式

实现:方法只有execute,可以执行一串Command;调用者和请求接收者分离
关注点分离:如MVC,C控制V
特点:支持多个命令(宏命令)、撤销
适合场景:日志的持久化(store/load)、电视遥控器

5. 组合模式

理解:递归实现功能,父菜单和子菜单使用一致的方法来处理(官方称之为透明化) --> 很容易形成一种树形结构(方便拆装)
优化:如遍历成本较高,组合节点的缓存

6. 装饰者模式

要求:装饰者必须实现被装饰者的接口,方便被外层调用(里氏替换)
理解:就是在对象上包裹了一层,补充功能,代替继承

7. 外观模式

目的:提供一个统一的接口,调用子系统的功能
使用场景:子系统很复杂,但调用方无需关心内部实现
对比:现有的类不符合(适配器),接口实现太大太复杂(外观)、补充功能(装饰者)

8. 迭代器模式

实现:通过hasNext()和next(),顺序访问元素,不暴露对象内部

9. 代理模式

目的:控制对对象对访问,保护被代理对象
对比:适配器重在适配,代理重在保护
适合场景:智能指针(智能引用代理)、缓存代理(CDN:减少计算和网络延时)、防火墙代理(避免恶意攻击)
分类:
> 动态代理(代理对象在运行时通过反射动态创建:InvocationHandler)
所有的函数调用最终都会经过invoke函数的转发,可实现日志记录、性能统计、拦截、权限控制等(即AOP)
> 静态代理(运行前,代理对象就已经生成)

10. 策略模式

理解:定义功能接口,在子类选择不同的实现,适用于多方案的实施和对比

11.状态模式

理解:状态模式定义好了状态之间的转换,可替代多个条件判断

12.模板方法模式

理解:Hook,定义好算法框架,子类可在不改变算法结构的情况下,重新定义算法里的某些步骤

对比:策略模式是继承的一个体现,子类实现整个算法;模板方法是实现算法框架

13. 原型模式

序列化是深拷贝的一种体现
–> 序列化如果不是深拷贝,捣鼓出个对象引用能干啥

14.享元模式

池化支持重用大量的细粒度对象

二、相似模式对比

  1. 装饰器模式 VS 代理模式:装饰器是增强功能;代理模式是控制访问
  2. 桥接模式 VS 中介模式:中介模式类似注册管理中心,以调停者为中心形成星状结构;桥接模式主要是隔离多个维度的变化
  3. 外观模式 VS 中介模式:外观是对外封装统一的高层接口,中介者避免多个互相协作的对象直接引用,松耦合
  4. 策略模式 VS 状态模式:策略模式重在算法的替换,状态模式重在通过状态改变行为

三、最佳实践

1. Android+JDK中的实践

  1. 模版方法:Activity生命周期、AbstractList(其实抽象类基本上都会用)
  2. 装饰者:ContextWrapper内部有个Context引用mContext
  3. 解释器:Manifest中定义Activity,PMS作为解释器解释
  4. 备忘录:Activity的onSaveInstanceState和onRestoreInstanceState,用于保存和恢复、序列化
  5. 建造者:AlertDialog、StringBuilder
  6. 观察者:ListView的Adapter,里面的nofityDataSetChanged()
  7. 代理:AIDL(Binder,BBinder和BPBinder的关系 --> 从Binder架构图细化进去)、动态代理
  8. 中介者:Binder机制中的ServiceManager和Binder驱动(中介者对象是将系统从网状结构转为以调停者为中心的星型结构,类似注册管理中心)
  9. 适配器:Adapter、Arrays.asList()
  10. 命令:Handler模式是典型的命令模式:Looper不知道Message的实现细节,只管调用它执行/线程池处理execute也沾点边
  11. 原型:Intent.clone(),Object.clone()
  12. 责任链:有序广播 & 事件分发(体现在onTouchEvent返回true消费事件)
  13. 策略:动画中的插值器、Comparator的比较器
  14. 组合:View和ViewGroup都实现了View接口
  15. 享元:Message.obtain()->对象池,常量池(char是0 ~ 127,其他是-127 ~ 128)、线程池
  16. 单例+静态工厂:getSystemService(),根据传入的参数决定返回哪个单例对象
  17. 抽象工厂:Service的onBind方法可以看成是一个工厂方法,从framework角度Service可以当做一个具体工厂
  18. 状态:Wi-Fi的状态
  19. 迭代器:SQLiteDatabase的Cursor
  20. 访问者:编译期注解(APT)
  21. 装饰:ContextWrapper、Reader/Writer、Collections转线程安全的类
  22. 外观:如startActivty、sendBroadcast、bindService等等,内部实现非常复杂
  23. 桥接:View的描述维护和绘制到屏幕上桥接(Display、HardwareLayer和Canvas)
  24. 简单工厂/静态工厂方法:DateFormat
  25. 工厂方法:Collection(抽象工厂)、ArrayList/LinkedList(具体工厂)、Iterator(抽象产品)、(ListItr/Itr)具体产品

2. 开源中的实践

  1. OkHttp3:Builder创建OkHttpClient、Request、Response等、责任链实现拦截器
  2. Retrofit2:动态代理解析请求的接口参数(HttpServiceMethod),CallAdapter适配不同平台的OkHttpCall、有必要则提供Converter选择不同的解析器(策略模式)、接口的解析可以认为是解析器模式,Builder、工厂、外观也有体现
  3. Glide:into这么方便,里面实现这么复杂,就必须提一下外观模式了、常规Builder、可配置的缓存策略(策略模式)、RequestManagerRetirever(猎犬)的applicationManager是双加锁的单例
  4. Gson:静态工厂创建各种类型的解析适配器,并缓存(适配器+静态工厂),毕竟是个序列化框架,read/write自带原型模式
  5. EventBus:观察者模式、注册中心(总线)唯一(单例模式)、根据不同情况选择不同的Poster(策略)
  6. Dagger2:利用工厂模式实现依赖注入
  7. RxJava2:观察者模式、如map操作符将ObservableCreate包装成ObservableMap(装饰)

3. 思维发散

  1. 端口复用技术、SocketServer的port映射多个fd来绑定多个连接(select/poll/epoll获取数据),这种构成星型结构的,可用理解为中介者模式
  2. 网络的自治系统,内部自行管理,对外提供一个出入口,可用理解为外观模式
  3. 代码的坏味道:

    1.代码重复;
    2.方法过长;
    3.类提供的功能太多;
    4.数据泥团(数据成批出现在多个对象中,此时需要封装成类);
    5.冗余类(不干啥活,也没啥意义,却多了个对象);
    6.需要太多注释(代码解释不通)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值