1.设计模式(Design Pattern,DP)
它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案,它是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用,目的是为了提高代码的可重用性,代码的可读性和代码的可靠性。
设计模式的六大原则:
1.单一职责原则;
2.开闭原则;
3.依赖倒置原则;
4.里氏替换原则;
5.迪米特原则;
6.接口隔离原则。
1.1.1开闭原则(OCP)
软件实体应该对扩展开放,对修改关闭;
一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化。
软件实体:
1.项目中划分出的模块;
2.类与接口;
3.方法;
开闭原则实例:
通过扩展子类实现新功能,而不是修改原有的接口或类。
开闭原则的作用:
1.软件测试;(只需要对扩展的代码进行测试)
2.提高代码的可复用性;(粒度越小,可复用性越大)
3.可以提高软件的可维护性;(稳定性与延续性强)
1.1.2单一职责原则(SRP)
职责该如何定义;
单一职责原则(也适用于方法):(核心是控制类的粒度大小)
职责是指类变化的原因,单一职责原则规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分。
单一职责原则的优点:
1.降低类的复杂度,一个类只负责一项职责,逻辑简单;
2.提高类的可读性,复杂度降低,可读性提高;
3.提高系统的可维护性;
4.变更引起的风险降低,修改一个功能可以显著降低对其他功能的影响。
1.1.3里氏替换原则(LSP)
子类可以替换父类;
优点:
1.代码共享,减少了创建类的工作量,每个子类都拥有父类的方法和属性;
2.提高代码的重用性;
3.提高代码的可扩展性,子类可以继承父类,也可以异于父类;
4.提供产品和项目的开放性;
缺点:
1.继承是侵入性的,只要继承就必须拥有父类的所有方法和属性;
2.降低了代码的灵活性,子类必须拥有父类的属性和方法;
3.增加了耦合,当父类的常量,变量或者方法被修改,需要考虑子类的修改;
定义:
1.T使用父类,S使用子类,T与S的行为一致;
2.任何使用父类的地方,都可以将它替换为它的子类,而不发生变化;
原则:
1.子类继承父类的所有属性和方法,可以使用子类替换父类;
2.子类可以重写父类的方法,子类也可以拥有新方法,异于父类;
3.当子类覆盖或者实现父类的方法时,方法的输入参数要比父类的方法输入参数更宽松,返回值更严格;
作用:
1.实现开闭原则的重要方式之一;
2.克服了继承中重写父类造成的可复用性变差的缺点;
3.它是动作正确性的保证,类的扩展不会引入新的错误,降低了代码出错的可能性。
4.子类继承父类时,除了添加新的方法完成新的功能外,尽量不重写父类的方法。
修正方法:取消原来的继承关系,重新设计它们之间的关系;
1.1.4依赖倒置原则(DIP)*
含义:
高层模块不应该依赖低层模块,两者都应该依赖其抽象;
抽象不应该依赖细节;
细节应该依赖抽象。
核心思想:要面向接口编程,不要面向实现编程。
作用:
可以降低类间的耦合性;
可以提高系统的稳定性;
可以减少并行开发引起的风险;
可以提高代码的可读性和可维护性;
**实现方法:**通过面向接口的编程来降低类间的耦合性。
遵循规则:
1.每个类尽量提供接口或抽象类,或者两者都具备;
2.变量的声明类型尽量是接口或者抽象类;
3.任何类都不应该从具体类派生;
4.尽量不要覆写基类的方法;
5.使用继承时结合里氏替换原则。
1.1.5接口隔离原则(ISP)
要求程序员尽量将臃肿庞大的接口拆分成更小的和更具体的接口,让接口中只包含客户感兴趣的方法。
含义:要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有的类去调用。
优点:
1.将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性;
2.接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性;
3.如果接口的粒度大小定义合理,能够保证系统的稳定性;
4.使用多个专门的接口还能体现对象的层次;
5.能减少项目工程中的代码冗余,过大的接口里面通常放置许多不用的方法,当实现这个接口,被迫设计冗余代码;
实现方法:
接口尽量小,一个接口只服务于一个子模块或业务逻辑;
为依赖接口的类定制服务,只提供调用者需要的方法,屏蔽不需要的方法;
了解环境,拒绝盲从;
提高内聚,减少对外交互,让接口用最少的方法完成最多的事情。
1.1.6迪米特原则(LoD)
最少知识原则(最小耦合度):要求一个对象应该对其他对象有最少的了解;
优点:
降低了类之间的耦合度,提高了模块的相对独立性;
由于亲和度降低,从而提高了类的可复用率和系统的扩展性;
(但过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块间的通信效率降低)
实现方法:
1.从依赖者的角度来说,只依赖应该依赖的对象;
2.从被依赖者的角度来说,只暴露应该暴露的方法。
注意点:
1.在类的划分上,应该创建弱耦合的类,类与类之间的耦合越弱,就越有利于实现可复用的目标;
2.在类的结构设计上,尽量降低类成员的访问权限;
3.在类的设计上,优先考虑将一个类设置成不变类;
4.在对其他类的引用上,将引用其他对象的次数降低到最小;
5.不暴露类的属性成员,而应该提供相应的访问器(set和get方法);
6.谨慎使用序列化(Serializable)功能。
1.2.1设计模式-创建型模式
创建型模式:
1.单例模式;
2.工厂方法模式;
3.抽象工厂模式;
4.建造者模式;
5.原型模式。
1.2.2单例模式
定义:
确保某一个类只有一个实例,而且自行实例化并向整个系统提供实例;
特点:
单例类只有一个实例对象;
该单例对象必须由单例类自行创建;
单例类对外提供一个访问该单例的全局访问点;
分类:
1.懒汉式(什么时候用,什么时候创建);
2.饿汉式(一旦加载该类就创建实例);
优点:
内存只有一个实例,减少了内存的开销,尤其式频繁的创建和销毁实例;避免对资源的多重占用;单例模式可以在系统设置全局的访问点,优化和共享资源访问。
缺点:
单例模式一般没有接口,扩展困难,只能修改代码;
与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么实例化。
单例模式-扩展:
用集合扩展;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wzSTyc2g-1608744535522)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20201223154833302.png)]
1.2.3工厂方法模式
定义:
定义一个用户创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类;
主要角色:
抽象工厂;具体工厂;
抽象产品,具体产品。
应用场景:
客户只知道创建产品的工厂名,而不知道具体的产品名;
创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口;
客户不关心创建产品的细节,只关心产品的品牌。
1.2.4抽象工厂模式
定义:为创建一组相关或者相互依赖的对象提供一个接口,而且无须指定它们的具体类。
条件:
系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品;
系统一次只可能消费其中某一族产品,即同族的产品一起使用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HFGE6nrQ-1608744535524)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20201223163015117.png)]
优点:
可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理;
当增加一个新的产品族时,不需要修改原代码,满足开闭原则。
缺点:
当产品族中需要增加一个新的产品时,所有的工厂类需要进行修改。
1.2.5建造者模式
定义:
将一个复杂对象的构造与它的表示分离,使同样的构建过程可以不同的表示,这样的设计模式被称为建造者模式;它将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成,它将变与不变相分离,即产品的组成部分是不变的,但每一个部分都是可以灵活选择的。
优点:
1.各个具体的建造者相互独立,有利于系统的扩展;
2.客户端不必知道产品内部组成的细节,便于控制细节风险。
缺点:
1.产品的组成部分必须相同,限制了其使用范围;
2.如果产品的内部变化复杂,该模式会增加很多的建造者类。
1.3.1代理模式
定义:为其他对象提供一种代理以控制这个对象的访问,这是一个使用频率很高的模式。
一个代理类可以代理多个被委托或者被代理者。
优点:
代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
代理对象可以扩展目标对象的功能;
代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;
缺点:
在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
增加了系统的复杂度。
1.3.2适配器模式
适配器模式是一个补偿模式,通常用来解决接口不相容的问题,一般项目开始的时候用的偏少,大多数是在项目的需求不断变化的时候,技术为了业务服务的,因此业务在变化的时候,对技术也提出了要求,这些时候可能就需要这样的补救模式诞生。
优点:
客户端通过适配器可以透明的调用目标接口;
复用了现存的类,程序员不需要修改原有代码而冲用现有的适配者类;
将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。
缺点:
对类适配器来说,更换适配器的实现过程比较复杂。
1.3.3装饰模式
定义:指在不改变现有对象结构的情况下,动态的给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。
优点:
采用装饰模式扩展对象的功能比采用继承方式更加灵活;
可以设计出多个不同的具体装饰类,创造出多个不同行为的组合;
缺点:
装饰模式增加了许多子类,如果过度使用会使程序变得很复杂。
1.3.4亨元模式
运用共享技术来有效的支持大量细粒度对象的复用,它通过共享已经存在的对象来大幅度减少需要创建的对象数量,避免大量相似类的开销,从而提高系统资源的利用率。
结构:
内部状态:即不会随着环境的改变而改变的可共享部分;
外部状态:指随环境改变而改变的不可以共享的部分,亨元模式的实现要领就是区分应用中的这两种状态,并将外部状态外部化。
优点:
大大减少应用程序创建的对象,相同对象只要保存一份,降低程序的内存占用,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
缺点:
1.为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性;
2.读取亨元模式的外部状态会使得运行时间稍微变长。
1.4.1策略模式
该模式定义了一系列算法,并将每个算法封装起来,使它们可以互相转换,且算法的变化不会影响使用算法的客户,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
优点:
多重条件不易维护,使用策略类可以避免多重条件
提供了一系列的可复用的算法族;
提供相同行为的不同实现;
对开闭原则的完美支持;
把算法的使用放到环境类中,算法的实现转移到具体策略类中,实现二者分离;
缺点:
客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类;
策略模式造成很多的策略类。
1.4.2观察者模式*
定义:
多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都得到通知并被自动更新,又称为发布-订阅模式,模型-视图模式,它是对象行为型模式。
优点:
降低了目标和观察者之间的耦合关系,两者之间是抽象耦合关系;
目标与观察者之间建立一套触发机制;
缺点:
目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用;
当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
1.4.3迭代器模式
定义:提供一种方法去访问一个容器对象中各个元素,而又不需要暴露该对象的内部细节的表示。(为容器服务)
优点:
访问一个聚合对象内容而无须暴露它的内部表示;
遍历任务交由迭代器完成,简化了聚合类;
它支持不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历;
增加新的聚合类和迭代器类都很方便,无须修改原有代码;
封装性良好,为遍历不同的聚合结构提供一个统一的接口;
缺点:
增加了类的个数,在一定程度上增加了系统的复杂性。
1.4.4模板方法模式
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构得情况下重定义该算法得某些特定步骤,使用java的继承机制。
优点:
它封装了不变部分,扩展可变部分,把认为是不变部分的算法封装到父类中实现,把可变部分算法由子类继承实现,便于子类继续扩展。
它在父类中提取了公共的部分代码,便于代码复用;
部分方法由子类实现,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
缺点:
对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象;
父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码的阅读难度。
扩展:(钩子方法)