什么是设计模式
模式是在某情景下,针对某问题的某种解决方案。
模式必然应用于一个重复出现的问题。问题包括了一个目标和一组约束。
模式是被“发现”的,而不是被“创建”的。
良好的设计模式必须具备可复用、可扩充、可维护三个特性。
模式分类
设计模式分为创建型、行为型和结构性。
-
创建型
涉及到将对象实例化,这类模式都提供一个方法,将客户从所需要实例化的对象中解耦。包括:
工厂模式,抽象工厂模式,单例模式,生成器模式,原型模式。 -
行为型
都涉及到类和对象如何交互及分配职责。包括:
模版模式,命令模式,迭代器模式,观察者模式,状态模式,策略模式,访问者模式,中介者模式 ,备忘录模式,责任链模式,翻译者模式。 -
结构型
可以让你把类或对象组合到更大的结构中。用来描述类和对象如何组合以建立新的结构或新的功能。包括:
装饰者模式,组合模式,适配器模式,代理模式,外观模式,桥接模式,蝇量模式,
另一种分类方法将设计模式分为:处理类的模式,处理对象的模式。
-
类
模板模式,适配器模式,工厂模式,翻译者模式 -
对象
装饰者模式,组合模式,代理模式,外观模式,桥接模式,蝇量模式,命令模式,观察者模式,状态模式,策略模式,访问者模式,中介者模式 ,备忘录模式,责任链模式,抽象工厂模式,单例模式,生成器模式,原型模式。
设计原则
-
找到应用中可能需要变化之处,把它们独立出来,不要和那些需要变化的代码混在一起
所有的设计模式都提供一套方法让系统中的某部分改变不会影响其他部分 -
针对接口编程,而不是针对实现编程
利用接口代表每个行为,行为的实现不会影响具体的使用该行为的对象。客户可以利用接口(而不是具体类)引用每个具体对象,减少了客户和具体类之间的依赖。 -
多用组合,少用集成
has-a比is-a具有更大的弹性和松耦合。JAVA中类的继承会限制复用潜力。 -
尽力实现交互对象之间的松耦合。
-
类应该对扩展开放,对修改关闭。
在不修改基础不变类的代码的情况下进行功能的扩展。 -
要依赖抽象,不要依赖具体类(依赖倒置原则)
不能让高层组件依赖底层组件。而且高层或底层组件,都应该依赖于抽象。
倒置指的是原本高层依赖于低层,现在高低层都依赖中间抽象层。在设计中尽量避免依赖。 -
好莱坞原则
别调用(打电话)我们,我们会调用(打电话给)你。
高层组件控制何时以及如何让底层组件参与,底层组件绝对不可以直接调用高层组件,底层组件可以参与计算。高层和底层有点类似与模版模式里的父类和子类,目的是不让高层组建依赖低层组件,但是低层组件能够被倒钩进计算里。 -
单一原则
一个类应该只有一个引起变化的原因。 -
保持简单(Keep It Simple)
模式常常会产生一些额外的类和对象,加入更多层,所以可能会引入复杂性并降低效率,你的目标应该是简单,而不是“如何在这个问题中应用模式”。学习管理软件的复杂度和变化,是程序员一生的课题。
模式是工具而不是规则,需要被适当地调用以符合你的需求。 -
最少知识原则
只和你的密友谈话。减少对象之间的交互。避免接触一个对象时需要注意它所交互的类有哪些,是如何和这些类交互的。缺点是会制造更多的包装类去处理和其它组件的沟通。以下为示例:
/**
*不推荐。从气象站取得温度计(Thermometer)后,再通过温度计对象取得温度。
*/
public float getTemperature() {
Thermometer thermometer = station.getThermometer();
return thermomeer.getTemperature();
}
/**
*推荐使用。将获取温度的操作封装到气象站中做,客户不需要接触过多数量的对象。
*/
pulic float getTemperature() {
return station.getTemperature();
}
具体模式
-
策略模式
定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变换独立于使用算法的客户。适用对象组合的方式让客户可以选择算法实现。可理解成是除了继承之外的一种弹性替代方案,可组合不同对象来改变行为。 -
观察者模式
定义为对象之间的一对多依赖,当一个对象改变状态时,他的所有依赖者都会收到通知并自动更新。Android为很多GUI(Graphical User Interface)控件(Button/ViewPager等)添加Listener监听,内部使用了观察者模式。 -
装饰者模式
动态的将责任附加到对象上。想要扩展功能,装饰者提供了比继承更有弹性的替代方案。通常装饰者和被装饰者具有相同的超类型,装饰者在被装饰者的行为之前/之后加上自行的行为进行修饰。I/O的API中用到了装饰者模式:LineNumberInputSrream(BufferedInputStream(FileInputStream(InputSream)))); -
工厂模式
定义了一个创捷对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
简单工厂模式:提供一个工厂负责制造多类型的产品,工厂是全能类
工厂模式:多个工厂各自负责单一类型产品的构建,工厂为单一职责 -
抽象工厂模式
提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。 如果工厂的产品全部属于同一个等级结构则属于工厂模式,如果来自多个等级结构则属于抽象工厂模式。 -
单例模式
确保一个类只有一个实例,并提供一个全局访问点。可以在需要的时候才创建对象。Java中实现单例模式需要私有的构造器、一个静态方法和一个静态变量。没有公开的构造器,只能通过getInstance()
获得单一对象。
JVM在加载类时会立即创建唯一的单例事例。 -
命令模式
将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
[应用] 日程安排,日志及事务请求,线程池,工作队列 -
适配器模式
将一个类的接口转换成客户期望的另一个接口。适配器让原来接口不兼容的类可以合作无间。 -
外观模式
提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。这个模式应用非常之广,比如Retrofit.create()方法就是使用了外观模式。
外观模式/适配器模式/装饰者模式 三者区别:
适配器:将一个对象包装起来以改变其接口
装饰者:将一个对象包装起来以增加新的行为和责任
外观:将一群对象“包装”起来以简化接口
-
模板方法模式
在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模版方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
模版方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。
比如Array的排序需要子类实现comparable接口的compareTo()方法.继承Activity的页面都是用了模版方法。模版方法的抽象类可定义具体方法,抽象方法和钩子(子类可选择是否覆盖该默认实现)。 -
迭代器模式
提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。把元素之间遍历游走的顺序交给迭代器而不是集合对象,让集合更专注在它所应该专注的事情上面。 -
组合模式
将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。
组合包含组件,组件有两种:组合与叶节点元素。类似View及ViewGroup的关系。 -
状态模式
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。将状态封装成为独立的类,将动作委托到代表当前状态的对象。如果使用的对象能够完全改变它的行为,就好像这个对象是从别的类实例化而来的,其实不是。
Context会将行为委托给当起对象。一般来讲,当状态是固定的时候,就适合将状态转换的流向放在context控制类中;然而,当状态是更动态的时候,通常就会把状态转换的流向放在状态类中。扩展:UML类图
就比如播放器MediaPlayer的各种状态都是一个具有行为的对象。 -
代理模式
为另一个对象提供一个替身或占位符以控制对这个对象的访问。使用代理模式创建代表对象,让代表对象控制某对象的访问,被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象。装饰者为对象增加行为。代理是控制对象的访问。
远程代理:管理客户和远程对象之间的交互。远程代理可以作为另一个JVM上对象的本地代表。代用代理的方法,会被代理利用网络转发到远程执行,并且结果会通过网络返回给代理。
虚拟代理:控制访问实例化开销大的对象。虚拟代理作为创建开销大的对象的代表。虚拟代理经常知道我们真正需要一个对象的时候才创建它。当对象再创建前和创建中时,由虚拟代理来扮演对象的替身。对象创建后,代理就会将请求直接委托给对象。
保护代理:也称动态代理。基于调用者控制对象方法的访问。可根据访问权限决定客户可否访问对象的代理。
- 复合模式
复合模式在一个解决方案中结合两个或多个模式,以解决一般或重复发生的问题。复合模式必须具有一般性,适合解决许多问题才行。
MVC
Model-View-Controller(模型-视图-控制)就是复合模式,Android中的典型应用是LiewView:(Model-ListView-Adapter);M层处理数据,业务逻辑等;V层处理界面的显示结果;C层起到桥梁的作用,聪明的将来自试图的动作转换成模型上的动作。
控制器把控制逻辑从视图中分离,减少视图的责任,让模型和视图之间解耦。
模式:
视图和控制器实现了策略模式。视图可以被调整使用不同的控制器提供的策略,任何界面行为都委托给控制器处理,控制器负责和模型交互来传递用户的请求。
视图和模型实现了观察者模式 。模型是被观察者,视图和控制器是观察者。
视图用了组合模式实现GUI。
-
桥接模式
将抽象部分与它的实现部分分离,使它们都可以独立地变化。不止改变你的实现,也改变你的抽象。适合使用在需要跨越多个平台的图形和窗口系统上。当需要用不同的方式改变接口和实现时,桥接模式很好用。 -
生成器模式
封装一个产品的构造过程,并需要按步骤构造。将一个复杂对象的创建过程封装起来,允许对象通过多个步骤来创建,并且可以改变过程。经常被用来创建组合结构。 -
责任链模式
当你想要让一个以上的对象有计划能够处理某个请求的时候,就使用责任链模式。为某个请求创建一个对象链,每个对象一次检查此请求,并对其进行处理,或者将它传给链中的下一个对象。将请求的发送者和接收者解耦。(okHttp, Fresco, RxJava都有使用) -
蝇量模式
如果想让某个类的一个实例能用来提供许多“虚拟实例”,就使用蝇量模式。的那个一个类又许多实例,而这些实例能被同一方法控制的时候,可使用蝇量。可将需要“虚拟”对象的状态集中管理。减少运行时对象实例的个数,节省内存。 -
解释器模式
为语言创建解释器。将每一个语法规则表示成一个类,方便于实现语言。可以处理脚本语言和编程语言。要想解释这种语言,就调用每个表达式类型的interpret()
方法,此方法需要传入一个上下文Context
——也就是我们正在解析的语言字符串输入流——然后进行对比并采取适当的动作。 -
中介者模式
使用中介者模式来集中相关对象之间复杂的沟通和控制方式。每个对象都会在自己的状态改变时,告诉中介者。每个对象都会对中介者所发出的请求作出回应。中介者内包含了整个系统的控制逻辑。通过将对象之间彼此解耦,可以增加对象的复用性。通过将控制逻辑集中,可以简化系统维护。中介者常常被用来协调相关的GUI组件。 -
备忘录模式
存储系统关键对象的重要状态,维护关键对象的封装。用户存储状态。将被储存的状态放在外面,不要和关键对象混在一起,这可以帮助维护内聚。在Java系统中,可以考虑使用序列化(serialization)机制存储系统的状态。Activity及Fragment的saveInstanceState有用到。 -
原型模式
当创建给定的类的实例的过程很昂贵或很复杂时,就使用原型模式。允许你通过复制现有的实例来创建新的实例(在Java中,这通常意味着使用clone()
方法或者反序列方法)。客户的代码在不知道要实例化何种特定类的情况下,可以制造新的实例。在一个复杂的类层次中,但系统必须从其中的许多类型创建新对象时,可以考虑原型。 -
访问者模式
当你想要为一个对象的组合增加新的能力,且封装并不重要时,就使用访问者模式。访问者必须参观组合内的每个元素,在导游traverser
对象中,访问者通过导游的引导,收集组合中所有对象的状态。一旦状态被收集了,客户就可以让访问者对象进行各种操作。允许你对组合结构加入新的操作,而无需改变结构本身。
语法基础
抽象,封装,多态,继承
多态
多态分为编译时多态和运行时多态。其中编辑时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不同的函数,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性。Java实现运动时多态有三个必要条件:继承、重写、向上转型。
牢牢记住每个设计模式是没有必要的,但是这是必经的一步。重要的是,要记住设计模式的思想。先看定义,再看思想。
思想补充
虽然大多数设计模式都会增加类,但是真正重要的是你暴露给使用者的类数目,设计模式能够将额外增加的类隐藏起来。但是这些新增的类有利于业务增长。在设计的过程中,折衷一直都是免不了的。
阅读感受
- 在实际开发中会发现其中的设计模式。
在写了一点代码之后学习设计模式会对这些模式有明确的实例关联,让人有意识的去总结并在之后应用这种模式。比如之前一直觉得哇这个功能类之间的架构好牛逼啊,只知道具体应用了继承重写重载等技巧和具体流程的逻辑,但是真的没有去进一步总结这种模式。 - 帮助开发过程中运用设计模式。
这次看设计模式会有一种总览的感觉,应该会让之后写代码更有设计观一点。这对于开发者理解和运用设计模式有很大帮助。而且这种学习没有阶层限制,但是一定要相信,你在学会一些入门知识以及业务工作后,会到达另一种状态。每个类都不知道其他类的细节,但是多个类实现了一个具体的功能需求,神奇。
网友评论