设计模式
1.Factory pattern(简单工厂模式)
目的:定义一个用于创建对象的接口,此接口可以根据子类需要创建出不同的实例对象。
适用:适用于需要延迟创建对象的场景
优点:使产品的使用和产品的生产分开,客户在使用不同的方法创建出的产品时没有差异,所以在使用的代码流程上无需修改
缺点:每个单独的工厂模式只能生产同一类产品,存在分支判断,由于要保存使用流程的一致,所以创建类时需要抽象出产品中一般的行为
public class JavaProject {
public static void main(String[] args) {
Operation operation = OperationFactory.createOperation("+");
operation.mNumberA = 10;
operation.mNumberB = 20;
System.out.println("Result:" + operation.calcResult());
}
public static abstract class Operation{
int mNumberA;
int mNumberB;
public abstract int calcResult();
}
public static class OperationFactory{
public static Operation createOperation(String operation){
Operation oper = null;
if(operation.equals("+")){
oper = new OperationPlus();
}else if(operation.equals("-")){
oper = new OperationMinus();
}else if(operation.equals("*")){
oper = new OperationMul();
}else if(operation.equals("/")){
oper = new OperationDiv();
}else{
}
return oper;
}
}
public static class OperationPlus extends Operation{
@Override
public int calcResult() {
return (mNumberA + mNumberB);
}
}
public static class OperationMinus extends Operation{
@Override
public int calcResult() {
return (mNumberA - mNumberB);
}
}
public static class OperationMul extends Operation{
@Override
public int calcResult() {
return (mNumberA * mNumberB);
}
}
public static class OperationDiv extends Operation{
@Override
public int calcResult() {
if(mNumberB == 0){
return 0;
}
return (mNumberA / mNumberB);
}
}
}
2.Strategy pattern(策略模式)
目的:用于封装经过抽象的算法,这些算法之间具有可替换性,使算法的使用独立于客户的使用
适用:客户希望同时存在多种算法以便在不同的场景中使用
优点:使算法独立于客户的使用,使用者无需关心处理的细节,也无需增加额外判断该使用哪种算法
缺点:要求不同的算法都处理同一件事情
3.Decorator pattern(装饰模式)
目的:对已有对象动态的添加新的功能,保留对象的核心功能。就扩展性来说,装饰着模式比生成一个子类要简单
适用:当新增的功能只是某个类在特定情况下才会适用或者并非该类的主要功能时,为了避免在该类中新增字段和方法导致类的复杂度上升
优点:把类的装饰功能从类中搬移出去,保留类的核心功能,使原有的类得到了简化
缺点:装饰模式在某些场合下要求有一定的装饰顺序,理想的情况是各个装饰功能彼此独立
4.Proxy pattern(代理模式)
目的:为其他对象提供一个代理以控制对这个对象的访问
适用:
1)远程代理,为不同地址空间的访问提供解决方案
2)虚拟代理:让代理处理开销很大的对象
3)安全代理:提供不同的访问权限
4)智能指引:交给代理一些权限让它在特定的时候完成一些事情。
5.Factory Method(工厂方法)
目的:Factory Method是简单工厂的进一步抽象和推广,只是将简单工厂内部的必要的逻辑判断移到客户端来完成
适用:当项目中每个逻辑判断所要完成的事情较多的时候可以采用此方法将不同的逻辑判断转化成不同的工厂类
优点:克服简单工厂模式违背开放-封闭原则,又保持了将对象的创建过程封装起来的目的
缺点:每增加一种不同的对象时需要新增一个工厂类
6.Prototype (原型模式)
目的:用原型实例来指定创建对象的种类,并通过拷贝来实现对象的创建
适用:当需要创建一批具有相同或大致相同属性的对象(对象的属性较多),一般用实现clone的接口来完成原型拷贝
优点:简化创建对象时不断初始化相同的内容的情况
缺点:要注意深层拷贝和浅层拷贝的问题
7.Template Method(模版方法)
目的:定义出一个算法的骨架,将一些变化的的步骤延迟到子类中去实现
适用:当固定不变的行为与一些可变的行为混在类中实现的时候,可将固定不变的部分抽离到父类中
优点:无需更改整个算法的结构就可以重定义某些算法的步骤
缺点:比较难以界定固定不变的行为和固定的行为,当固定的行为发生变化时会增加了编程/维护的复杂度
8.Facade pattern(外观模式)
目的:为子系统提供一组一致的界面,此模式定义了一个高层接口,这个接口使得这个子系统更加容易使用。
适用:当一个子系统随时间的推移越加复杂的时候可以考虑为此复杂的字系统提供一个简单的接口,以降低子系统与外界的耦合性
优点:模块化设计,降低模组间耦合性,外界无需了解太多子系统的具体实现
9.Builder(建造者)
目的:将一个复杂对象的创建与他的表示分离。使相同的构建过程表示不同的类,其核心特征是抽象出某个复杂对象的各个部分(或者步骤),以保证创建的完整性
适用:当创建复杂的对象时需要将算法独立于该对象的组成部分以及他们的装备方式
优点:使对象的创建与对象的使用隔离,降低耦合性,而且保证了创建对象的各个步骤的完整性
PS: 或许builder 模式与工厂模式有异曲同工之妙,但是builder 的核心在于将复杂对象的的各个部分(或者步骤),以保证创建的完整性,而builder的实现必须依靠抽象类或接口来完成。
10.Observe(观察者)
目的:将一个系统分割成一系列相互协作的模组存在一个副作用,那就是需要维护相同模组之间的一致性。我们不希望为了维护一致性而是各个模组之间紧密耦合,这样会给维护、扩展和重用带来麻烦。观察者模式则是在定义出一个或者多个观察者对象来监听某个主题对象的状态变化,当主题对象发生变化时所有登记过的观察者就会收到消息通知相关的对象。
适用:当一个对象或者模组的状态发生改变时如果需要通知其他对象,
优点:将两个相关联的对象之间的依赖关系抽象出来,在不改变对象实现的情况下实现相关模组间的同步,降低对象之间的耦合
11.Abstract Factory(抽象工厂)
目的:提供一个接口,此接口用来描述一个复杂子系统会用到的所有功能的抽象方法,方便在不同子系统之间切换来适应不同的环境需要
适用:通常用于一个较为复杂的子系统,且此子系统的具体实现方式可能会改变,有时可以理解成对工厂方法的进一步抽象(将不同的工厂集中到一个类中,方便集体被替换)
优点:通过最小的改动来达到更换子系统的目的,使整个子系统的实例化过程与客户端分离,客户通过操纵抽象的工厂接口来选择不同的子系统
PS:注意一次性声明需要的子系统的类型,避免在每个具体的类中去修改
12.State (状态模式)
目的:当控制一个对象状态转移的条件过于复杂的时候,可以将各个状态的判断逻辑转移到不同的类或者方法中。
适用:如果软件要实现的功能分成几个特定的状态(或者说一个对象的行为取决于它的状态),而每个状态要负责的功能过于复杂的时候可以考虑将不同状态下要完成的事情转移到子类或者方法中。
优点:减少过多的判断逻辑(分支语句)集中在一个方法(类)中,使代码的可读性增加,减少相互间的依赖关系
13.Adapter(适配器模式)
目的:将一类接口转换成客户希望另外一类接口,使得原本不兼容的接口可以一同工作
适用:通常用于项目后期对大型的接口维护,当两个接口所完成的事情类似时可以通过适配器的方式将一个接口进行转换以符合客户希望的另一个接口
优点:无需更改现存的类即可实现复用,客户只需调用统一的接口即可
缺点:容易被滥用,适配器模式属于无奈之举,有点像亡羊补牢,开发初期重构要比使用适配器模式要好。
PS: 适配器模式的概念也被扩大化,实际开发中不同环境下数据的表现形式不同,可以适配器负责处理不同数据表现形式之间的转换
14.Memento(备忘录模式)
目的:在不破坏封装性的前提下,将对象的状态在对象之外进行保存,以便将对象恢复到所保存的状态
适用:功能比较复杂且需要记录历史属性的对象
优点:不破坏对象的封装性,将保存对象历史属性的功能放在对象之外,降低耦合性也便于统一的修改
缺点:过多的使用备忘录会耗费较多的系统资源
15.Composite(组合模式)
目的:将对象组合成 部分-整体 的层次结构,使得用户对单个对象或者组合对象的使用具有一致性
适用:需体现部分与整体的层次关系;希望用户忽略组合对象与单个对象在使用上的不同
优点:用户无需确认部分-整体之间的差别,使用上比较统一,可实现批量化处理
16.Iterator(迭代器)
目的:当需要顺序的访问某一聚合对象,又不希望暴露对象的内部
适用:当一个聚合对象需要被一种或多种方式遍历时;多种遍历方式
优点:迭代器的原理就是将对聚合对象的遍历方式抽离出来,让迭代器负责对局和对象的访问,这样既达到透明的访问聚合对象内部,又不会暴露聚合对象。
PS:迭代器的使用已经相当普遍,有的语言(如Java)已经将这种模式做到了语法的内部(如foreach 用法)
17.Singleton(单例模式)
目的:保证一个类仅有一个实例,并提供一个访问它的全局访问点
适用:希望控制用户何时\以何种方式访问一个对象,并希望只存在一个实例的情况,一些工具类通常都是采用了静态的单例模式
优点:采用私有构造方法,实现对象的受控访问,且在只需实例化一个对象的场景,用户在使用上比较方便
缺点:需要考虑多线程下创建对象的线程安全
18.Bridge(桥接模式)
目的:将系统的抽象部分与它的实现部分分离,使他们都可以独立的变化
适用:实现系统可能有多角度分类,每一种分类都有可能变化,主要用于设计初期
优点:只使用继承来实现系统会增加大量的类,且不能满足开放-闭合原则,这个时候将这种多角度分类抽离出来让他们独立的变化,可以减少他们之间的耦合
19.Command(命令模式)
目的:将一个请求封装为一个对象,从而可以用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作
适用:当需要恢复/撤销等操作时,一般是在项目开发过程中发现需求后重构出命令模式
优点:1)比较容易设计一个命令队列;2)方便支持撤销与重做;3)可以较容易的将请求记入日志;4)加入新的命令不会影响其他的对象。实现了将发出请求与如何执行请求分离。
缺点:应避免为代码添加基于猜测的、实际不需要的功能
20.Chain of Responsibility(职责链模式)
目的:使多个对象都有机会处理请求,从而避免请求的发送者与请求的执行者之间的耦合关系。将这些能处理请求的对象练成一条链,并沿这条链传递请求,直到有一个对象处理它为止
适用:职责链是对C语言中单项链表的一种扩展,当你需要灵活的增删改查处理请求的结构时可考虑使用职责链模式
优点:请求的发送者和请求的接收者都没有对方明确的信息,处在链中的对象也并不了解整个链的结构。结果是职责链可以简化对象的相互连接,他们仅需要保存一个后继者的引用
public class JavaProject {
public static void main(String[] args) {
KeyNode keyNodeA = new HotKeyA(0x10);
KeyNode keyNodeB = new HotKeyB(0x20);
KeyNode keyNodC = new HotKeyC(0x30);
keyNodeA.mNextNode = keyNodeB;
keyNodeB.mNextNode = keyNodC;
System.out.println("Process:" + keyNodeA.handKeyCode(0X20));
}
public static abstract class KeyNode{
public static int mNumberCnt = 0;
int mKeyCode;
int mNumber = mNumberCnt++;
KeyNode mNextNode = null;
public KeyNode(int keyCode){
mKeyCode = keyCode;
}
public abstract void operate();
public boolean handKeyCode(int KeyCode){
boolean handle = false;
System.out.println("node number " + mNumber);
if(KeyCode == mKeyCode){
System.out.println("handle key by " + getClass().getName());
handle = true;
}else if(mNextNode != null){
mNextNode.handKeyCode(KeyCode);
}
return handle;
}
}
public static class HotKeyA extends KeyNode{
public HotKeyA(int keyCode) {
super(keyCode);
}
@Override
public void operate() {
System.out.println("HotKeyA operate.");
}
}
public static class HotKeyB extends KeyNode{
public HotKeyB(int keyCode) {
super(keyCode);
}
@Override
public void operate() {
System.out.println("HotKeyB operate.");
}
}
public static class HotKeyC extends KeyNode{
public HotKeyC(int keyCode) {
super(keyCode);
}
@Override
public void operate() {
System.out.println("HotKeyC operate.");
}
}
}
21.Mediator(中介模式)
目的:用一个中介对象来封装一系列的对象交互,中介者使各个对象之间不必显示地进行交互,从而使其松散耦合,而且可以独立地改变它们之间的交互
适用:一般用于一组定义良好的对象但是进行复杂的方式进行通信的场合;以及想定制一个分布在不同类中的行为,而又不想生成太多的类
优点:Mediator 减少了各个对象之间的耦合,使各个对象和Mediator可以各自独立的变化和复用,且将Mediator作为一个独立的概念并将其封装在一个对象中,这样关注的焦点就从关注对象本身的实现转移到对象之间的关系,这就能从更宏观的角度上去看待一个系统
缺点:Mediator 将各个对象之间的交互复杂性转移到mediator的复杂性,使Mediator的复杂性增加,较难以维护
22.Flyweitht(享元模式)
目的:运用共享技术支持大量细粒度的对象
适用:需要生成大量细粒度对象来表示数据,而这些对象只有几个属性不同,就可以考虑使用享元模式。Java的String对象就是采用了享元模式。
优点:通过共享相同的数据大幅度的减少系统的开销
缺点:享元模式内部需要维护已经共享数据的列表,增加了享元模式复杂度,使得程序的逻辑更加复杂化。
23.Interpreter(解释器模式)
目的:给定一个语言,定义它文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子
适用:如果一个问题发生的频率足够高,那么就值得将该问题的实例表述一个个简单的语言中的列子。这样可以构建一个解释器,该解释器通过解决这些句子来解决该问题。(机器语言向汇编语言的转变就是个典型的解释器模式)
优点:方便用户解决问题,减少对问题的细节了解
缺点:解决问题的效率必定降低
24.Visitor(访问者模式)
目的:表示一个作用于对象结构中各元素的操作。它使你在不改变元素类的前提下定义作用于这些元素的新操作(把处理数据从数据结构中分离出来)
适用:适用于数据结构相对稳定的系统
优点:访问者模式将数据结构和操作数据结构之间的耦合解开,使操作的集合可以自由的演化。增加新的操作很容易,因为增加新的操作相当于增加一个新的访问者
缺点:如果被访问的数据结构成员要发生变化会变得比较困难