20世纪90年代,由建筑领域引入软件设计中。
1995年,艾瑞克·伽马(ErichGamma)、理査德·海尔姆 (Richard Helm)、拉尔夫·约翰森(Ralph Johnson)、约翰·威利斯迪斯(JohnVlissides)等 4 位作者合作出版了《设计模式:可复用面向对象软件的基础》 一书,在此书中收录了 23 个设计模式,这是设计模式领域里程碑的事件。
软件设计模式(Software Design Pattern)就是对以前的程序员在实际开发中遇到的问题,进行总结优化,这些总结优化出来的解决方案就是软件设计模式。
1.就是对面向对象设计的应用,对面向对象设计原则的应用,对封装,继承,多态,类之间关系的应用。
2.可以提高设计能力,有助于阅读源码,提升程序设计的可维护性,可复用性,可扩展性(可扩展性一般指在添加新功能时,使代码改动成本最低,且不影响之前功能)。
一个开发人员进行设计的语言,使用各种图形符号来表示各模块之间的关系 。
类图(Class diagram)是显示了模型的静态结构,类、类 的内部结构以及它们与其他类的关系,不显示暂时性的信息。
类是指具有相同属性、方法和关系的对象的抽象,它封装了数据和行为,是 面向对象程序设计(OOP)的基础,具有封装性、继承性和多态性等三大特性。
接口(Interface)是一种特殊的类,它具有类的结构但不可被实例化,只可以 被子类实现。它包含抽象操作,但不包含属性。
在一个类的方法中使用到了另一个类,具有临时性(方法使用结束就不使用了),低耦合。用带箭头的虚线来表示。
把一个类当作另一个类的属性,分为单向关联,双向关联,自关联(单例模式)。用一个带箭头的实线表示。
聚合关系是关联关系的一种,是强关联关系。成员对象是整体对象的一部分,但是 成员对象可以脱离整体对象而独立存在。例如,学校与老师的关系,学校包含老 师,但如果学校停办了,老师依然存在。用带空心菱形的实线来表示。
组合也是关联关系的一种,是一种更强烈的聚合关系。一旦整体对象不存在, 部分对象也将不存在,部分对象不能脱离整体对象而存在。例如,头和嘴的关系, 没有了头,嘴也就不存在了。用带实心菱形的实线来表示。
是父 类与子类之间的关系,是一种继承关系,是 is-a 的关系。用带空心三角箭头的实线来表示。
类实现了接口,类中的操作实现了接口中所声明的所有的抽象操作。用带空心三角箭头的虚线来表示。
一个类不要负责太多事情,否则类内部耦合度太高,不利于扩展。高内聚,低耦合。
程序扩展功能时,不建议修改原来代码,建议扩展一个新的类来实现新功能。
在使用继承关系时,在子类中重写父类方法,不要在重写后对子类运行结果产生影响,子类原来调用父类的方法,方法重写后,子类可能会调错,产生影响。
有多个同类型事物时,可以抽取一个共同抽象层,具体实现细节应依赖于抽象。
根据功能设计接口,不要将所有功能设计到一个接口中,不用是想一些不必要的功能。
最少了解原则,一个类最好只与自己相关类产生依赖,尽量不使用与自己没有直接关系的类,只与直接朋友交谈,不与陌生人交谈。
在某些情况下,B类想用A类中的方法,一般使用继承。但现在不使用继承,可以用关联的方式,调用A类中的方法;还可以在B类中依赖A类,在B类的一个方法中使用A类的方法(觉得这个更好)。
开闭原则:要求对扩展开放,对修改关闭
里氏替换原则:不要破坏继承体系
依赖倒置原则:要求面向接口编程
单一职责原则:实现类职责要单一
接口隔离原则:在设计接口的时候要精简单一
迪米特法则:只与直接的朋友的通信
合成复用原则:尽量使用聚合和组合的方式,而不是使用继承
遵循设计原则:就是为了让程序高内聚,低耦合
1. 单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局 访问点供外部获取该实例,其拓展是有限多例模式。
2. 原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
3. 工厂方法(Factory Method)模式:定义一个用于创建产品的接口,由子 类决定生产什么产品。
4. 抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个 子类可以生产一系列相关的产品。
5. 建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然 后根据不同需要分别创建它们,最后构建成该复杂对象。
6. 代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客 户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
7. 适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口, 使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
8. 桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组 合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
9. 装饰(Decorator)模式:动态的给对象增加一些职责,即增加其额外的功能。
10. 外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子 系统更加容易被访问。
11. 享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。
12. 组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象 和组合对象具有一致的访问性。
13. 模板方法(TemplateMethod)模式:定义一个操作中的算法骨架,而将算 法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定 义该算法的某些特定步骤。
14. 策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它 们可以相互替换,且算法的改变不会影响使用算法的客户。
15. 命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任 和执行请求的责任分割开。
16. 职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下 一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。
17. 状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。
18. 观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改 变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。
19. 中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互 关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
20. 迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数 据,而不暴露聚合对象的内部表示。
21. 访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个 元素提供多种访问方式,即每个元素有多个访问者对象访问。
22. 备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对 象的内部状态,以便以后恢复它。
23. 解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的 解释方法,即解释器。
用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使 用分离”。提供了单例、原型、工厂方法、抽象工厂、建造者 5 种创建型模式。
用于描述如何将类或对象按某种布局组成更大的结构,提供了代理、 适配器、桥接、装饰、外观、享元、组合 7 种结构型模式。
用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独 完成的任务,以及怎样分配职责。提供了模板方法、策略、命令、职责链、状态、 观察者、中介者、迭代器、访问者、备忘录、解释器 11 种行为型模式。
解决一个类在一个程序中保证只能创建一个类。(也是适用的方式)
例如:Windows任务管理器窗口,只需创建一个
只创建一个对象;对象由单例类自己创建;向外提供访问方法。
饿汉式:在类加载时就把唯一的对象创建好,且不存在线程安全问题。但启动系统时加载的东西很多。
懒汉式:不是在类加载时,第一次获取单例对象时,创建对象。但存在线程安全问题,加锁可以解决此类问题。双重检索+volatile。
//懒汉模式
public class WindowDome {
static volatile WindowDome windowDome;//volatile修饰成员变量对其操作时禁止指令重排序
private WindowDome(){
System.out.println("WindowDome");
}
/*
有线程安全问题
public WindowDome getWindowDome(){
if (windowDome==null){
windowDome = new WindowDome();
}
return windowDome;
}
*/
/*
加锁是解决了线程安全问题,但并发情况效率低
public synchronized WindowDome getWindowDome(){
if (windowDome==null){
windowDome = new WindowDome();
}
return windowDome;
}
*/
//用双重检索可解决高并发情况下线程安全问题
public WindowDome getWindowDome(){
if (windowDome==null){//第一波线程到来时全部进去,第二波到来时windowDome已经建立好了。
synchronized(WindowDome.class){
if (windowDome==null){//第一波第一个线程进来就创建,其余的都不符合条件就不创建对象了。
windowDome = new WindowDome();
}
}
}
return windowDome;
}
}
Runtime 类属于典型的单例设计,利用 Runtime 类可以启动新的进 程或进行相关运行时环境的操作。比如,取得内存空间以及释放垃圾空间。
简单工厂模式并不是 23 种设计模式之一,因为它违背了开闭原则,添加一个产品就需要修改一个工厂代码。
有一个工厂类负责生产某一类产品,同一产品具备同一个抽象父类(抽象类,接口),将创建的对象与使用的对象分离(spring框架就是这种设计)。
工厂角色:负责实现创建所有实例的内部逻辑。工厂类提供静态方法,可以被外界直接调用,创建所需的产品对象。
抽象产品角色:描述所有实例共有的接口。
具体产品角色:创建目标。
实现了创建和调用的分离,降低了客户端代码的难度。 工厂类专门负责对象的创建;客户端只负责对象的调用。
违反了开闭原则,不利于后期维护。增加和减少产品类,需要修改工厂类;如果产品类过多,就会导致工厂类过于庞大。
对工厂进行抽象,一个抽象产品对应一个抽象工厂,一个具体产品对那个一个具体工厂。需要扩张新产品时,只需要添加新具体产品类和生产该产品的具体工厂类。就不必修改原来的工厂。
符合开闭原则。
抽象工厂角色:任何在模式中创建的对 象的工厂类必须实现这个接口。
具体工厂角色:实现抽象工厂接口的具体工厂类。
抽象产品角色:产品对象的共同父 类或共同拥有的接口。
具体产品角色:实现了抽象产品角色所定义的接口。
在简单工厂模式优点的基础上新增:若增加和减少产品子类, 不需修改工厂类,只增加产品子类和工厂子类,符合开闭原则即使产品子类过多, 不会导致工厂类的庞大,利于后期维护。
需要额外的编写代码,增加了工作量。
是生产一系列产品(某个品牌,例如:华为手机,华为汽车),在抽象从工厂中定义生产不同产品,具体工厂负责生产一个公司的系列产品。具体工厂负责生产一个公司的一系列产品。
一个具体的工厂负责创建一系列相互关联的产品。会简化 客户端的调一用。并且更换产品系列非常方便,更换一个工厂类即可。
抽象工厂、具体工厂、抽象产品、具体产品。
获取具体系列产品只需要通过具体系列工厂获取,无序关心创建的细节。
在开发过程中,需要创建多个数据相同的对象,每次使用new 创建开销比较大,使用对象克隆,以先创建出来的原型对象为模板进行对象复制,提高创建效率。用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对 象相同的新对象。
例如:建立复制,奖状复制。
抽象原型类:规定了具体原型对象必须实现的的 clone()方法。
具体原型类:实现抽象原型类的 clone()方法,它是可被复制的对象。
访问类:使用具体原型类中的 clone()方法来复制新的对象。
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本 类型属性,仍指向原有属性所指向的对象的内存地址。
深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原 有对象地址。