什么是设计模式?
设计模式(Design Pattern) ,这个术语最初并不是出现在软件设计中,而是被用于建筑领域的设计中,后来随着软件工程的发展,设计模式思想被引入到软件工程中。直到今天,狭义的设计模式还是人们广为流传的 23 种经典设计模式。
设计模式(Design pattern)代表了计算机软件工程最佳的实践,是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。
在当今大型工程软件体系的发展背景下,不管是设计模式也好,其他工程模式也罢,都是为了解决问题而发明的有效方法。除了常见的的23种设计模式以外,还有 MVVM、MVC、Combinator 等,都是前辈们经过多年的摸爬滚打总结出来的,其有效性不容置疑。对于传统简单的程序开发,写一个简单的算法可能比引入某种设计模式更容易实现,但对于大型项目开发或框架设计,用设计模式来组织代码才是主要方向。
合理使用设计模式
曾有人这样说:
设计模式是为了封装变化,让各个模块可以独立变化。精准地使用设计模式的前提是你能够精准的预测需求变更的走向。
我们都知道大部分人是做不到的,所以大部分人就算精通设计模式也多少会做错点什么东西。所以这其实不怪设计模式。所以说如何避免过度设计,这就要求你深入的理解你的程序所在的领域的知识,了解用户使用你的软件是为了解决什么问题,这样你预测用户的需求才会比以前更加准确,从而避免了你使用设计模式来封装一些根本不会发生的变化,也避免了你忽视了未来会发生的变化从而发现你使用的模式根本不能适应需求的新走向。
所以,在你满足了【知道所有设计模式为什么要被发明出来】的前提之后,剩下的其实都跟编程没关系,而跟你的领域知识和领域经验有关系。
我认为说的很对,合理使用设计模式应该基于对工程的完全理解。
设计模式的七大原则
俗话说得好,没有规矩不成方圆,设计模式也要遵循一些准则。一般地,我们在设计软件工程的时候需要遵循七项基本原则,分别是:
- 单一职责原则(Single Responsibility)
- 接口隔离原则(Interface Segregation)
- 依赖倒置原则(Dependence Inversion)
- 里氏替换原则(Liskov Substitution)
- 开闭原则(Open Close)
- 迪米特法则(Demeter)
- 合成复用原则(Composite Reuse)
设计模式包含了面向对象的精髓,懂了设计模式就懂了面向对象分析(OOA)和设计(OOD)的精要。
1. 单一职责原则
对于类来说,一个类应该只负责一项职责。
- 降低类的复杂度。
- 提高类的可读性,可维护性。
- 降低变更引起的风险。
- 通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级别违反单一职责原则;只有类中的方法足够少,可以在方法级别违反单一职责原则。
2. 接口隔离原则
一个类不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。
3. 依赖倒置原则 (面向接口编程)
- 高层模块不应该依赖低层模块,二者都应该依赖其抽象。
- 抽象不应该依赖细节,细节应该依赖抽象。
- 依赖倒转(倒置)的中心思想是面向接口编程。
- 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在 Java 中,抽象指的是接口或抽象类,细节就是具体的实现类。
- 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
4. 里氏替换原则 (正确使用继承)
里氏替换原则,主张使用“抽象(Abstraction)”和“多态(Polymorphism)”将设计中的静态结构改为动态结构,维持设计的封闭性。“抽象”是语言提供的功能。“多态”由继承语义实现。
里氏替换原则的内容可以描述为: “派生类(子类)对象可以在程式中代替其基类(超类)对象。”
- 如果对每个类型为 T1 的对象 object1 ,都有类型为 T2 的对象 object2 ,使得以 T1 定义的所有程序 P 在所有的对象 object1 都代换成 object2 时,程序 P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象。
- 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法。
- 里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过 聚合、组合、依赖 来解决问题。
5. 开闭原则 (编程中最基础、最重要的设计原则)
- 一个软件实体如类,模块和函数应该对扩展开放,对修改关闭。用抽象构建框架,用实现扩展细节。
- 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
- 编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。
违反开闭原则的示例代码
GraphicEditor 类违反了开闭原则。
// 主方法
public class Ocp {
public static void main(String[] args) {
//使用看看存在的问题
GraphicEditor graphicEditor = new GraphicEditor();
graphicEditor.drawShape(new Rectangle());
graphicEditor.drawShape(new Circle());
graphicEditor.drawShape(new Triangle());
}
}
//这是一个用于绘图的类 [使用方]
class GraphicEditor {
//接收Shape对象,然后根据type,来绘制不同的图形
public void drawShape(Shape s) {
if (s.m_type == 1)
drawRectangle(s);
else if (s.m_type == 2)
drawCircle(s);
else if (s.m_type == 3)
drawTriangle(s);
}
//绘制矩形
public void drawRectangle(Shape r) {
System.out.println(" 绘制矩形 ");
}
//绘制圆形
public void drawCircle(Shape r) {
System.out.println(" 绘制圆形 ");
}
//绘制三角形
public void drawTriangle(Shape r) {
System.out.println(" 绘制三角形 ");
}
}
//Shape类,基类
class Shape {
int m_type;
}
class Rectangle