设计模式总结
设计模式的六大原则
开放-封闭原则(OCP) (总原则)
Open-Close Principle:该对扩展开放,对修改关闭。
目的就是保证程序的扩展性好,易于维护和升级。
开放-封闭原则是面向对象设计的核心所在, 开闭原则是Java世界里最基础的设计原则。
开闭原则被称为面向对象设计的基石,实际上,其他原则都可以看作是实现开闭原则的工具和手段。通俗来说就是,开发一个软件时,应该对其进行功能扩展,而在进行这些扩展时,不需要对原来的程序进行修改。
单一职责原则(SRP)
Single-Responsibilitiy Principle:对一个类而言,应该仅有一个引起它变化的原因。
优点:
1.降低类的复杂度
2.提高类的可读性,提高系统的可维护性。
3.降低变更引起的风险。
单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都适用单一职责原则。单一职责适用于接口,类,同时也适用于方法。
在类中新加一个方法的修改方式,虽然也违背了SRP,但在方法级别上却是符合SRP的,因为它并没有动原来方法的代码。在开发中,需要根据实际情况来确定。但对于接口一定要做到单一职责,对于类的设计尽量做到只有一个原因引起变化。
里氏替换原则(LSP)
Liskov Substitution Principle:一个软件实体如果适用一个父类的话,那一定是适用于其子类,所有引用父类的地方必须能透明地使用其子类的对象,子类对象能够替换其父类对象,而不改变程序逻辑。
要满足上面的要求那就需要
- 子类可以扩展父类的功能,但是不能改变父类原有的功能。
- 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。(但在实际代码开发过程中,可能需要在某些特殊情景下重写父类的某个方法,不建议,但是为了代码的统一这么写个人也是觉得可以接受的)
依赖倒置原则(DIP)
Dependence Inversion Principle:是一个类与类之间的调用规则。这里的依赖就是代码中的耦合。高层模块不应该依赖底层模块,二者都应该依赖其抽象;抽象不依赖细节;细节应该依赖抽象、接口编程。
问题由来:
类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。
解决方案:将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。
依赖倒置原则基于这样一个事实:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。在java中,抽象指的是接口或者抽象类,细节就是具体的实现类,使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
依赖倒置原则的核心思想是面向接口编程。
在实际编程中,我们一般需要做到如下2点:
- 低层模块尽量都要有抽象类或接口,或者两者都有。
- 变量的声明类型尽量是抽象类或接口。
接口隔离原则(ISP)
Interface Segregation Principle:用于恰当的划分角色和接口,具有两种含义:1、用户不应该依赖它不需要的接口;2、类间的依赖关系应该建立在最小的的接口上。
单一职责原则,要求行为分离,接口细化,感觉有些相同。但实际上,单一职责原则要求类与接口的职责单一,注重的是职责,没有要求接口尽量的少。
在接口隔离原则中,要求尽量使用多个专门的接口。专门的接口也就是提供给多个模块的接口。提供给几个模块就应该有几个接口,而不是建立一个臃肿庞大的接口,所有的模块都可以访问。
但是接口的设计是有限度的。接口的设计粒度越小系统越灵活,但是接口太多也就使得结构复杂,维护难度大。因此实际中,怎样把握就靠开发的经验和常识了。
迪米特原则(LoD)
Law of Demeter(最小知识原则):一个对象应该对其他对象有最少的了解。通俗来说就是,一个类对自己需要耦合或者调用的类知道的尽可能的少。
迪米特原则核心观念就是:类间解耦,弱耦合。
从迪米特法则的定义和特点可知,它强调以下两点:
从依赖者的角度来说,只依赖应该依赖的对象。
从被依赖者的角度说,只暴露应该暴露的方法。
迪米特法则要求限制软件实体之间通信的宽度和深度,正确使用迪米特法则将有以下两个优点:
- 降低了类之间的耦合度,提高了模块的相对独立性。
- 由于亲合度降低,从而提高了类的可复用率和系统的扩展性。
合成复用原则(CARP)
Composite/Aggregate Reuse Principle :尽量使用组合实现代码复用,而不使用继承。
继承复用子类和父类的耦合度很高,如果修改了父类,就需要修改所有的子类。
不过个人认为在业务开发过程中使用模板方法的时候就需要通过继承来复用,有时候也会引入一些不必要的接口参数,这个就看自己的衡量了
设计模式总结
设计模式要⼲的事情就是解耦。
可分为三大类:
- 创建型模式是将创建和使⽤代码解耦
- 结构型模式是将不同功能代码解耦
- ⾏为型模式是将不同的⾏为代码解耦
创建型模式
共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
结构型模式
共七种:适配器模式、装饰器模式、代理模式、桥接模式、组合模式、享元模式、门面模式。
行为型模式
共十一种:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、状态模式、访问者模式、中介者模式、解释器模式、命令模式、备忘录模式。
还有一种不在23种设计模式种的委派模式
设计模式使用频率
类型 | 高频 | 低频 |
---|---|---|
创建型模式 | 单例模式,工厂方法模式,建造者模式 | 原型模式、抽象工厂模式 |
结构型模式 | 适配器模式、装饰器模式、代理模式、门面模式 | 桥接模式、组合模式、享元模式 |
行为型模式 | 策略模式、模板方法模式、观察者模式、责任链模式 | 迭代器模式(一般作为基础类库提供)、访问者模式、中介者模式、状态模式、解释器模式、命令模式、备忘录模式 |
不同设计模式比较
每个设计模式都应该由两部分组成:第⼀部分是应⽤场景,即这个模式可以解决哪类
问题;第⼆部分是解决⽅案,即这个模式的设计思路和具体的代码实现。
如果我们只从设计模式的设计思路来看,就会觉得很多设计模式看起来都很类似。但是,设计模式之间的主要区别还是在于设计意图,也就是应用场景。
单纯地看设计思路或者代码实现,有些模式确实很相似,⽐如策略模式和⼯⼚模式。
策略模式从代码结构上来,它⾮常像⼯⼚模式,都包含三部分: 策略(工厂对象)的定义、创建和使⽤。
它们的区别在于,策略模式侧重算法的应⽤场景,⽤来解决根据运⾏时状态从⼀组策略中选择合适的策略的问题,⽽⼯⼚模式侧重封装对象的创建过程,这⾥的对象没有任何业务场景的限定,可以是策略,但也可以是其他东⻄。从设计意图上来,这两个模式完全是两回事⼉。
命令模式VS策略模式
在策略模式中,不同的策略具有相同的⽬的、不同的实现、互相之间可以替换。⽐如,BubbleSort、SelectionSort 都是为了实现排序的,只不过⼀个是⽤冒泡排序算法来实现的,另⼀个是⽤选择排序算法来实现的。
⽽在命令模式中,不同的命令具有不同的⽬的,对应不同的处理逻辑,并且互相之间不可替换。
参考文献:
设计模式六大原则
实际开发过程中,并不一定是要求所有代码都遵循上述设计原则,要考虑人力,时间,质量成本,要在适当的使用场景遵循设计原则,不能为了设计而设计。所有的设计模式和设计原则最终都是为了服务于业务。