Builder 模式
Builder 模式是在当创建复杂对象的算法应该独立于该对象的组成部分以及他们的装配方式时使用的模式。使得同样的构建过程可以创建不同的表示。
UML

在 Android 中体现最多的用处就是使类的构建和表示分离,同时也是将一个类的配置从目标类中分离出来。避免了过多的 set 方法,目标类被过多的暴露出的接口”污染“。
简化
通常我们可以省去 Director,直接在抽象父类 Builder 中 创建一个 build方法调用构建步骤。可以是静态的方法(在静态方法中创建对象),也可以是非静态的(直接 new 创建对象调用 build 方法)。
总结
建造者模式的核心在于如何一步步构建一个包含多个组成部件的完整对象,使用相同的构建过程构建不同的产品,在软件开发中,如果我们需要创建复杂对象并希望系统具备很好的灵活性和可扩展性可以考虑使用建造者模式。
1.主要优点
(1) 在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
(2) 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。由于指挥者类针对抽象建造者编程,增加新的具体建造者无须修改原有类库的代码,系统扩展方便,符合“开闭原则”
(3) 可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
2.主要缺点
(1) 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,例如很多组成部分都不相同,不适合使用建造者模式,因此其使用范围受到一定的限制。
(2) 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,增加系统的理解难度和运行成本。
3.适用场景
(1) 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。
(2) 需要生成的产品对象的属性相互依赖,需要指定其生成顺序。
(3) 对象的创建过程独立于创建该对象的类。在建造者模式中通过引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类和客户类中。
(4) 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
参考文献:
观察者模式
发布-订阅模式。
定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。
常见的用例:ListView 中 Adapter.notifySetDataChange。 EventBus。
BoradcastReceiver 也是经典的发布-订阅模式。
优点:
观察者和被观察者之间是抽象耦合,应对业务变化。 增强紫铜灵活性,可扩展性。
缺点:
由于是一对多的关系,开发调试可能会比较困难。 Java 模式是顺序执行,一个观察者卡顿,会影响整体的执行效率。这种情况,一般可用异步方式。
外观模式
外观模式是一种使用频率非常高的结构型设计模式,它通过引入一个外观角色来简化客户端与子系统之间的交互,为复杂的子系统调用提供一个统一的入口,降低子系统与客户端的耦合度,且客户端调用非常方便。
为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
class SubSystemA{public void MethodA(){//业务实现代码}}class SubSystemB{public void MethodB(){//业务实现代码}}class SubSystemC{public void MethodC(){//业务实现代码}}
class Facade{private SubSystemA obj1 = new SubSystemA();private SubSystemB obj2 = new SubSystemB();private SubSystemC obj3 = new SubSystemC();public void Method(){obj1.MethodA();obj2.MethodB();obj3.MethodC();}}
class Program{static void Main(string[] args){Facade facade = new Facade();facade.Method();}}
适配器模式
兼容不符合需求的接口,从而使其能够组合在一起工作。
分为:类适配器、对象适配器。
类适配器

Target 就是我们所需要的接口,Adaptee 使我们实际提供的类,显然没有我们所需要的接口 opratioon1()、opratioon2()。那么创建一个中间类 Adapter 来进行兼容。
Adapter 继承 Adaptee 类,实现了 Target 接口。此时 Adapter 既有 Target 里的接口,又有原本类 Adaptee 的接口。符合需求。
置于如何Adapter 的 opratioon1()、opratioon2() 函数内的具体逻辑如何实现,就要根据具体需求去操作父类 Adaptee 和 函数opratioon3() 去实现了。
对象适配器

和类适配器相似,不同的是,Adapter 不去继承 Adaptee 类,而是内部创建一个 Adaptee 的实例对象。然后去兼容实现 Target 接口。
对象适配器其实更加的灵活,而且由于是组合的形式,对于外界来说只会暴露需要的函数,而不是将 Adaptee 的函数也暴露出来。更加符合迪米特法则。减少了用户的使用成本。
而且类适配器由于是通过继承来实现,是无法实现同时适配多个类的。
列表的 Adapter
对于 ListView、RecyclerView 来说,Adapter 是我们来根据不同数据、不同布局 layout 来实现各种样式页面的入口。
对于列表来说,需要展示每一个 itemView,而对于实际业务来说,itemView 各不相同,这时候就通过适配器来进行兼容,同时在适配 itemView 的时候,也适配了不同数据的显示。
总结
优点:适配器模式很好的体现了固定逻辑的复用性,通过兼容不同类体现了更好的扩展性。
缺点:适配器增加了逻辑的复杂性,由于做了兼容,使得调用目标函数的时候,其内部实现往往调用了另一个类的函数,不容易追踪问题。对于本身变化少的功能来说过度的使用了适配器说明其类本身在设计的时候就没有满足需求。
工厂模式
简单工程模式
简单工厂其实不是一个标准的的设计模式。GOF 23种设计模式中只有「工厂方法模式」与「抽象工厂模式」。简单工厂模式可以看为工厂方法模式的一种特例,为了统一整理学习,就都归为工厂模式。
简单工厂模式包含 3 个角色(要素):
- Factory:即工厂类, 简单工厂模式的核心部分,负责实现创建所有产品的内部逻辑;工厂类可以被外界直接调用,创建所需对象
- Product:抽象类产品, 它是工厂类所创建的所有对象的父类,封装了各种产品对象的公有方法,它的引入将提高系统的灵活性,使得在工厂类中只需定义一个通用的工厂方法,因为所有创建的具体产品对象都是其子类对象
- ConcreteProduct:具体产品, 它是简单工厂模式的创建目标,所有被创建的对象都充当这个角色的某个具体类的实例。它要实现抽象产品中声明的抽象方法
实例
工厂模式的简化

将抽象产品类和工厂类合并,将静态工厂方法转移至抽象产品类中。客户端可通过产品父类的静态工厂方法,根据参数的不同创建不同类型对象。
工厂方法模式
工厂方法模式包含 4 个角色(要素):
- Product:抽象产品,定义工厂方法所创建的对象的接口,也就是实际需要使用的对象的接口
- ConcreteProduct:具体产品,具体的Product接口的实现对象
- Factory:工厂接口,也可以叫 Creator(创建器),申明工厂方法,通常返回一个 Product 类型的实例对象
- ConcreteFactory:工厂实现,或者叫 ConcreteCreator(创建器对象),覆盖 Factory 定义的工厂方法,返回具体的 Product 实例
抽象工程模式
抽象工厂模式包含的角色(要素):
- AbstractFactory:抽象工厂,用于声明生成抽象产品的方法
- ConcreteFactory:具体工厂,实现抽象工厂定义的方法,具体实现一系列产品对象的创建
- AbstractProduct:抽象产品,定义一类产品对象的接口
- ConcreteProduct:具体产品,通常在具体工厂里,会选择具体的产品实现,来创建符合抽象工厂定义的方法返回的产品类型的对象。
- Client:客户端,使用抽象工厂来获取一系列所需要的产品对象
抽象工厂模式适用场景
抽象工厂模式和工厂方法模式一样,都符合开闭原则。但是不同的是,工厂方法模式在增加一个具体产品的时候,都要增加对应的工厂。但是抽象工厂模式只有在新增一个类型的具体产品时才需要新增工厂。也就是说,工厂方法模式的一个工厂只能创建一个具体产品。而抽象工厂模式的一个工厂可以创建属于一类类型的多种具体产品。工厂创建产品的个数介于简单工厂模式和工厂方法模式之间。
在以下情况下可以使用抽象工厂模式:
- 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。
- 系统中有多于一个的产品族,而每次只使用其中某一产品族。
- 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。
-
抽象工厂模式总结
抽象工厂模式是工厂方法模式的进一步延伸,由于它提供了功能更为强大的工厂类并且具备较好的可扩展性,在软件开发中得以广泛应用,尤其是在一些框架和API类库的设计中,例如在Java语言的AWT(抽象窗口工具包)中就使用了抽象工厂模式,它使用抽象工厂模式来实现在不同的操作系统中应用程序呈现与所在操作系统一致的外观界面。抽象工厂模式也是在软件开发中最常用的设计模式之一。
优点: 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易,所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。
- 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。
- 增加新的产品族很方便,无须修改已有系统,符合“开闭原则”。
缺点:
增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了“开闭原则”。
工厂模式优点
- 可以使代码结构清晰,有效地封装变化。在编程中,产品类的实例化有时候是比较复杂和多变的,通过工厂模式,将产品的实例化封装起来,使得调用者根本无需关心产品的实例化过程,只需依赖工厂即可得到自己想要的产品。
- 对调用者屏蔽具体的产品类。如果使用工厂模式,调用者只关心产品的接口就可以了,至于具体的实现,调用者根本无需关心。即使变更了具体的实现,对调用者来说没有任何影响。
- 降低耦合度。产品类的实例化通常来说是很复杂的,它需要依赖很多的类,而这些类对于调用者来说根本无需知道,如果使用了工厂方法,我们需要做的仅仅是实例化好产品类,然后交给调用者使用。对调用者来说,产品所依赖的类都是透明的。
适用场景
不管是简单工厂模式,工厂方法模式还是抽象工厂模式,他们具有类似的特性,所以他们的适用场景也是类似的。
首先,作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过new就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
其次,工厂模式是一种典型的解耦模式,迪米特法则在工厂模式中表现的尤为明显。假如调用者自己组装产品需要增加依赖关系时,可以考虑使用工厂模式。将会大大降低对象之间的耦合度。
再次,由于工厂模式是依靠抽象架构的,它把实例化产品的任务交由实现类完成,扩展性比较好。也就是说,当需要系统有比较好的扩展性时,可以考虑工厂模式,不同的产品用不同的实现工厂来组装。
参考文章
命令模式
将一个请求封装为一个对象,从而让我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务(Transaction)模式。
UML图
示例
class Receiver {fun action() {println("具体执行逻辑")}}interface ICommand {fun execute();}class ConcreteCommand(private var receiver: Receiver) : ICommand {override fun execute() {receiver.action()}}class Invoker(private val command: ICommand) {//通过构造方法持有一个具体命令对象引用fun invoke() {//执行一个命令command.execute()}}fun main() {//构造一个接受者对象val receiver = Receiver()//构造一个命令对象val command = ConcreteCommand(receiver)//构造一个请求者对象val invoker = Invoker(command)invoker.invoke()}
优点
- 能容易的设计一个命令队列。
- 在需要的情况下,可以竟命令记录日志。
- 允许接收请求方决定是否要否决请求。
- 容易实现队请求的撤销和重做。
- 能更容易添加新的具体命令类,而不影响其它的类。
- 命令模式把请求操作的对象和执行操作对象分隔开,更弱的耦合性、更灵活的控制性、更好的扩展性。
缺点
造成类的膨胀、大量衍生类的创建。责任链模式
拥有多个节点,每个节点都持有下一个节点的引用,每个节点可以看做是一个对象,每个对象的处理逻辑不同。一个操作请求从首节点开始,沿着链的路径依次传递给每一个节点对象,直到有对象处理这个请求为止。
在软件开发中,如果遇到有多个对象可以处理同一请求时可以应用职责链模式。
UML
在 Android 中的应用
- View 的事件分发机制,从顶层的 View 开始,一直往下分发,直到有 View 处理这个事件,或者这个 View 树都没有 View 处理这个事件。
有序广播的分发。有序广播是根据优先级依次传播的,知道有接受者将其终止或所有的接受者都不终止它。参考文章
优点
接收者和发送者都没有对方的明确信息,且链中的对象自己也并不知道链的结构。他们仅需保持一个指向后继者的引用,不需要保持它所有的候选接收者的引用,大大降低了耦合度。
缺点
责任链最大的缺点是对链的遍历,如果节点过多,势必会影响性能。特别是递归调用中,会使递归深度很大,要慎重。
- 由于一个请求没有明确的接收者,可能会导致这个请求一直到链的末端都没有被执行。
- 如果建链不当,可能会造成循环调用,从而导致死循环。
代理模式
不直接访问一个类,而是通过另一个类去间接访问。代理者内部创建被代理者的实例,两个类都实现了业务需求的接口。调用代理类业务接口的时候,实际上是通过代理类内部调用被代理类的接口。
Subject 就是业务需求接口,被代理类 RealSubject,代理类 ProxySubject。
其中一个应用场景:例如显示通知栏,在 Android 不同版本、不同手机厂商有不同的显示方式。我们可以抽象出一个通知栏显示的接口,根据版本、手机厂商创建不同的类去实现具体的显示逻辑。然后再创建一个代理类也实现这个接口,并创建具体显示通知的类的实例。然后根据不同的版本、系统去调用不同的实例。
public interface INotification{void send();void notify();}public class Version1 implement INotification{@Overridepublic void send(){...}@Overridepublic void notify(){...}}public class Version2 implement INotification{@Overridepublic void send(){...}@Overridepublic void notify(){...}}public class System1 implement INotification{@Overridepublic void send(){...}@Overridepublic void notify(){...}}public class Proxy implement INotification{Version1 version1;Version2 version2;System1 system1;public Proxy(){version1 = new Version1();version2 = new Version2();system1 = new System1();}@Overridepublic void send(){if(...){version1.send();}else if(...){version2.send();}else{system1.send();}}@Overridepublic void notify(){...}}
代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。代理模式本质上的目的是为了增强现有代码的功能。
静态代理
我们自己编写的一些固定的代理类。在代码运行前,这些类就存在。代理类需要自己编写代码写成。
动态代理
运行期间通过反射动态生成代理者对象。代理类通过 Proxy.newInstance() 方法生成。
JDK 动态代理、cglib 代理
装饰模式
对原有对象的扩展,不使用继承而是使用持有原有对象的引用,对所装饰的对象功能增强。
UML

和代理模式不同的是,装饰模式是对已有对象功能的增强,代理模式是代替已有对象执行功能。
组合模式
将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。一组相似的对象看做一个对象处理。
UML

其经典的应用是:
- 文件和文件夹系统
- View 和 ViewGroup 组合成的树型结构
优点
组合模式可以清楚的定义分层次的复杂对象,表示对象的全部或部分层次,它让高层模块忽略层次的差异,方便对整个层次结构进行控制。
高层模块可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合机构,简化了高层模块代码。
组合模式中新增新的枝干或叶子构件都很方便,无需对库进行修改,符合“开闭原则”。
缺点
不容易限制容器中的构件类型。有时候我们希望一个容器中只能有某些特定类型的对象,例如在某个文件夹中只能包含文本文件,使用组合模式时,不能依赖类型系统来施加这些约束,因为它们都来自于相同的抽象层,在这种情况下,必须通过在运行时进行类型检查来实现,这个实现过程较为复杂。
策略模式
定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换,策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。策略模式是一种对象行为型模式。
结构图

- Context(环境类):环境类是使用算法的角色,它在解决某个问题(即实现某个方法)时可以采用多种策略。在环境类中维持一个对抽象策略类的引用实例,用于定义所采用的策略。
- Strategy(抽象策略类):它为所支持的算法声明了抽象方法,是所有策略类的父类,它可以是抽象类或具体类,也可以是接口。环境类通过抽象策略类中声明的方法在运行时调用具体策略类中实现的算法。
- ConcreteStrategy(具体策略类):它实现了在抽象策略类中声明的算法,在运行时,具体策略类将覆盖在环境类中定义的抽象策略类对象,使用一种具体的算法实现某个业务处理。
优点
(1) 策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
(2) 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族,恰当使用继承可以把公共的代码移到抽象策略类中,从而避免重复的代码。
(3) 策略模式提供了一种可以替换继承关系的办法。如果不使用策略模式,那么使用算法的环境类就可能会有一些子类,每一个子类提供一种不同的算法。但是,这样一来算法的使用就和算法本身混在一起,不符合“单一职责原则”,决定使用哪一种算法的逻辑和该算法本身混合在一起,从而不可能再独立演化;而且使用继承无法实现算法或行为在程序运行时的动态切换。
(4) 使用策略模式可以避免多重条件选择语句。多重条件选择语句不易维护,它把采取哪一种算法或行为的逻辑与算法或行为本身的实现逻辑混合在一起,将它们全部硬编码(Hard Coding)在一个庞大的多重条件选择语句中,比直接继承环境类的办法还要原始和落后。
(5) 策略模式提供了一种算法的复用机制,由于将算法单独提取出来封装在策略类中,因此不同的环境类可以方便地复用这些策略类。缺点
(1) 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
(2) 策略模式将造成系统产生很多具体策略类,任何细小的变化都将导致系统要增加一个新的具体策略类。
桥接模式
桥接模式是一种很实用的结构型设计模式,如果软件系统中某个类存在两个独立变化的维度,通过该模式可以将这两个维度分离出来,使两者可以独立扩展,让系统更加符合“单一职责原则”。与多层继承方案不同,它将两个独立变化的维度设计为两个独立的继承等级结构,并且在抽象层建立一个抽象关联,该关联关系类似一条连接两个独立继承结构的桥,故名桥接模式。
桥接模式用一种巧妙的方式处理多层继承存在的问题,用抽象关联取代了传统的多层继承,将类之间的静态继承关系转换为动态的对象组合关系,使得系统更加灵活,并易于扩展,同时有效控制了系统中类的个数。
桥接定义如下:
桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。
实际例子
假如我们需要大中小3种型号的画笔,能够绘制12种不同的颜色,如果使用蜡笔,需要准备3×12 = 36支,但如果使用毛笔的话,只需要提供3种型号的毛笔,外加12个颜料盒即可,涉及到的对象个数仅为 3 + 12 = 15,远小于36,却能实现与36支蜡笔同样的功能。如果增加一种新型号的画笔,并且也需要具有12种颜色,对应的蜡笔需增加12支,而毛笔只需增加一支。为什么会这样呢?通过分析我们可以得知:在蜡笔中,颜色和型号两个不同的变化维度(即两个不同的变化原因)融合在一起,无论是对颜色进行扩展还是对型号进行扩展都势必会影响另一个维度;但在毛笔中,颜色和型号实现了分离,增加新的颜色或者型号对另一方都没有任何影响。如果使用软件工程中的术语,我们可以认为在蜡笔中颜色和型号之间存在较强的耦合性,而毛笔很好地将二者解耦,使用起来非常灵活,扩展也更为方便。
UML
优点
- 分离抽象接口及其实现部分。桥接模式使用“对象间的关联关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度变化。所谓抽象和实现沿着各自维度的变化,也就是说抽象和实现不再在同一个继承层次结构中,而是“子类化”它们,是他们各自具有自己的子类,以便任何组合子类,从而获得多维度组合对象。
- 在很多情况下,桥接模式可以取代多层继承方案,多层继承方案违背了“单一职责原则”,复用性较差,且类的个数非常多,桥接模式是比多层继承方案更好的解决方法,它极大减少了子类的个数。
桥接模式提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统,符合“开闭原则”。
缺点
桥接模式的使用会增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就针对抽象层进行设计与编程。
- 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性,如何正确识别两个独立维度也需要一定的经验积累
Demo
两个形状:圆形和矩形。两个颜色:红色和黑色
颜色 ```java public interface Color { void setColor(); }
public class BlackColor implements Color { @Override public void setColor() { System.out.print(“设置黑色”); } }
public class RedColor implements Color{ @Override public void setColor() { System.out.print(“设置红色”); } }
形状```javapublic abstract class Shape {protected Color color;public void setColor(Color color) {this.color = color;}abstract void draw();}public class CircleShape extends Shape{@Overridevoid draw() {color.setColor();System.out.println(" 画圆形");}}public class RectangleShape extends Shape{@Overridevoid draw() {color.setColor();System.out.println(" 画矩形");}}
客户端代码
public class BirdgePattern {public static void main(String[] args) {Shape circle = new CircleShape();Color red = new RedColor();circle.setColor(red);circle.draw();}}//-----------------------Log-----------------------设置红色 画圆形

