总览
在软件工程中,设计模式描述了软件设计中最常遇到的问题的既定解决方案。 它代表了经验丰富的软件开发人员经过长期反复试验而形成的最佳实践。
在 Erich Gamma、John Vlissides、Ralph Johnson 和 Richard Helm(也称为 Gang of Four 或 GoF)于 1994 年出版《设计模式:可重用的面向对象软件的元素》一书后,设计模式开始流行。
在本文中,我们将探讨创建设计模式及其类型。 我们还将查看一些代码示例并讨论这些模式适合我们设计的情况。
创建型设计模式
创建型设计模式关注对象的创建方式。 它们通过以受控方式创建对象来降低复杂性和不稳定性。
new 运算符通常被认为是有害的,因为它会将对象分散到整个应用程序中。 随着时间的推移,由于类变得紧密耦合,因此更改实现变得具有挑战性。
创建设计模式通过将客户端与实际初始化过程完全分离来解决这个问题。
在本文中,我们将讨论四种创建型设计模式:
-
单例 – 确保在整个应用程序中最多只存在一个对象实例
-
工厂方法——创建几个相关类的对象,而不指定要创建的确切对象
-
抽象工厂——创建相关依赖对象的族
-
Builder – 使用循序渐进的方法构建复杂对象
现在让我们详细讨论这些模式。
单例设计模式
虽然Singleton模式是由GoF引入的,但是最初的实现在多线程场景中是有问题的。
所以在这里,我们将遵循一种更优化的方法,即使用静态内部类:
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
public static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
在这里,我们创建了一个包含 Singleton 类实例的静态内部类。 它仅在有人调用 getInstance() 方法而不是在加载外部类时创建实例。
这是 Singleton 类的一种广泛使用的方法,因为它不需要同步,是线程安全的,强制执行延迟初始化并且样板代码相对较少。
另外,请注意构造函数具有私有访问修饰符。 这是创建 Singleton 的要求,因为公共构造函数意味着任何人都可以访问它并开始创建新实例。
什么时候使用单例设计模式
-
对于创建成本高的资源(如数据库连接对象)
-
将所有记录器保持为单例是一种很好的做法,这可以提高性能
-
提供对应用程序配置设置的访问的类
-
包含以共享模式访问的资源的类
工厂方法设计模式
工厂设计模式或工厂方法设计模式是 Java 中最常用的设计模式之一。
根据 GoF 的说法,这种模式“定义了一个用于创建对象的接口,但让子类决定实例化哪个类。 Factory 方法允许类将实例化推迟到子类”。
该模式通过创建一种虚拟构造函数将初始化类的责任从客户端委托给特定的工厂类。
为了实现这一点,我们依赖于为我们提供对象的工厂,隐藏了实际的实现细节。 使用通用接口访问创建的对象。
在本例中,我们将创建一个 Polygon 接口,该接口将由几个具体类实现。 PolygonFactory 将用于从该系列中获取对象:
让我们首先创建 Polygon 接口:
public interface Polygon {
String getType();
}
接下来,我们将创建一些实现,例如 Square、Triangle 等,它们实现了这个接口并返回一个 Polygon 类型的对象。
现在我们可以创建一个工厂,将边数作为参数并返回此接口的适当实现:
注意客户端如何依赖这个工厂来给我们一个合适的多边形,而不必直接初始化对象。
什么时候使用工厂方法设计模式
-
当接口或抽象类的实现预计会频繁更改时
-
当当前的实现不能舒适地适应新的变化时
-
当初始化过程比较简单,构造函数只需要少量参数时
抽线工厂设计模式
在上一节中,我们看到了如何使用工厂方法设计模式来创建与单个系列相关的对象。
相比之下,抽象工厂设计模式用于创建相关或依赖对象的系列。 它有时也被称为工厂中的工厂。
《Design Patterns: Elements of Reusable Object-Oriented Software》一书指出,抽象工厂“提供了一个接口,用于创建相关或依赖对象的系列,而无需指定它们的具体类”。 换句话说,这个模型允许我们创建遵循一般模式的对象。
JDK 中抽象工厂设计模式的一个示例是 javax.xml.parsers.DocumentBuilderFactory 类的 newInstance()。
在本例中,我们将创建工厂方法设计模式的两个实现:AnimalFactory 和 ColorFactory。
之后,我们将使用抽象工厂 AbstractFactory 管理对它们的访问:
首先,我们将创建一个 Animal 类家族,稍后将在我们的抽象工厂中使用它。
这是动物接口:
public interface Animal {
String getAnimal();
String makeSound();
}
和一个具体的实现 Duck:
public class Duck implements Animal {
@Override
public String getAnimal() {
return "Duck";
}
@Override
public String makeSound() {
return "Squeks";
}
}
此外,我们可以完全以这种方式创建 Animal 接口(如 Dog、Bear 等)的更具体的实现。
抽象工厂处理依赖对象的族。 考虑到这一点,我们将引入另外一种颜色系列作为具有一些实现(白色、棕色……)的接口。
public interface AbstractFactory<T> {
T create(String animalType) ;
}
接下来,我们将使用我们在上一节中讨论的工厂方法设计模式来实现 AnimalFactory:
public class AnimalFactory implements AbstractFactory<Animal> {
@Override
public Animal create(String animalType) {
if ("Dog".equalsIgnoreCase(animalType)) {
return new Dog();
} else if ("Duck".equalsIgnoreCase(animalType)) {
return new Duck();
}
return null;
}
}
同样,我们可以使用相同的设计模式为 Color 接口实现一个工厂。
设置完所有这些后,我们将创建一个 FactoryProvider 类,该类将为我们提供 AnimalFactory 或 ColorFactory 的实现,具体取决于我们提供给 getFactory() 方法的参数:
public class FactoryProvider {
public static AbstractFactory getFactory(String choice){
if("Animal".equalsIgnoreCase(choice)){
return new AnimalFactory();
}
else if("Color".equalsIgnoreCase(choice)){
return new ColorFactory();
}
return null;
}
}
什么时候抽象工厂设计模式
-
客户端独立于我们如何在系统中创建和组合对象
-
该系统由多个对象族组成,这些族旨在一起使用
-
我们需要一个运行时值来构造一个特定的依赖项
虽然该模式在创建预定义对象时非常有用,但添加新对象可能具有挑战性。 要支持新类型的对象,需要更改 AbstractFactory 类及其所有子类。
Builder设计模式
Builder 设计模式是另一种创建模式,旨在处理相对复杂的对象的构造。
当创建对象的复杂性增加时,Builder 模式可以通过使用另一个对象(builder)来构造对象,从而分离出实例化过程。
然后可以使用此构建器通过简单的逐步方法创建许多其他类似的表示。
GoF 引入的原始 Builder 设计模式侧重于抽象,在处理复杂对象时非常好,但是设计有点复杂。
Joshua Bloch 在他的《Effective Java》一书中介绍了构建器模式的改进版本,它干净、可读性强(因为它使用了流畅的设计)并且从客户的角度来看易于使用。 在本例中,我们将讨论该版本。
此示例只有一个类 BankAccount,其中包含一个构建器作为静态内部类:
public class BankAccount {
private String name;
private String accountNumber;
private String email;
private boolean newsletter;
// constructors/getters
public static class BankAccountBuilder {
// builder code
}
}
请注意,字段上的所有访问修饰符都被声明为私有,因为我们不希望外部对象直接访问它们。
构造函数也是私有的,因此只有分配给此类的 Builder 才能访问它。 构造函数中设置的所有属性都是从我们作为参数提供的构建器对象中提取的。
我们在静态内部类中定义了 BankAccountBuilder:
public static class BankAccountBuilder {
private String name;
private String accountNumber;
private String email;
private boolean newsletter;
public BankAccountBuilder(String name, String accountNumber) {
this.name = name;
this.accountNumber = accountNumber;
}
public BankAccountBuilder withEmail(String email) {
this.email = email;
return this;
}
public BankAccountBuilder wantNewsletter(boolean newsletter) {
this.newsletter = newsletter;
return this;
}
public BankAccount build() {
return new BankAccount(this);
}
}
请注意,我们已经声明了外部类包含的相同字段集。 任何必填字段都需要作为内部类构造函数的参数,而剩余的可选字段可以使用 setter 方法指定。
此实现还通过让 setter 方法返回构建器对象来支持流畅的设计方法。
最后,build 方法调用外部类的私有构造函数,并将自身作为参数传递。 返回的 BankAccount 将使用 BankAccountBuilder 设置的参数进行实例化。
让我们看一个构建器模式的快速示例:
BankAccount newAccount = new BankAccount
.BankAccountBuilder("Jon", "22738022275")
.withEmail("jon@example.com")
.wantNewsletter(true)
.build();
什么时候使用builder设计模式
当创建对象的过程非常复杂,有很多强制和可选参数时
当构造函数参数数量增加导致构造函数列表很大时
当客户端期望构造的对象有不同的表示时
结论
在本文中,我们了解了 Java 中的创建设计模式。 我们还讨论了它们的四种不同类型,即 Singleton、Factory Method、Abstract Factory 和 Builder Pattern,它们的优点、示例以及我们应该何时使用它们。
本文为从大数据到人工智能博主「xiaozhch5」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://lrting.top/backend/3475/
[…] 在上篇文章中我们说到创建者设计模式。 现在我们来看看行为设计模式。 这些模式关注我们的对象如何相互交互或者我们如何与它们交互。 […]