整理资料源自菜鸟教程 http://www.runoob.com/design-pattern/design-pattern-tutorial.html
设计模式可大致分为三种:创建型模型、结构型模型、行为型模型。
常用设计模式:单例模式、工厂模式(简单+抽象)、外观模式、代理模式、观察者模式、命令模式、策略模式
创建型模型
工厂模式 **
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
先创建一个接口Shape,里面有行为draw(),用于打印形状。
创建两个类实现Shape接口,分别为Circle类、Square类,分别重写draw()。
创建一个工厂类,在工厂类中实现根据输入工厂类参数的不同从而创建返回不同的Shape对象。
抽象工厂模式 **
抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
通过创建一个工厂的抽象类,让工厂模式中的工厂继承该抽象类,达到创建“工厂的工厂”的目的。
创建一个工厂生成类,在里面写一个方法(或静态方法)用来判断创建返回哪一个工厂对象。
通过返回的工厂对象再进行Shape对象的创建。
单例模式 **
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
单例模式类中满足三个条件:
1.构造函数私有化
2.静态创建一个本类的对象
3.提供一个返回已创建对象的静态方法
单例模式有多重实现方式
详细:https://www.runoob.com/design-pattern/singleton-pattern.html
- 懒汉式,线程不安全 (不可取)
- 懒汉式,线程安全 (在getInstance()方法上加锁会严重影响多线程情况下getInstance()方法的并发能力)
- 饿汉式 (痴汉式,在类加载时就创建了单例,可能造成资源浪费)
- 双重校验锁 (在2方式上进行了改良,在判断了单例对象不存在时才进行单例对象的实例化,并且通过在实例化前为单例的类对象加锁保证了单例对象实例化的唯一)
- 登记式/静态内部类 ****暂不理解
- 枚举——这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。
//枚举法创建单例 public enum Singleton { INSTANCE; public void whateverMethod() { } }
一般情况下,不建议使用第 1 种和第 2 种懒汉方式,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第 6 种枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式。
建造者模式
建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。
主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
何时使用:一些基本部件不会变,而其组合经常变化的时候。
如何解决:将变与不变分离开。
建筑者模式主要分有三个组成部分:
- 建筑者角色:该接口定义了生成实例所需要的所有方法
- 具体的建筑者角色:实现生成实例所需要的所有方法,并且定义获取最终生成实例的方法
- 监工角色:定义使用建造者角色中的方法来生成实例的方法
https://www.runoob.com/design-pattern/builder-pattern.html
原型模式
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
关键代码: 1、实现克隆操作,在 JAVA 实现 Cloneable,重写 clone()。 2、原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些"易变类"拥有稳定的接口。
注意事项:与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流。
.... public Object clone() { //在实现了Cloneable的类中重写clone()方法 Object clone = null; try { clone = super.clone(); //在需要克隆某对象的情况下直接调用该对象的clone()方法 Class.clone() } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; } ...
结构型模式
适配器模式
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。
这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。
意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。
注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。
有A、B两接口,A有实现类C,B有实现类D,希望通过C对象既能实现A接口方法又能实现B接口方法。
则需要创建一个适配器类
public class MediaAdapter implements MediaPlayer { //适配器类 AdvancedMediaPlayer advancedMusicPlayer; //存储实现B类接口类 public MediaAdapter(String audioType){ if(audioType.equalsIgnoreCase("vlc") ){ //判断输入类型需要用B类接口方法处理 advancedMusicPlayer = new VlcPlayer(); } else if (audioType.equalsIgnoreCase("mp4")){ advancedMusicPlayer = new Mp4Player(); } } @Override public void play(String audioType, String fileName) { //重写A类接口方法 if(audioType.equalsIgnoreCase("vlc")){ advancedMusicPlayer.playVlc(fileName); }else if(audioType.equalsIgnoreCase("mp4")){ advancedMusicPlayer.playMp4(fileName); } } } public class AudioPlayer implements MediaPlayer { //类C实现 MediaAdapter mediaAdapter; //引用适配器 @Override public void play(String audioType, String fileName) { //重写A类接口方法 //播放 mp3 音乐文件的内置支持 if(audioType.equalsIgnoreCase("mp3")){ //A类接口方法支持类型 System.out.println("Playing mp3 file. Name: "+ fileName); } //mediaAdapter 提供了播放其他文件格式的支持 else if(audioType.equalsIgnoreCase("vlc") //B类接口方法支持类型 || audioType.equalsIgnoreCase("mp4")){ mediaAdapter = new MediaAdapter(audioType); mediaAdapter.play(audioType, fileName); } else{ System.out.println("Invalid media. "+ audioType + " format not supported"); } } }
桥接模式
桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。
这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。
意图:将抽象部分与实现部分分离,使它们都可以独立的变化。
主要解决:在有多种可能会变化的情况下,用继承会造成类爆炸问题,扩展起来不灵活。
如何解决:把这种多角度分类分离出来,让它们独立变化,减少它们之间耦合。
关键代码:抽象类依赖实现类。
理解“把抽象化与实现化解耦,使得二者可以独立变化”:https://www.runoob.com/w3cnote/bridge-pattern2.html
过滤器模式
过滤器模式(Filter Pattern)或标准模式(Criteria Pattern)是一种设计模式,这种模式允许开发人员使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来。这种类型的设计模式属于结构型模式,它结合多个标准来获得单一标准。
设置一个接口,通过创建这个接口的实现类且在类中进行条件判断从而进行过滤。
组合模式(部分整体模式)
用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。
通过在实体类添加一个List,在实例化的时候按照层级,将下一层的对象存放进上一层的对应的对象的List中。
装饰器模式
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
创建装饰器抽象类实现目的接口,然后实例化抽象类,在该对象中添加修饰,对于接口的方法通过多态实现。
private Shape shape ; //在抽象类中设置接口类属性 public void Decorator(Shape shape){ this.shape = shape ; //通过多态方式为对象赋予不同的Shape类 } public void draw(){ shape.toString(); //实现Shape类原方法 .... //装饰 }
外观模式**
外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。
这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。
通过创建一个对外的类,在类中创建系统中类的实体且调用希望被外部使用的方法。
public class ShapeMaker { private Shape circle; //内部实体类 private Shape rectangle; private Shape square; public ShapeMaker() { circle = new Circle(); //实例化 rectangle = new Rectangle(); square = new Square(); } public void drawCircle(){ //外部通过调用封装后的方法来实现内部复杂方法 circle.draw(); } public void drawRectangle(){ rectangle.draw(); } public void drawSquare(){ square.draw(); } }
享元模式
享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。
享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。
意图:运用共享技术有效地支持大量细粒度的对象。
主要解决:在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。
何时使用: 1、系统中有大量对象。 2、这些对象消耗大量内存。 3、这些对象的状态大部分可以外部化。 4、这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。 5、系统不依赖于这些对象身份,这些对象是不可分辨的。
如何解决:用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象。
关键代码:用 HashMap 存储这些对象。
应用实例: 1、JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面。 2、数据库的数据池。
使用场景: 1、系统有大量相似对象。 2、需要缓冲池的场景。
注意事项: 1、注意划分外部状态和内部状态,否则可能会引起线程安全问题。 2、这些类必须有一个工厂对象加以控制。
设计一个工厂对象,在里面存储一个Map<某变动属性,对象>,每次希望调用对象时现在Map中根据属性查找是否已创建该属性对象,存在则直接调用,不存在则新建该属性对象。
代理模式**
在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。
在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。
意图:为其他对象提供一种代理以控制对这个对象的访问。
主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。
何时使用:想在访问一个类时做一些控制。
如何解决:增加中间层。
关键代码:实现与被代理类组合。
优点: 1、职责清晰。 2、高扩展性。 3、智能化。
缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
使用场景:按职责来划分,通常有以下使用场景: 1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。 4、保护(Protect or Access)代理。 5、Cache代理。 6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(Smart Reference)代理。
代理模式需要代理类实现被代理类实现的接口,且在代理类中实例化并保存被代理类
public class ProxyImage implements Image{ //代理类 private RealImage realImage; //在代理类中保存被代理类的对象 private String fileName; public ProxyImage(String fileName){ this.fileName = fileName; } @Override public void display() { if(realImage == null){ realImage = new RealImage(fileName); } realImage.display(); } }
****************************************************************
代理模式、适配器模式、外观模式、装饰器模式的区别:
装饰器模式(注重功能扩展):通过外部向装饰器类传入一个对象,再在装饰器中添加方法,达到向对象添加功能的目的。
适配器模式(注重接口的兼容):目的是将A类接口方法通过实现了B类接口的适配器的转化——即在适配器重写的B类接口方法中调用A类接口方法,在实现了B类接口的C类中通过调用适配器达到以B类接口的方法实现了A类接口的方法(A向B兼容)
外观模式(注重多个类的集成):将系统内多个类封装在一个类中,外部系统通过这个类完成对内部类的访问及方法调用。
代理模式(注重控制隔离):代理类需要实现和被代理类一样的接口,代理类内部保存了被代理类的实例,外部通过访问代理类达到访问被代理类的目的。
******************************************************************************************************************************************************************************
行为型模式
责任链模式
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。
在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
意图:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
主要解决:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
主要解决:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
关键代码:Handler 里面聚合它自己,在 HandlerRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。
优点: 1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。
缺点: 1、不能保证请求一定被接收。 2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 3、可能不容易观察运行时的特征,有碍于除错。
通过菜鸟教程的示例,发现实现责任链模式的关键是:
- 在抽象类或接口中设置等级,一个下一级属性(存放下一级职责类,用来实现"链"),一个参数为等级和被处理对象的A方法——方法中需要进行两个判断
- 1、对被处理对象等级与当前等级进行比较决定是否由当前等级职责类处理。
- 2、若下一级属性不为空,则将被处理信息和被处理信息的等级传递下去。( 下一级职责.A方法(level,被处理对象) )
- 让责任链中各个职责类继承同一抽象类或接口。各个职责类重写处理对象的方法。
- 实现时,创建一个静态方法,在其中创建各个职责类且设置等级,设置下一级属性。该方法返回最高级职责类。
- 将待处理对象和其等级传入返回的最高级职责类的A方法。
命令模式**
命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
意图:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
何时使用:在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
如何解决:通过调用者调用接受者执行命令,顺序:调用者→接受者→命令。
关键代码:定义三个角色:1、received 真正的命令执行对象 2、Command 3、invoker 使用命令对象的入口
http://www.runoob.com/design-pattern/command-pattern.html
通过往invoker中存储received成批地处理Command
解释器模式
解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
主要解决:对于一些固定文法构建一个解释句子的解释器。
如何解决:构件语法树,定义终结符与非终结符。
关键代码:构件环境类,包含解释器之外的一些全局信息,一般是 HashMap。
注意事项:可利用场景比较少,JAVA 中如果碰到可以用 expression4J 代替。
http://www.runoob.com/design-pattern/interpreter-pattern.html
迭代器模式
迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式。这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。
意图:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
关键代码:定义接口:hasNext, next。
应用实例:JAVA 中的 iterator。
注意事项:迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。
http://www.runoob.com/design-pattern/iterator-pattern.html
中介者模式
中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。
意图:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
使用场景: 1、系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。 2、想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。
注意事项:不应当在职责混乱的时候使用。
http://www.runoob.com/design-pattern/mediator-pattern.html 聊天室例子
在菜鸟笔记中思考中介者模式深层使用
备忘录模式
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。
注意事项: 1、为了符合迪米特原则,还要增加一个管理备忘录的类。 2、为了节约内存,可使用原型模式+备忘录模式。
迪米特原则:一个对象应该对其他对象有较少的了解。
http://www.runoob.com/design-pattern/memento-pattern.html
创建一个工作类进行工作,备忘录类记录工作类的某种属性状态,管理备忘录类对备忘录进行管理。
可以在菜鸟的例子里对管理备忘录类进行优化,设置一个索引,每次添加新的备忘录则更新最新索引,每次回溯索引-1,则可以达到回溯遍历的需求。
观察者模式**
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
关键代码:在抽象类里有一个 ArrayList 存放观察者们。
优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景:
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
- 一个对象必须通知其他对象,而并不知道这些对象是谁。
- 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
http://www.runoob.com/design-pattern/observer-pattern.html
创建一个观察抽象类,所有观察者继承该抽象类。观察抽象类保存被观察者。
被观察者提供方法将观察者与其“绑定”,且被观察者类中创建列表保存观察者类们。在设置状态的方法中提醒所有观察者们更新状态(遍历)。
状态模式
在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。
在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。
意图:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
关键代码:通常命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法。而且,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。状态模式和命令模式一样,也可以用于消除 if...else 等条件选择语句。
注意事项:在行为受状态约束的时候使用状态模式,而且状态不超过 5 个。
http://www.runoob.com/design-pattern/state-pattern.html
几个状态类实现同一个接口,状态类中进行行为的实现。创建一个Context类对不同状态进行访问。
空对象模式
在空对象模式(Null Object Pattern)中,一个空对象取代 NULL 对象实例的检查。Null 对象不是检查空值,而是反应一个不做任何动作的关系。这样的 Null 对象也可以在数据不可用的时候提供默认的行为。
在空对象模式中,我们创建一个指定各种要执行的操作的抽象类和扩展该类的实体类,还创建一个未对该类做任何实现的空对象类,该空对象类将无缝地使用在需要检查空值的地方。
https://www.runoob.com/design-pattern/null-object-pattern.html
让空对象和实对象继承相同的抽象类,在检查某集合是否存在某些值时,当判断不存在时可通过返回空对象代替null值。
策略模式 **
在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。
使用场景: 1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 2、一个系统需要动态地在几种算法中选择一种。 3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
与状态模式的比较