一、软件设计模式概述
设计模式(Design Pattern)是对代码开发经验的总结,用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
1995 年,GoF(Gang of Four,四人组/四人帮)合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了 23 种设计模式,从此树立了软件设计模式领域的里程碑,人称「GoF设计模式」.
这23种设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性、多态性,以及类的关联关系和组合关系的充分理解。
设计模式只是一个引导,在实际软件开发中,根据具体的需求选择,也可以用于其他面向对象的程序设计语言。
- 可以提高程序员的思维能力、编程能力和设计能力。
- 使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
- 使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。
软件设计模式的基本要素
- 模式名称(pattern name)
- 问题(problem)
- 解决方案(Solution)
- 效果(Conquence)
二、设计模式的分类
1.根据目的来分
- 创建型模式
用于描述怎样创建对象,将对象的创建与使用分离,有单例、原型、工厂方法、抽象工厂和建造者等五种创建型模式。 - 结构型模式
用于描述如何将类与对象按某种布局组成更大的结构,有代理、适配器、桥接、装饰、外观、享元、组合等七种结构型模式。 - 行为型模式
用于描述类与对象如何相互协作完成任务,有模板方法、策略、命令、职责链、状态】观察者、中介者、迭代器、访问者、备忘录、解释器、等十一种行为模型。
2.按作用范围来分
- 类模式
用于处理类与子类之间的关系,这些关系通过静态的继承来建立,有工厂方法、适配器、模板方法解释器等类模式。 - 对象模式
用于处理对象之间的关系,这些关系可以通过组合或聚合来实现,在运行时刻是可以变化的,除以上四种,其他都是对象模式。
设计模式分类表
范围\目的 | 创建型模式 | 结构型模式 | 行为型模式 |
---|---|---|---|
类模式 | 工厂方法 | 适配器 | 模板方法、解释器 |
对象模式 | 单例 原型 抽象工厂 建造者 |
(对象)适配器 桥接 装饰 外观 享元 组合 |
策略 命令 职责链 状态 观察者 中介者 迭代器 访问者 备忘录 |
三、设计模式的功能
- 单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
- 原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
- 工厂方法(Factory Method)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
- 抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
- 建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。
- 代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
- 适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
- 桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
- 装饰(Decorator)模式:动态的给对象增加一些职责,即增加其额外的功能。
- 外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。
- 享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。
- 组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。
- 模板方法(TemplateMethod)模式:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
- 策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
- 命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
- 职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。
- 状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。
- 观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。
- 中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
- 迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
- 访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。
- 备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。
- 解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。
四、常用设计模式详解
4.1单例模式
- 模式定义:保证一个类只有一个实例,并且提供一个全局访问点
- 场景:重量级的对象,不需要多个实例,如线程池、数据库连接池
- 实现方式:
- 懒汉模式:延迟加载,只有真正使用的时候才初始化
单线程演示程序
package edu.sust.designpattern;
public class LazySingletonTest {
public static void main(String[] args) {
LazySingleton instance1 = LazySingleton.getInstance();
LazySingleton instance2 = LazySingleton.getInstance();
System.out.println(instance1 == instance2);//true
}
}
class LazySingleton {
private static LazySingleton instance;//实例
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
多线程环境下
package edu.sust.designpattern;
public class LazySingletonTest {
public static void main(String[] args) {
new Thread(() -> {
LazySingleton instance = LazySingleton.getInstance();
System.out.println(instance);
}).start();
new Thread(() -> {
LazySingleton instance = LazySingleton.getInstance();
System.out.println(instance);
}).start();
}
}
class LazySingleton {
private static LazySingleton instance;//实例
private LazySingleton() {//私有的构造函数,类外无法使用构造实例
}
public static synchronized LazySingleton getInstance() {
if (instance == null) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new LazySingleton();
}
return instance;
}
}
- 饿汉模式:类加载的初始化阶段就完成了实例的初始化,本质上就是借助JVM的类加载机制,保证类的唯一性。
类加载过程:
-
加载二进制数据到内存,生成对应的Class数据结构
-
连接:a.验证 b.准备(给类的静态成员变量赋默认值)c.解析
-
初始化:给类的静态变量赋初值
package edu.sust.designpattern;
public class HungrySingletonTest {
public static void main(String[] args) {
HungrySingleton instance1 = HungrySingleton.getInstance();
HungrySingleton instance2 = HungrySingleton.getInstance();
System.out.println(instance1==instance2);//true
}
}
class HungrySingleton {
private static HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return instance;
}
}
- 静态内部类实现
- 本质上利用类加载机制来保证线程安全
- 只有在实际使用的时候才会触发类的初始化,所以也是懒加载的一种形式
package edu.sust.designpattern;
public class InnerClassSingletonTest {
public static void main(String[] args) {
//单线程环境下
InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
InnerClassSingleton instance2 = InnerClassSingleton.getInstance();
System.out.println(instance1 == instance2);//true
//多线程环境下
new Thread(() -> {
InnerClassSingleton instanceI = InnerClassSingleton.getInstance();
System.out.println(instanceI);
}).start();
new Thread(() -> {
InnerClassSingleton instanceII = InnerClassSingleton.getInstance();
System.out.println(instanceII);
}).start();
}
}
class InnerClassSingleton {
private static class InnerClassHolder {//静态内部类
private static InnerClassSingleton instance = new InnerClassSingleton();
}
private InnerClassSingleton() {
}
public static InnerClassSingleton getInstance() {
return InnerClassHolder.instance;
}
}
4.2工厂方法模式
- 模式定义:一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法模式让类的实例化延迟到子类。
- 应用场景:
- 当你不知道该使用对象的确切类型的时候
- 当你希望为库或框架提供扩展其内部组件的方法时
- 主要有点:
- 将具体产品和创建者解耦
- 符合单一职责原则
- 符合开闭原则
- 面向实现的编程
package edu.sust.designpattern;
public class FactoryMethod {
public static void main(String[] args) {
Application application = new Application();
ProductA object = application.getObject();
object.method();
}
}
class ProductA {
public void method() {
System.out.println("ProductA.method executed...");
}
}
class Application {
private ProductA createProduct() {
return new ProductA();
}
ProductA getObject() {
ProductA productA = new ProductA();
return productA;
}
}
4.3抽象工厂模式
-
模式定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类
-
应用场景:程序需要处理不同系列的相关产品,但是您不希望不希望它依赖
这些产品的具体实现,可以使用抽象工厂 -
优点:
- 可以确信你从工厂得到的产品彼此是兼容的
- 可以避免具体产品与客户端代码之间的紧密耦合
- 符合单一职责原则
- 符合开闭原则
package edu.sust.designpattern; public class AbstractFactoryTest { public static void main(String[] args) { IDatabaseUtils iDatabaseUtils = new MySQLDatabaseUtils(); IConnection iConnection = iDatabaseUtils.getConnection(); iConnection.connect(); ICommand iCommand = iDatabaseUtils.getCommand(); iCommand.command(); } } interface IConnection { void connect(); } class MySQLConnection implements IConnection { @Override public void connect() { System.out.println("MySQL connected"); } } interface ICommand { void command(); } class MySQLCommand implements ICommand { @Override public void command() { System.out.println("MySQL command"); } } interface IDatabaseUtils { ICommand getCommand(); IConnection getConnection(); } class MySQLDatabaseUtils implements IDatabaseUtils { @Override public ICommand getCommand() { return new MySQLCommand(); } @Override public IConnection getConnection() { return new MySQLConnection(); } }
4.4 建造者模式
-
模式定义:将一个复杂对象的创建与它的表示分离,使得同样的构建过程可以创建不同的表示
-
应用场景:
1.需要生成的对象具有复杂的内部结构
2.需要生成的对象内部属性本身相互依赖
3.与不可变对象配合使用
-
优点:
1.建造者独立,易扩展
2.便于控制细节风险
4.5 原型模式
-
模式定义:指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
-
y应用场景:当代码不应该依赖于需要复制的对象的具体类时,使用原型模式
-
优点:
1.可以不耦合具体类的情况下克隆对象
2.避免重复的初始化代码
3.更方便的构建复杂对象