设计模式

常用的设计模式有23种,按特点分为3类:创建型、结构型、行为型

创建型

功能:
将系统所使用的具体类的信息封装起来;
隐藏类的实例是如何被创建和组织的。外界对于这些对象只知道它们共同的接口,而不清楚其具体的实现细节。

作用:
封装创建逻辑,不仅仅是new一个对象那么简单;
封装创建逻辑变化,客户代码尽量不修改,或尽量少修改。

  • 单例模式(Singleton Pattern):一个类只有一个实例,而且自行实力和并向整个系统提供这个实例。
  • 工厂方法模式(Factory
    Pattern):在工厂方法模式中,工厂类成为了抽象类,实际的创建工作将由具体子类来完成。工厂方法的用意是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中去,强调的是“单个对象”的变化。
  • 抽象工厂模式(Abstract
    Factory):抽象工厂是所有工厂模式中最为抽象和最具有一般性的一种形态。抽象工厂可以向客户提供一个接口,使得客户可以在不必指定产品具体类型的情况下,创建多个产品族中的产品对象,强调的是“系列对象”的变化。
  • 建造者模式(Builder
    Pattern):把构造对象实例的逻辑移到了类的内部,在类的外部定义了该类的构造逻辑。它把一个复杂对象的构造过程从对象的表示中分离出来,其直接效果是将一个复杂的对象简化为一个比较简单的目标对象,强调的是产品的构造过程。
  • 原型模式(Prototype
    Pattern):原型模式和工厂模式一样,同样对客户隐藏了对象创建工作,但与通过对一个类进行实例化来构建新对象不同的是,原型模式是通过复制一个现有对象生成新对象。

结构型

讨论类和对象的结构,采用继承机制来组合接口或实现。

  • 代理模式(Proxy):为其他对象提供一种代理以控制对该对象的访问。
  • 装饰模式(Decorator):动态地给一个对象添加一些额外的职责,装饰模式比生成子类更为灵活。
  • 适配器模式(Adapter):将一个类的接口变换成客户端所期待的另一接口,从而使原本因接口不匹配而无法工作的两个类能够在一个工作。
  • 组合模式(Composite):也叫合成模式,将对象组合成树形结构以表示“部分–整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
  • 桥梁模式(Bridge):也叫桥接模式,将抽象和实现解耦,使得两者可以独立变化。
  • 外观模式(Facade):也叫门面模式,要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行,外观模式提供一个高层次的接口,使得子系统更易于使用。
  • 享元模式(Flyweight):是池技术的重要实现方式,使用共享对象可有效地支持大量的细粒度的对象。

行为型

解决对象之间的联系问题

  • 模板方法模式(Template
    Method):定义一个操作中的算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
  • 命令模式(Command):是一种高内聚的模式,将一个请求封装成一个对象,从而使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
  • 责任链模式(Chain of
    Responsibility):使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
  • 策略模式(Strategy):也叫政策模式,定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。
  • 迭代器模式(Iterator):提供一种方法访问一个容器对象中的各个元素,而又不需要暴露该对象的内部细节。
  • 中介者模式(Mediator):用一个中介对象封装一系列的对象交互,中介者使各对象不需要显式地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
  • 观察者模式(Observer):也叫发布订阅模式,定义对象间的一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
  • 备忘录模式(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
  • 访问者模式(Visitor):封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用这些元素的新的操作。
  • 状态模式(State):当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类型,状态模式的核心是封装,状态的变更引起行为的变更。
  • 解释器模式(Interpreter):给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该文法表示来解释语言中的句子。

单一职责原则(Single Responsibility Principle,SRP)

一个类应当只有一个职责;一个类只有一个引起它变化的原因。
降低类的复杂性;
提高类的可读性;
提高代码的可维护性和复用性;
降低因变更引起的风险。
在这里插入图片描述

里氏替换原则(Liskov Subsitution Principle,LSP)

第一种定义:如果一个类型为S的对象o1,都有类型为T的对象o2,使得以S定义的所有程序P在所有的对象o1都带换成o2时,程序P的行为没有发生变化,那么类型T是类型S的子类型。
第二种定义:所有引用基类的地方必须能透明地使用其子类对象。清晰明确地说明只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道父类还是子类;但是反过来则不可以,有子类的地方,父类未必就能适应。
在这里插入图片描述
子类必须完全实现父类的方法;
子类可以有自己的个性;
覆盖或实现父类的方法时输入参数可以被放大;
覆盖或实现父类的方法时输出结果可以被缩小。
按照里氏替换原则,当多个类之间存在继承关系时,通常应该使用父类或接口来指向子类对象(除非需要子类特有的方法),这便利与提高系统的可扩展性。
体现里氏替换原则的模式:策略模式、组合模式、代理模式。

依赖倒置原则(Dependence Inversion Principle,DIP)

高层模块不应该依赖底层模块,两者都依赖其抽象;
抽象不依赖细节;
细节应该依赖于抽象。
在Java中的表现:
模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生;
接口或抽象类不依赖于实现类
实现类依赖于接口或抽象类。
在这里插入图片描述
需要遵循的规则:
每个类尽量都具有接口或抽象类,或者抽象类和接口两者都具备。这是依赖倒置的基本要求,接口和抽象类都是抽象的,有了抽象才可能有依赖倒置。
变量的表面类型尽量是接口或者是抽象类;
任何类都不应该从具体类派生;
尽量不要重写基类的方法。如果基类是一个抽象类,而且这个方法已经实现了,子类尽量不要重写。类之间依赖的是抽象,重写了非抽象方法,对依赖的稳定性会产生一定影响;
结合里氏替换原则使用。里氏替换原则指出父类出现的地方子类就可以出现,结合依赖倒置原则可以得出一个通俗的规则:接口负责定义抽象方法,并且声明与其他对象的依赖关系,抽象类负责公共构造部分的实现,实现类准确地实现业务逻辑,同时在适当的时候对父类进行细化。

接口隔离原则(Interface Segregation Principle,ISP)

接口分为两种:
实例接口(Object Interface),在Java中声明一个类,然后用new关键字产生一个实例,new后面的就是实例接口;
类接口(Class Interface),Java中使用Interface定义的接口

定义:
第一定义:客户端不应该依赖它不需要的接口;
第二定义:类间的依赖关系应该建立在最小的接口上。
在这里插入图片描述
一个接口只对一个子模块或者业务逻辑进行服务;
只保留接口中业务逻辑需要的public方法;
尽量修改污染了的接口,若修改的风险较大,则可采取适配器模式进行转化处理;
接口设计应因项目而异,因环境而异,不能教条照搬。

迪米特法则(Law of Demeter,LoD)

如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用;如果一个类需要调用另一个类的某一个方法,可以通过第三者转发这个调用。
在这里插入图片描述
使用迪米特法则的设计模式:外观模式、中介者模式

开闭原则(Open-Closed Principle,OCP)

对扩展开放,对修改关闭。
把系统所有可能的行为抽象成一个抽象底层,这个抽象底层规定出所有的具体实现必须提供的方法的特征,不更改抽象设计,只允许行为在实现层被实现。
开闭原则提高复用性
开闭原则提高可维护性
开闭原则提高灵活性
开闭原则易于测试
在这里插入图片描述

单例模式

确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

饿汉式:类加载时,就进行对象实例化;
懒汉式:第一次引用类时,才进行对象实例化。
从资源利用效率上来说,饿汉式要差一些,但从速度和反应时间来讲,饿汉式比懒汉式稍好些。

优点:
由于单例模式在内存中只有一个实例,减少了内存的开支,特别是一个对象需要频繁地创建、销毁,而且创建和销毁的性能又无法优化时,单例模式的优势就非常明显了。
由于单例模式只生成一个实例,所以减少了系统性能的开销,当一个对象的产生需要比较多资源时,可以通过在启用时直接产生在内存中,然后用永久驻留内存的方式来解决。
单例模式可以避免对资源的多重占用,由于只有一个实例存在内存中,避免了对同一个资源文件的同时操作。
单例模式可以在系统设置全局的访问点,优化和共享资源访问,可以设计一个单例,负责所有数据表的映射处理。
缺点:
单例模式无法创建子类,扩展困难,若要扩展,除了修改代码基本上没有第二种途径。
单例模式对测试不利。在并行开发环境中,如果采用单例模式的类没有完成,是不能进行测试的;单例的类通常不会实现接口,这也妨碍了使用mock的方式虚拟一个对象。
单例模式与单一职责原则有冲突。一个给类应该只实现一个逻辑,而不关心它是否单例的,要不要使用单例取决于环境和业务逻辑。

工厂方法模式

简单工厂模式、工厂方法模式、抽象工厂模式。
定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延到其子类。
优点:
良好的封装性,代码结构清晰。只需知道类名或约束字符串即可,不用知道创建对象的过程如何,降低了模块间的耦合。
优秀的可扩展。
屏蔽产品类。
使用场景:
工厂方法模式是new一个对象的替代品,因此在所有需要生成对象的地方都可以使用,但是需要慎重考虑是否需要增加一个工厂类进行管理,增加代码的复杂的。
需要灵活的、可扩展的框架时,可以考虑采用工厂方法模式 。
工厂方法模式可以使用在测试驱动开发的框架下。

抽象工厂模式

为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类。
优点:
产品族内的约束为非公开状态,在不同的工厂中,各种产品可能具有不同的相互依赖关系,这些依赖关系由工厂封装在其内部,对于工厂的使用者是不可见的。
生产线的扩展非常容易,如果要针对同一产品族建立新的生产线,只需要实现产品族中的所有产品接口并建立新的工厂类即可。
缺点:产品族本身的扩展非常困难,如果需要在产品族中增加一个新的产品类型,则需要修改多个接口,并且会影响已有的工厂类。

建造者模式

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
优点:
封装性,使用建造者模式可以使客户端不必知道产品内部组成的细节。
建造者独立,容易扩展。
便于控制细节风险,由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。
使用场景:
相同的方法,不同的执行顺序,产生不同的结果时,可以采用建造者模式。
多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时,则可以使用该模式。
产品类非常复杂,或者产品类中的方法调用顺序不同产生了不同的效果,这个时候使用建造者模式
在对象创建过程中会使用到系统的一些其他对象,这些对象在产品对象的创建过程中不易得到时,也可以采用建造者模式封装该对象的创建过程。

原型模式

使用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
使用原型模式需要使用Java内部克隆机制,只用方法有两步:
实现Cloneable接口;
覆盖Object 的clone()方法,完成对象的克隆操作,通常只需要调用Object的clone()方法即可。为了使外部能够调用此类的clone()方法,可以将可访问性修改为public。

优点:
性能优良:使用内存二进制复制,比直接new一个对象性能好。
逃避构造函数的约束:这是优点也是缺点,直接在内存中复制,构造函数是不会执行的,因此减少了约束,需要在实际应用时进行权衡考虑。

使用场景:
资源优化场景,类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
性能和安全要求的场景,通过new产生一个对象需要非常繁琐的数据准备或访问权限,可以使用原型模式。
一个对象多个修改者的场景,一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式复制多个对象供调用者使用。

代理模式

为其他对象提供一种代理以控制对这个对象的访问。
远程代理:为一个位于不同的地址空间的对象提供一个局部代表对象。这个不同的地址空间可以是在本机器中,也可以在另一台机器中。
虚拟代理:有时需要创建一些消耗较多资源的对象,可以首先创建代理对象,而将真实对象的创建延迟。
保护代理:控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。
缓存代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
同步代理:使几个用户能够同时使用一个对象而没有冲突。
智能引用代理:当一个对象被引用时,提供一些额外的操作。

优点:
职责清晰:真实的角色实现实际的业务逻辑,不用关心其他非本职的事务,通过后期的代理完成附加的事务,附带的结果就是编程简洁清晰。
高扩展性:具体主题角色随需求不同可能有很多种,但只要实现了接口,代理类就完全可以在不做任何修改的情况下代理各种真实主题角色。
智能化:代理类可以在运行时才确定需要去代理的真实主题,这是一种强大的功能。

装饰模式

动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式比生成子类更为灵活。
优点:
装饰类和被装饰类可以独立发展,而不会相互耦合。
装饰模式是继承关系的一个替代方案。
装饰模式可以动态地扩展一个实现类的功能。
缺点:多层的装饰是比较复杂的。

应用场景:
需要扩展一个类的功能,或给一个类增加附加功能。、
需要动态地给一个对象增加功能,这些功能可以再动态地销毁。
需要为一批类进行改装或加装功能。

适配器模式

将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
优点:
适配器模式可以让两个没有任何关系的类在一起运行。
增加了类的透明性。
提高类的复用度。
增强代码的灵活性。

组合模式

将对象组合成树形结构以表示“部分–整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
优点:
高层模块调用简单。
节点增加自由。
缺点:
不易控制树枝结构的类型。
不易使用继承的方法来增加新的行为。

桥梁模式

将抽象和实现解耦,使得两者可以独立地变化。
优点:
抽象和实现分离是桥梁模式的 主要特点,是为了解决继承的缺点而提出的设计模式。在该模式下,实现可以不受抽象的约束,不用绑定在一个固定的抽象层次上。
实现对客户透明,客户端不用关心细节的实现,它已经由抽象层通过聚合关系完成了封装。
提高灵活性和扩展性。

应用场景:
如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的联系。
设计要求实现化角色的任何改变不应当影响客户端,或者说实现化角色的改变对客户端是完全透明的。
一个构件有多于一个的抽象化角色和实现化角色,系统需要它们之间进行动态耦合。
不希望或不适合使用继承的场合。

外观模式

要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。外观模式提供一个高层次的接口,使得子系统更易使用。
优点:
减少系统的相互依赖,所有的依赖都是对Facade对象的依赖,与子系统无关。
提高灵活性,不管子系统内部如何变化,只要不影响Facade对象,任何活动都是自由的。
提高安全性,Facade中未提供的方法,外界就无法访问,提高系统的安全性。
缺点:外观模式最大的缺点就是不符合开闭原则,对修改关闭,对扩展开放。
应用场景:
为一个复杂的模块或子系统提供一个外界访问的接口。
子系统相对独立,外界对子系统的访问只要黑箱操作即可。
预防风险扩散,使用Facade进行访问操作控制。

享元模式

使用共享对象可有效地支持大量的细粒度的对象。
享元模式是以共享的方式高效地支持大量的细粒度对象。享元对象能做到共享的关键是区分内部状态(Internal State)和外部状态(External State)。
内部状态是存储在享元对象内部的、可以共享的信息,并且不会随环境改变而改变。
外部状态是随环境改变而改变且不可以共享的状态。享元对象的外部状态必须由客户端保存,并且享元对象被创建之后,在需要使用的时候再传入到享元对象内部。

优点:大幅减少内存中对象的数量,降低程序内存的占用,提高性能。
缺点:
享元模式增加了系统的复杂性,需要分出外部状态和内部状态,而且内部状态具有固话特性,不应该随外部状态改变而改变,这使得程序的逻辑复杂化。
享元模式将享元对象的状态外部化,而读取外部状态使得运行时间变长。
使用场景:
系统中由大量的相似对象,这些对象耗费大量的内存。
细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,即对象没有特定身份。
需要缓冲池的场景。

模板方法模式

定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
优点:
封装不变的部分,扩展可变部分。不变的部分封装到父类中实现,而可变的部分则通过继承进行扩展。
提取公共部分代码,便于维护。将公共部分的代码抽取出来放在父类中,维护时只需要修改父类中的代码。
行为由父类控制,子类实现。

应用场景:
多个子类有公共方法,并且逻辑基本相同。
可以把重要的、复杂的、核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。
重构时,模板方法模式是一个经常使用的模式,将相同的代码抽取到父类中。

命令模式

将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
优点:
类间解耦。调用者与接收者之间没有任何依赖关系。
可扩展性。
命令模式结合其他模式会更优秀。
缺点:命令模式可能会导致系统中出现过多的具体命令类,因此需要再项目中慎重考虑使用。

责任链模式

使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象练成一条链,并沿着这条链传递该请求,直到由对象处理它为止。
优点:
责任链模式将请求和处理分开,请求者不知道是谁处理的,处理者可以不用知道请求者的全貌。
提高系统的灵活性。
缺点:
降低程序的性能,每个请求都是从链头遍历到链尾,当链比较长的时候,性能会大幅下降。
不易于调试,由于采用了类似递归的方式,调试的时候逻辑比较复杂。
应用场景:
一个请求需要一系列的处理工作。
业务流的处理。
对系统进行补充扩展。

策略模式

定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。
优点:
策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族,恰当地使用继承可以把公共的代码移到父类中,从而避免代码重复。
策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为。
使用策略模式可以避免使用多重条件转移语句。
缺点:
客户端必须知道所有的策略类,并自行决定使用哪个策略类。
策略模式造成很多的策略类。
应用场景:
多个类只是在算法或行为上稍有不同的场景。
算法需要自由切换的场景。
需要屏蔽算法规则的场景。

迭代器模式

提供一种方法访问一个容器对象中各个元素,而又不需要暴露该对象内部细节。
优点:
迭代器模式简化了访问容器元素的操作,具备一个统一遍历接口。
封装遍历算法,使算法独立于聚集角色。客户无须知道聚集对象的类型,即使聚集对象的类型发生变化,也不会影响遍历过程。
缺点:
迭代器模式给使用者一个序列化的错觉,从而产生错误。
地带其的元素都是Object类型,没有类型特征(JDK1.5后通过引入泛型可以解决此问题)

中介者模式

用一个中介对象封装一系列对象的交互,中介者使各对象不需要显式地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

优点:
减少类间的依赖,将原有的一对多的依赖变成一对一的依赖,使得对象之间的关系更易维护和理解。
避免对象之间过度耦合,类只依赖中介者,使类更容易被复用。
中介者模式将对象的行为和协作抽象化,将对象在小尺度的行为上与其他对象的相互作用分开处理

缺点:
中介者模式降低了对象的复杂性,但增加了中介者类的复杂性。
中介者类经常充满了各个具体类的关系协调代码,这种代码不能复用。

观察者模式

定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。

优点:
观察者和被观察者之间是抽象耦合。
支持广播通信。被观察者会向所有等级过的观察者发出通知,这就是一个触发机制,形成一个触发链。
缺点:
如果一个主题有多个直接或间接的观察者,则通过所有的观察者会花费很多时间,且开发和调试都比较复杂。
如果在主题之间有循环依赖,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察者模式时要特别注意这一点。
如果对观察者的通知是通过另外的线程进行异步投递,系统必须保证投递的顺序执行。
虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有提供相应的机制使观察者知道所观察的对象是如何发生变化。

使用场景:
关联行为场景。
时间多级触发场景。
跨系统的消息交换场景,如消息队列的处理机制。

注意:
广播链问题。一个观察者可以有双重身份,既是观察者也是被观察者,广播链一旦建立,逻辑就比较复杂,可维护性非常差。
异步处理的问题。异步处理就要考虑线程安全和队列问题。
观察者广播链和责任链模式的最大区别就是观察者广播链在传播的过程中,消息是随时更改的,是由相邻的两个节点协商的消息结构;而责任链模式在消息传递过程中,消息是保持不变 的,如果要改变,也只有在原有消息上进行修正。

备忘录模式

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
使用场景:
需要保存和恢复数据的相关状态场景。
提供一个可回滚的操作。
需要监控副本的场景。
数据库连接的事务管理使用的就是备忘录模式。
注意:
备忘录的声明周期,备忘录创建出来就要在最近的代码中使用,不适用就要删除引用。
备忘录的性能。不要在频繁建立备份的场景使用备忘录模式。

访问者模式

封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
优点:
访问者模式使得增加新的操作变得很容易,增加新的操作只需要增加新的访问者类。
访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个元素类中。
访问者模式可以跨过几个类的等级结构访问属于不同等级结构的成员类。
累计状态。每一个单独的访问者对象都集中了相关的行为,从而就可以在访问的过程中将执行操作的状态累计在自己内部,而不是分散到很多的元素对象中,易于系统的维护。
缺点:
增加新的元素类变得困难。
破坏封装。
违背了依赖倒置原则。

状态模式

当一个对象内在状态改变时允许改变行为,这个对象看起来像改变了其类型。

优点:
结构清晰
遵循设计原则
封装性非常好
缺点:子类太多,不易管理。

解释器模式

给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
优点:
简单的语法分析工具。
扩展性,修改语法规则只要修改相应的非终结符表达式即可,若扩展语法,则只要增加非终结符类即可。
缺点:
解释器模式会引起膨胀。每个语法都要产生一个非终结符表达式,语法比较复杂时就可能产生大量的类文件,不易维护。
采用递归调用方法。不易调试且影响效率。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值