分类
设计模式共分为三种类型:创建型、结构型、行为型
- 创建型:用于创建对象,为设计类实例化新对象提供指南
- 结构型:用于处理类或对象的组合,对类如何设计以形成更大的结构提供指南
- 行为型:用于描述类或对象的交互以及职责的分配,对类之间交互以及分配职责的方式提供指南
定义来自权威的软考
常见的设计模式有如下几种
创建型:构建者、单例、工厂
结构型:适配器、代理、外观
行为型:策略、观察者、责任链
正文
Builder构建者
-
场景
用于生成对象的类其内部结构过于复杂,为屏蔽类的复杂性,需要将类的构建和表示进行分离,用最少的参数生成对象 -
例子
OkHttpClient
产生实际对象的必需参数是从builder对象中取出的
即使什么参数也不传Builder也会生成默认参数,以支持对象的生成
Builder每次添加参数时,记得返回Builder对象,用以链式的传递
这是OkHttpClient对象传递参数的精妙之处,源码内多处使用Singleton单例
- 场景
保证实例是唯一的存在,以保证操作的唯一性
单例模式的实现有两种:饱汉、饿汉模式
联想记忆:饱汉不饿,需要的时候再吃;饿汉很饥,提前抢着吃
饱汉其实是懒加载,不必提前占内存
- 例子
//饱汉模式
public class SingletonClass{
private static volatile SingletonClass instance=null;//静态变量保证对象的唯一性,volatile关键字防止指令重排
public static SingletonClass getInstance(){//使用内部加锁,避免每次调用时无谓的加锁,提升了性能
if(instance==null){//指令重排的话,其他线程看到instance不空,则不会进入,也不会加锁,直接返回半成品instance,从而导致异常
synchronized(SingletonClass.class){//锁对象最好是类对象(个人观点)
if(instance==null)//再判断一遍,避免多个线程在上一行同时被锁住,释放后进入,再次实例化对象
instance=new SingletonClass();//指令重排发生地
}
}
return instance;
}
private SingletonClass(){//私有构造方法,防止实例化
}
}
//饿汉模式
public static class Singleton{//静态类直接进入方法区常量池
private static final Singleton instance = new Singleton();//静态常量进入方法区常量池
private Singleton(){//私有构造方法,防止实例化
}
public static Singleton getInstance(){//静态方法
return instance;
}
}
饿汉模式,由于事先分配内存空间不存在饱汉的多线程问题,但其常驻内存会造成开销问题,不同的场景需采用不同的手段以保证消耗与效率的平衡。
Factory工厂
-
场景
多态场景下,用以管理生成不同的对象,并且耦合度不能够过高 -
例子
简单工厂模式
public interface Phone {//Phone是产品抽象
}
public class Honor implements Phone{//Honor是具体的产品
}
public class Mate20 implements Phone{//Mate20是具体的产品
}
public class HuaWei {//具体的厂商
public static Phone create(String str){//厂商根据要求生产手机
if(str.equalsIgnoreCase("honor")){
return new Honor();
}
else if(str.equalsIgnoreCase("mate20")){
return new Mate20();
}
return null;
}
}
工厂方法模式
public interface Phone {//Phone是产品抽象
}
public interface Factory {//Factory是厂商抽象
public Phone create();
}
public class HWPhone implements Phone{//华为手机
}
public class MPhone implements Phone{//小米手机
}
public class HuaWei implements Factory {//华为产华为手机
public Phone create(){
return new HWPhone();
}
}
public class XiaoMi implements Factory {//小米产小米手机
public Phone create(){
return new MPhone();
}
}
简单工厂模式:一个工厂类,工厂生产各种产品
工厂方法模式:一个工厂接口,多个工厂类,不同工厂生产不同产品
工厂方法模式的改进在于,减轻了简单工厂模式中工厂类的复杂度,具体的产出交由具体的工厂类,降低了耦合度。
抽象工厂模式
public interface Phone {//Phone是产品抽象
}
public interface OS {//OS是产品抽象
}
public interface Factory {//Factory是厂商抽象
public Phone create();
public OS develop();
}
public class HWPhone implements Phone{//华为手机
}
public class MPhone implements Phone{//小米手机
}
public class MIUI implements OS{//系统
}
public class EMUI implements OS{//系统
}
public class HuaWei implements Factory{//华为产华为手机
public Phone create(){
return new HWPhone();
}
public OS develop(){
return new EMUI();
}
}
public class XiaoMi implements Factory{//小米产小米手机
public Phone create(){
return new MPhone();
}
public OS develop(){
return new MIUI();
}
}
抽象工厂模式较工厂方法模式的区别在于,不生产单一产品,使工厂能够生产不同类型的相关产品,从而提高扩展性
在Retrofit中
使用的是工厂方法模式,将工厂抽象
初始化阶段,将实例化的不同工厂放在List中,根据参数使用情景的不同,再在List中取出合适的工厂进行处理。
Adapter适配器
-
场景
已有类的功能不满足需求,需将功能进行转换,以达到匹配 -
例子
类适配器模式
原有类并不具备某功能,通过创建Adapter类,继承原有功能、实现目标功能接口,从满足功能要求
public class Source {//现有类
public void method1() {
System.out.println("this is original method!");
}
}
public interface Targetable {//目标功能
public void method1();
public void method2();//缺失功能
}
public class Adapter extends Source implements Targetable {
@Override
public void method2() {//补足缺失
System.out.println("this is the targetable method!");
}
}
对象适配器模式
不同于类适配器模式,通过继承、实现这种高耦合手段,以满足功能要求。对象适配器模式采用的是聚合的手段,创建新类Wrapper通过持有原类对象具备原有功能外;并且类Wrapper实现功能接口补足缺失功能
如OkHttpClient中的Cache类
在OkHttpClient中缓存拦截器一层使用的是InternalCache,见注释,官方已经不建议使用此类改用Cache
Cache类本身并没有实现InternalCache接口,而是持有一个InternalCache对象,且对象内的方法都是调用Cache内的
接口适配器模式
不同于类适配器模式、对象适配器模式的补足功能,接口适配器模式恰恰相反:不暴露功能。
当接口方法过多,而我们关心的却寥寥可数时,通过实现接口的方式就会空实现很多不必要的方法。通过,创建新类(或抽象类)空实现所有方法,实际使用时继承自新类重写关心的方法,就避免了每次实现方法过多的问题,这种设计的原则是接口隔离,对接口知道的越少越好
如OkHttp中的回调监听EventListener
所有阶段的回调都空实现,当我关心某一阶段的回调时,就重写对应方法,否则20个回调方法每添加一次监听就都要实现一遍,代码的易读性也不好
Proxy代理
代理分静态代理和动态代理,这里列举的是静态代理
-
场景
原有类的功能需要进行扩充 -
例子
public interface Sourceable {
public void method();
}
public class Source implements Sourceable {//原类
@Override
public void method() {
System.out.println("the original method!");
}
}
public class Proxy implements Sourceable {//代理类
private Source source;
public Proxy(){
super();
this.source = new Source();
}
@Override
public void method() {
before();
source.method();
atfer();
}
private void atfer() {
System.out.println("after proxy!");
}
private void before() {
System.out.println("before proxy!");
}
}
原类与代理类都实现了相同的接口,方法的实际执行是由代理对象触发的,只不过代理对象在方法触发前后增加了其他功能。
有点和对象适配器模式类似都实现相关接口、持有原类对象,但
- 对象适配器模式是为了弥补功能上的不足
- 代理模式是给功能进行加强
一个是弥补功能,一个是加强功能这是两种模式的区别
从上来看对象适配器模式中的Cache例子放错位置了,它应该属于静态代理模式,Cache是被代理类,InternalCache是代理类
Facade外观
-
场景
多个类间有依赖关系时,由多个类组合创建新类 -
例子
OkHttpClient和Retrofit类就是明显的外观模式,其内部由多个功能不同的类对象组成,外部的单一操作实际影响内部多个对象间的联动。
Strategy策略
-
场景
相同的方法,根据不同的情景有不同的实现 -
例子
以前做过比特币的自动交易软件,实际操作就两种:买、卖,但是针对不同的行情、手上的持仓和现金情况,买和卖就变得有学问了:
行情好的时候采用激进策略,大胆买卖;
行情差的时候就要高抛低吸,慢慢来;
Observer观察者
-
场景
某一对象状态发生变化时,其他对象需要及时的告知 -
例子
android中的回调就是观察者模式,简单点的:给一个button设置onClickListener,此时观察者就是onClickListener被观察者是button,setOnClickListener是给两者创建关联,当被观察者button被单击,观察者onClickListener就会作出回应。
Chain of Responsibility责任链
-
场景
问题一次得不到解决,需要层层处理并传递,每次将任务细化 -
例子
最有名的当数OkHttp,其中的链是环形链
有些自己用的比较熟的或没太多重点要记录的就没有贴例子,一般这种模式比较常见且简单
网友评论