什么是设计模式
在日常生活中每个人都会遇到各种各样的问题, 经过归类和分析后发现, 很多问题都可以使用相同的方式方法来解决, 将这些方式方法整理就产生了设计模式. 简单说, 设计模式是解决通用问题的模板. 每个领域都有自己的设计模式, 在计算机领域设计模式主要分三大类:
- 创建型设计模式 - 提供创建对象的机制, 增加已有代码的灵活性和可复用性。
- 结构型设计模式 - 如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。
- 行为设计模式 - 对象间的高效沟通和职责委派。
本文在主要体现创建型设计模式.
工厂方法模式
模式意图
在父类提供创建对象的方法, 允许子类决定实例化对象类型.
模式结构
使用实例
class Pet {
say();
}
class Dog extends Pet {
say() {
print("A Dog say")
}
}
class Cat extends Pet {
say() {
print("A Cat say")
}
}
class PetSaied {
main() {
String petType = "Dog";
Pet pet = createPet(petType);
Optional.isNullable(pet).say();
String petType = "Cat";
Pet pet = createPet(petType);
Optional.isNullable(pet).say();
}
createPet(String petType) {
Pet pet = null;
switch(petType) {
case "Dog":
pet = new Dog();
break;
case "Cat":
pet = new Cat();
break;
}
return pet;
}
}
适合应用场景
- 当你在编写代码的过程中, 如果无法预知对象确切类别及其依赖关系时, 可使用工厂方法。
- 如果你希望用户能扩展你软件库或框架的内部组件, 可使用工厂方法。
- 如果你希望复用现有对象来节省系统资源, 而不是每次都重新创建对象, 可使用工厂方法。
Java中的使用
核心 Java 程序库中有该模式的应用:
- java.util.Calendar#getInstance()
- java.util.ResourceBundle#getBundle()
- java.text.NumberFormat#getInstance()
- java.nio.charset.Charset#forName()
- java.net.URLStreamHandlerFactory#createURLStreamHandler(String) (根据协议返回不同的单例对象)
- java.util.EnumSet#of()
- javax.xml.bind.JAXBContext#createMarshaller() 及其他类似的方法。
抽象工厂模式
模式意图
抽象工厂模式是一种创建型设计模式, 它能创建一系列相关的对象, 而无需指定其具体类。
模式结构
使用实例
适合应用场景
- 如果代码需要与多个不同系列的相关产品交互, 但是由于无法提前获取相关信息, 或者出于对未来扩展性的考虑, 你不希望代码基于产品的具体类进行构建, 在这种情况下, 你可以使用抽象工厂。
- 如果你有一个基于一组抽象方法的类, 且其主要功能因此变得不明确, 那么在这种情况下可以考虑使用抽象工厂模式。
Java中的使用
以下是来自核心 Java 程序库的一些示例:
生成器模式
模式意图
能够分步骤创建复杂对象。 该模式允许你使用相同的创建代码生成不同类型和形式的对象。
模式结构
使用实例
适合应用场景
- 使用生成器模式可避免 “重叠构造函数 (telescoping constructor)” 的出现。
- 当你希望使用代码创建不同形式的产品 (例如石头或木头房屋) 时, 可使用生成器模式。
- 使用生成器构造组合树或其他复杂对象
Java中的使用
生成器在 Java 核心程序库中得到了广泛的应用:
- java.lang.StringBuilder#append() (
非同步
) - java.lang.StringBuffer#append() (
同步
) - java.nio.ByteBuffer#put() (还有 CharBuffer、 ShortBuffer、 IntBuffer、 LongBuffer、 FloatBuffer 和 DoubleBuffer)
- javax.swing.GroupLayout.Group#addComponent()
- java.lang.Appendable的所有实现
原型模式
模式意图
能够复制已有对象, 而又无需使代码依赖它们所属的类。
模式结构
使用实例
适合应用场景
- 如果你需要复制一些对象, 同时又希望代码独立于这些对象所属的具体类, 可以使用原型模式。
- 如果子类的区别仅在于其对象的初始化方式, 那么你可以使用该模式来减少子类的数量。 别人创建这些子类的目的可能是为了创建特定类型的对象。
Java中的使用
Java 的 Cloneable
(可克隆) 接口就是立即可用的原型模式。任何类都可通过实现该接口来实现可被克隆的性质。
java.lang.Object#clone() (类必须实现 java.lang.Cloneable 接口)
识别方法: 原型可以简单地通过 clone
或 copy
等方法来识别。
单例模式
模式意图
保证一个类只有一个实例, 并提供一个访问该实例的全局节点。
模式结构
使用实例
// 数据库类会对`getInstance(获取实例)`方法进行定义以让客户端在程序各处
// 都能访问相同的数据库连接实例。
class Database is
// 保存单例实例的成员变量必须被声明为静态类型。
private static field instance: Database
// 单例的构造函数必须永远是私有类型,以防止使用`new`运算符直接调用构
// 造方法。
private constructor Database() is
// 部分初始化代码(例如到数据库服务器的实际连接)。
// ……
// 用于控制对单例实例的访问权限的静态方法。
public static method getInstance() is
if (Database.instance == null) then
acquireThreadLock() and then
// 确保在该线程等待解锁时,其他线程没有初始化该实例。
if (Database.instance == null) then
Database.instance = new Database()
return Database.instance
// 最后,任何单例都必须定义一些可在其实例上执行的业务逻辑。
public method query(sql) is
// 比如应用的所有数据库查询请求都需要通过该方法进行。因此,你可以
// 在这里添加限流或缓冲逻辑。
// ……
class Application is
method main() is
Database foo = Database.getInstance()
foo.query("SELECT ……")
// ……
Database bar = Database.getInstance()
bar.query("SELECT ……")
// 变量 `bar` 和 `foo` 中将包含同一个对象。
适合应用场景
- 如果程序中的某个类对于所有客户端只有一个可用的实例, 可以使用单例模式。
- 如果你需要更加严格地控制全局变量, 可以使用单例模式。
Java中的使用
尽管如此, Java 核心程序库中仍有相当多的单例示例: