设计模式
尚硅谷—韩顺平—图解 Java设计模式
https://blog.csdn.net/qinwang_gz/category_11340442.html
类(对象)与类(对象)之间的关系有六种: 依赖、泛化(继承)、实现、关联、聚合、组合(属性初始化)。
设计模式的七大原则
-
单一职责原则 即一个类应该只负责一项职责
单一职责原则注意事项和细节
(1)降低类的复杂度,一个类只负责一项职责。
(2)提高类的可读性,可维护性
(3)降低变更引起的风险
(4)通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则。 -
接口隔离原则 一个类对另一个类的依赖应该建立在最小的接口上。
-
依赖倒转(倒置)原则 面向接口编程。
-
里氏替换原则 子类中尽量不要重写父类的方法。
-
开闭原则 编程中最基础、最重要的设计原则。即对扩展开放(提供方),对修改关闭(使用方)。
-
迪米特法则(最少知道原则)直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,继承,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。
-
合成复用原则 尽量使用合成/聚合的方式,而不是使用继承。
1、单例模式
在 JDK 应用的源码分析
(1)我们 JDK中,java.lang.Runtime就是经典的单例模式(饿汉式)
(2)代码分析+Debug源码+代码说明
单例模式注意事项和细节说明
(1)单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
(2)单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)。
2、工厂模式
看一个披萨的项目:要求便于披萨种类的扩展,要求便于维护。
简单工厂模式
工厂方法模式
抽象工厂模式
工厂模式在 JDK-Calendar 应用的源码分析
(1)JDK 中的 Calendar类中,就使用了简单工厂模式
(2)源码分析+Debug源码+说明
package com.atguigu.jdk;
import java.util.Calendar;
/**
* @author wjd
* @date 2021年09月13日 15:37
*/
public class Factory {
public static void main(String[] args) {
// getInstance 是 Calendar 静态方法
Calendar cal = Calendar.getInstance();
// 注意月份下标从0开始,所以取月份要+1
System.out.println("年:" + cal.get(Calendar.YEAR));
System.out.println("月:" + (cal.get(Calendar.MONTH) + 1));
System.out.println("日:" + cal.get(Calendar.DAY_OF_MONTH));
System.out.println("时:" + cal.get(Calendar.HOUR_OF_DAY));
System.out.println("分:" + cal.get(Calendar.MINUTE));
System.out.println("秒:" + cal.get(Calendar.SECOND));
}
}
public static Calendar getInstance()
{
return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
CalendarProvider provider =
LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
.getCalendarProvider();
if (provider != null) {
try {
return provider.getInstance(zone, aLocale);
} catch (IllegalArgumentException iae) {
// fall back to the default instantiation
}
}
Calendar cal = null;
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
if (cal == null) {
// If no known calendar type is explicitly specified,
// perform the traditional way to create a Calendar:
// create a BuddhistCalendar for th_TH locale,
// a JapaneseImperialCalendar for ja_JP_JP locale, or
// a GregorianCalendar for any other locales.
// NOTE: The language, country and variant strings are interned.
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
&& aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
return cal;
}
原型模式
现在有一只羊 tom,姓名为: tom, 年龄为:1,颜色为:白色,请编写程序创建和 tom羊 属性完全相同的 10只羊。
原理结构图说明:
(1)Prototype : 原型类,声明一个克隆自己的接口。
(2)ConcretePrototype: 具体的原型类, 实现一个克隆自己的操作实现Cloneable和重写clone方法。
(3)Client: 让一个原型对象克隆自己,从而创建一个新的对象(属性一样)。
原型模式在 Spring框架中源码分析
(1)Spring中原型 bean的创建,就是原型模式的应用
(2)代码分析+Debug源码
深拷贝基本介绍
(1)复制对象的所有基本数据类型的成员变量值
(2)为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝。
(3)深拷贝实现方式 1:重写clone方法来实现深拷贝。
(4)深拷贝实现方式 2:通过对象序列化实现深拷贝(推荐)
原型模式的注意事项和细节
(1)创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率。
(2)不用重新初始化对象,而是动态地获得对象运行时的状态。
(3)如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码。
(4)在实现深克隆的时候可能需要比较复杂的代码。
(5)缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了ocp原则,这点请同学们注意。
建造者模式
(1)需要建房子:这一过程为打桩、砌墙、封顶。过程可变化。
(2)房子有各种各样的,比如普通房,高楼,别墅,要求不同的。
原理类图
(1)Product(产品角色):一个具体的产品对象。
(2)Builder(抽象建造者):创建一个 Product对象的各个部件指定的 接口/抽象类。
(3)ConcreteBuilder(具体建造者):实现接口,构建和装配各个部件。
(4)Director(指挥者):构建一个使用 Builder接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。
建造者模式在 JDK的应用和源码分析
(1) java.lang.StringBuilder中的建造者模式
(2)代码说明+Debug源码
(3)源码中建造者模式角色分析
Appendable 接口定义了多个 append方法(抽象方法), 即 Appendable 为抽象建造者, 定义了抽象方法。
AbstractStringBuilder 实现了 Appendable 接口方法,这里的 AbstractStringBuilder 已经是建造者,只是不能实例化。
StringBuilder 即充当了指挥者角色,同时充当了具体的建造者,建造方法的实现是由 AbstractStringBuilder 完成, 而 StringBuilder 继承了 AbstractStringBuilder。
建造者模式的注意事项和细节
(1)客户端(使用程序)不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
(2)每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。
(3)可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
(4)增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。
(5)建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
(6)如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,因此在这种情况下,要考虑是否选择建造者模式.
(7)抽象工厂模式 VS建造者模式
抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可。而建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。
适配器模式
适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)。
类适配器模式应用实例
类适配器模式注意事项和细节
(1)Java是单继承机制,所以类适配器需要继承 src类这一点算是一个缺点,dst必须是接口,有一定局限性;
(2)因为继承src类的方法在 Adapter中都会暴露出来,也增加了使用的成本。
(3)由于其继承了 src类,所以它可以根据需求重写 src类的方法,使得 Adapter的灵活性增强了。
对象适配器模式
对象适配器模式注意事项和细节
(1)对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。根据合成复用原则,使用组合替代继承,所以它解决了类适配器必须继承 src 的局限性问题,也不再要求 dst必须是接口。
(2)使用成本更低,更灵活。
接口适配器模式介绍
(1)一些书籍称为:适配器模式(Default Adapter Pattern)或缺省适配器模式。
(2)核心思路:当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求。
(3)适用于一个接口不想使用其所有的方法的情况。
适配器模式在 SpringMVC框架应用的源码剖析
适配器模式的注意事项和细节
(1)三种命名方式,是根据 src是以怎样的形式给到 Adapter(在 Adapter里的形式)来命名的。
(2)类适配器:以类给到,在 Adapter里,就是将 src当做基类,继承对象适配器:以对象给到,在 Adapter里,将 src作为一个对象,以组合或聚合,持有接口适配器:以接口给到,在 Adapter里,将 src作为一个接口,匿名类实现。
(3)Adapter模式最大的作用还是将原本不兼容的接口融合在一起工作。
(4)实际开发中,实现起来不拘泥于我们讲解的三种经典形式。
桥接模式
原理类图
(1)Client类:桥接模式的调用者。
(2)抽象类(Abstraction) :维护了 Implementor / 即它的实现类 ConcreteImplementorA.., 二者是聚合关系, Abstraction充当桥接类。
(3)RefinedAbstraction : 是 Abstraction 抽象类的子类
(4)Implementor : 行为实现类的接口
(5)ConcreteImplementorA /B :行为的具体实现类
(6)从 UML图:这里的抽象类和接口是聚合的关系,其实调用和被调用关系
桥接模式的注意事项和细节
(1)实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统。
(2)对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部分由具体业务来完成。
(3)桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本。
(4)桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程。
(5)桥接模式要求正确识别出系统中两个独立变化的维度(抽象、和实现),因此其使用范围有一定的局限性,即需要有这样的应用场景。
桥接模式其它应用场景
对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
常见的应用场景:
(1) -JDBC驱动程序
(2) -银行转账系统
转账分类: 网上转账,柜台转账,AMT转账
转账用户类型:普通用户,银卡用户,金卡用户..
(3) -消息管理
消息类型:即时消息,延时消息
消息分类:手机短信,邮件消息,QQ消息...
装饰者模式
(1)咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)。
(2)调料:Milk、Soy(豆浆)、Chocolate。
(3)要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便。
(4)使用 OO的来计算不同种类咖啡的费用: 客户可以点单品咖啡,也可以单品咖啡+调料组合。
装饰者模式在 JDK应用的源码分析
组合模式
原理类图
对原理结构图的说明-即(组合模式的角色及职责)
(1)Component :这是组合中对象声明接口,在适当情况下,实现所有类共有的接口默认行为,用于访问和管理Component 子部件, Component 可以是抽象类或者接口。
(2)Leaf : 在组合中表示叶子节点,叶子节点没有子节点
(3)Composite :非叶子节点,用于存储子部件,在 Component 接口中实现 子部件的相关操作,比如增加(add),删除。
组合模式的注意事项和细节
(1)简化客户端操作。客户端只需要面对一致的对象而不用考虑整体部分或者节点叶子的问题。
(2)具有较强的扩展性。当我们要更改组合对象时,我们只需要调整内部的层次关系,客户端不用做出任何改动
(3)方便创建出复杂的层次结构。客户端不用理会组合里面的组成细节,容易添加节点或者叶子从而创建出复杂的树形结构。
(4)需要遍历组织机构,或者处理的对象具有树形结构时, 非常适合使用组合模式。
(5)要求较高的抽象性,如果节点和叶子有很多差异性的话,比如很多方法和属性都不一样,不适合使用组合模式。
外观模式
外观模式原理类图
对类图说明(分类外观模式的角色)
(1)外观类(Facade): 为调用端提供统一的调用接口, 外观类知道哪些子系统负责处理请求,从而将调用端的请求代理给适当子系统对象。
(2)调用者(Client): 外观接口的调用者。
(3)子系统的集合:指模块或者子系统,处理 Facade 对象指派的任务,他是功能的实际提供者。
外观模式在MyBatis框架应用的源码分析
(1)MyBatis 中的 Configuration 去创建MetaObject 对象使用到外观模式
(2) 代码分析+Debug源码+示意图
外观模式的注意事项和细节
(1)外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性。
(2)外观模式对客户端与子系统的耦合关系 - 解耦,让子系统内部的模块更易维护和扩展。
(3)通过合理的使用外观模式,可以帮我们更好的划分访问的层次。
(4)当系统需要进行分层设计时,可以考虑使用 Facade模式。
(5)在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个Facade类,来提供遗留系统的比较清晰简单的接口,让新系统与 Facade类交互,提高复用性。
(6)不能过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好。要以让系统有层次,利于维护为目的。
享元模式
小型的外包项目,给客户 A做一个产品展示网站,客户 A的朋友感觉效果不错,也希望做这样的产品展示网站,但是要求都有些不同:
-
有客户要求以新闻的形式发布
-
有客户人要求以博客的形式发布
-
有客户希望以微信公众号的形式发布
享元模式的原理类图
对原理图的说明-即(模式的角色及职责)
(1)FlyWeight 是抽象的享元角色, 他是产品的抽象类, 同时定义出对象的外部状态和内部状态(后面介绍) 的接口或实现。
(2)ConcreteFlyWeight 是具体的享元角色,是具体的产品类,实现抽象角色定义相关业务
(3)UnSharedConcreteFlyWeight 是不可共享的角色,一般不会出现在享元工厂。
(4)FlyWeightFactory 享元工厂类,用于构建一个池容器(集合),同时提供从池中获取对象方法。
享元模式在 JDK-Interger的应用源码分析
(1) Integer中的享元模式
(2) 代码分析+Debug源码+说明
代码说明:
package com.atguigu.jdk;
public class FlyWeight {
public static void main(String[] args) {
//如果 Integer.valueOf(x) x 在 -128 --- 127 直接,就是使用享元模式返回,如果不在
//范围类,则仍然 new
//小结:
//1. 在 valueOf 方法中,先判断值是否在 IntegerCache 中,如果不在,就创建新的 Integer(new), 否则,就直接从缓存池返回
//2. valueOf 方法,就使用到享元模式
//3. 如果使用 valueOf 方法得到一个 Integer 实例,范围在 -128 - 127 ,执行速度比 new 快
Integer x = Integer.valueOf(127); // 得到 x实例,类型 Integer
Integer y = new Integer(127); // 得到 y 实例,类型 Integer
Integer z = Integer.valueOf(127);//..
Integer w = new Integer(127);
System.out.println(x.equals(y)); // 大小,true
System.out.println(x == y ); // false
System.out.println(x == z ); // true
System.out.println(w == x ); // false
System.out.println(w == y ); // false
Integer x1 = Integer.valueOf(200);
Integer x2 = Integer.valueOf(200);
System.out.println("x1==x2" + (x1 == x2)); // false
}
}
享元模式的注意事项和细节
(1)在享元模式这样理解,“享”就表示共享,“元”表示对象。
(2)系统中有大量对象,这些对象消耗大量内存,并且对象的状态大部分可以外部化时,我们就可以考虑选用享元模式。
(3)用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象,用 HashMap/HashTable存储。
(4)享元模式大大减少了对象的创建,降低了程序内存的占用,提高效率。
(5)享元模式提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变,这是我们使用享元模式需要注意的地方.。
(6)使用享元模式时,注意划分内部状态和外部状态,并且需要有一个工厂类加以控制。
(7)享元模式经典的应用场景是需要缓冲池的场景,比如 String常量池、数据库连接池。
代理模式
静态代理思路分析图解(类图)
静态代理优缺点
(1)优点:在不修改目标对象的功能前提下, 能通过代理对象对目标功能扩展。
(2)缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,而动态代理只需要一个就可以代理不同类型的类。
(3)一旦接口增加方法,目标对象与代理对象都要维护。
JDK代理思路图解(类图)
Cglib代理(子类代理)
它是在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将Cglib代理归属到动态代理。
思路图解(类图)
1)首先导入cglib jar包
2)注意,cglib代理的目标对象里要被增强的方法不能是static或者final的,如果是static或者final的则不会被拦截到,因为cglib是通过继承目标类实现方法增强,而static或者final的方法是不能被重写的也就不会执行增强的代码。
还有 cglib代理的目标类更不能是final的,否则会报错,因为不能被继承
几种常见的代理模式介绍—几种变体
(1)防火墙代理
内网通过代理穿透防火墙,实现对公网的访问。
(2)缓存代理
比如:当请求图片文件等资源时,先到缓存代理取,如果取到资源则 ok,如果取不到资源,再到公网或者数据库取,然后缓存。
(3) 远程代理
远程对象的本地代表,通过它可以把远程对象当本地对象来调用。远程代理通过网络和真正的远程对象沟通信息。
(4)同步代理:主要使用在多线程编程中,完成多线程间同步工作
同步代理:主要使用在多线程编程中,完成多线程间同步工作。
模板方法模式
模板方法模式 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤。
钩子方法
在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”。
模板方法模式在 Spring框架应用的源码分析
模板方法模式的注意事项和细节
(1)基本思想是:算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改。
(2)实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。
(3)既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现。
(4)该模式的不足之处:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大。
(5)一般模板方法都加上 final关键字,防止子类重写模板方法。
(6)模板方法模式使用场景:当要完成在某个过程,该过程要执行一系列步骤 ,这一系列的步骤基本相同,但其个别步骤在实现时 可能不同,通常考虑用模板方法模式来处理。
观察者模式
具体要求如下:
(1)气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)。
(2)需要设计开放型 API,便于其他第三方也能接入气象站获取数据。
(3)提供温度、气压和湿度的接口。
(4)测量数据更新时,要能实时的通知给第三方。
类图说明
Subject:登记注册、移除和通知
(1)registerObserver 注册
(2)removeObserver 移除
(3)notifyObservers() 通知所有的注册的用户,根据不同需求,可以是更新数据,让用户来取,也可能是实施推送,看具体需求定
Observer:接收输入
观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象为 Subject,依赖的对象为 Observer,Subject通知 Observer变化,比如这里的奶站是 Subject,是 1的一方。用户时 Observer,是多的一方。
观察者模式在 Jdk应用的源码分析
(1)Jdk的 Observable类就使用了观察者模式
(2)代码分析+模式角色分析
(3)模式角色分析
Observable 的作用和地位等价于 我们前面讲过 Subject
Observable 是类,不是接口,类中已经实现了核心的方法 ,即管理 Observer的方法 add.. delete .. notify...
Observer 的作用和地位等价于我们前面讲过的 Observer, 有 update
Observable 和 Observer 的使用方法和前面讲过的一样,只是 Observable 是类,通过继承来实现观察者模式
中介者模式
中介者模式的注意事项和细节
(1)多个类相互耦合,会形成网状结构, 使用中介者模式将网状结构分离为星型结构,进行解耦
(2)减少类间依赖,降低了耦合,符合迪米特原则
(3)中介者承担了较多的责任,一旦中介者出现了问题,整个系统就会受到影响
(4)如果设计不当,中介者对象本身变得过于复杂,这点在实际使用时,要特别注意
备忘录模式
原理类图
-
originator : 对象(需要保存状态的对象)
-
Memento : 备忘录对象,负责保存好记录,即 Originator内部状态
-
Caretaker: 守护者对象,负责保存多个备忘录对象,使用集合管理,提高效率
-
说明:如果希望保存多个 originator对象的不同时间的状态,也可以,只需要要 HashMap <String, 集合>
备忘录模式的注意事项和细节
(1)给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。
(2)实现了信息的封装,使得用户不需要关心状态的保存细节。
(3)如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存, 这个需要注意。
(4)适用的应用场景:
1、后悔药。
2、打游戏时的存档。
3、Windows 里的 ctri + z。
4、IE 中的后退。
5、数据库的事务管理
状态模式
1)对象转换 50 积分,中奖概率是 10%
2)奖品数量固定,抽完就不能抽奖
3)活动有四个状态: 可以抽奖、不能抽奖、发放奖品和奖品领完
4)活动的四个状态转换关系图(下图)
基本介绍
1)状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一 一对应的,状态之间可以相互转换
2)当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类
3)状态发生了变化,那行为也随之改变
状态模式的注意事项和细节
1)代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中
2)方便维护。将容易产生问题的 if-else 语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产出很多 if-else 语句,而且容易出错
3)符合“开闭原则”。容易增删状态
4)会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度
5)应用场景:
当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候, 可以考虑使用状态模式