解读Java中的设计模式

2024-10-30 09:12

本文的先决条件

本文要求您对面向对象编程和核心 Java 有基本的掌握。了解一些 Spring 生态系统和 Spring Security 也可以帮助您理解我们将要介绍的一些示例。

什么是设计模式?

设计模式代表了广泛接受的解决特定编程挑战的解决方案。它们与最佳实践的不同之处在于它们提供有针对性的解决方案而不是一般准则。然而,在不必要的时候实现设计模式可能会不必要地使代码复杂化。设计模式通常分为三个主要类别:行为型、创造型和结构型。

创建设计模式:创建模式处理对象创建机制,尝试以适合情况的方式创建对象。他们的目标是使系统独立于其对象的创建、组合和表示方式。单例、工厂、抽象工厂、构建器;是创造性的设计模式。

结构设计模式:结构模式涉及类和对象如何组合以形成更大的结构。 Adapter和Decorator是结构设计模式。

行为设计模式:行为模式涉及算法和对象之间的职责分配。

观察者模式(Observer Pattern )

观察者模(Observer Pattern)式以松散耦合的方式促进多个对象之间的通信和交互。从本质上讲,此模式涉及一个主题和一个或多个观察者(或侦听器)。当主题发生更改或更新时,它会通知所有注册的观察者,使他们能够做出相应的响应。

该模式解决了几个关键挑战:

防止紧耦合:通过将主题与其观察者解耦,对一个组件的更改不会直接影响另一个组件。这提高了代码库的灵活性和可维护性。

支持多个侦听器:观察者模式允许合并多个观察者,每个观察者对主题状态的不同方面感兴趣。这使得设计更加模块化和可扩展。

动态注册和取消注册:观察者可以动态地添加或从监听者列表中删除。这种灵活性允许对观察者集进行运行时修改,以适应不断变化的需求或运行时条件。

Example

让我们试着用一个例子来理解这一点——

我们有一个支付管理器对象。当付款经理创建付款(使用付款管理器)时,我们必须能够将付款记录到数据库(使用付款事件记录器)。我们还需要向用户显示付款已完成的通知(使用通知管理器)。

图片

付款管理器负责付款,付款事件记录器记录记录,通知管理器通知用户。

public interface PaymentSubject {

    //an interface representing the Subject
}

PaymentSubject 接口由 PaymentManager 实现。

import java.util.List;
import java.util.UUID;
import java.util.ArrayList;
public class PaymentManager implements PaymentSubject{

    private final List<PaymentListener> paymentListeners=new ArrayList<>();

    public void pay(){
      System.out.println("Payment Processing...");
      System.out.println("Payment Complete");
      PaymentEvent paymentEvent=new PaymentEvent(UUID.randomUUID().toString());
      paymentListeners.forEach(e->e.paymentMade(paymentEvent));
    }

    public void registerListener(PaymentListener p){
        paymentListeners.add(p);
    }

    public void unregisterListener(PaymentListener p){
        paymentListeners.remove(p);
    }
}

此类充当观察者模式中的主题。它管理付款并在付款时通知其注册听众。支付经理不知道听众的情况。它只是通知所有听众。

@FunctionalInterface
public interface PaymentListener {
    
    void paymentMade(PaymentEvent paymentEvent);
}

PaymentListener 是一个功能接口,它由任何付款侦听器实现。

public class PaymentEventLogger implements PaymentListener{

    private final String id;

    public PaymentEventLogger(String id) {
        this.id = id;
    }

    @Override
    public void paymentMade(PaymentEvent e) {
        System.out.println("Payment received: "+e.paymentId);
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        PaymentEventLogger other = (PaymentEventLogger) obj;
        if (id == null) {
            if (other.id != null)
                return false;
        } else if (!id.equals(other.id))
            return false;
        return true;
    }

}

PaymentEventLogger 是一个侦听器类,它将事件记录到数据库中。

public class NotificationManager implements PaymentListener{

    @Override
    public void paymentMade(PaymentEvent e) {
       System.out.println("User notified!: "+e.paymentId);
    }

}

通知管理器负责发送付款成功通知。

public class PaymentEvent {
   final  String paymentId;

    public PaymentEvent(String paymentId) {
        this.paymentId = paymentId;
    }  
    
}

可以修改 PaymentEvent 类以处理更多与付款相关的数据。

import java.util.UUID;

public class Main {
    public static void main(String[] args) {
        PaymentManager paymentManager=new PaymentManager();
        paymentManager.registerListener(new NotificationManager());
        PaymentEventLogger p=new PaymentEventLogger(UUID.randomUUID().toString());
        paymentManager.registerListener(p);      
        paymentManager.pay();
        paymentManager.unregisterListener(p);
        paymentManager.pay();

    }
}

在上面的代码中,我们可以看到我们向支付管理器注册了两个监听器。每次付款完成时,这些侦听器都会收到有关该事件的通知。

真实用例

观察者模式广泛应用于 Java 生态系统的各个领域。 Java 的 GUI 工具包 Swing 广泛使用了观察者模式。按钮、文本字段和滚动窗格等组件是监听用户生成的事件(例如按钮单击、鼠标移动)的观察者。然后这些事件被分派给注册的监听器(观察者)进行处理。

Model View Controller (模型视图控制器)

MVC(模型-视图-控制器)是一种架构模式,指导如何在应用程序中组织代码。它主张将责任分为三个主要部分:

模型:模型代表应用程序的数据和业务逻辑。它构建和管理数据,通常包括与数据库或其他数据源的交互。虽然像 Hibernate 这样的工具有助于数据建模和持久化,但模型涵盖了应用程序的整个数据层。

视图:视图负责向用户呈现数据并处理用户界面元素。这包括网页、用户界面和任何数据的可视化表示。

控制器:作为中介,控制器处理用户请求,与模型交互以检索或修改数据,并相应地更新视图。它协调应用程序内的数据和逻辑流。在控制器内,可以利用服务和存储库来管理业务逻辑和数据访问。

真实用例

如果您熟悉 Spring 生态系统,您就会知道在 Web 应用程序中,我们有模型、存储库、服务、控制器和视图等组件。因此,在本例中,实体类和 DTO 是模型的一部分。存储库、服务和控制器是控制器的一部分。网页构成视图。可以像 REST API 一样完全删除视图。

图片

建造者模式(Builder Pattern)

构建器模式属于创建设计模式的类别。它提供了创建具有多个属性的复杂对象的解决方案,为构造对象提供了灵活流畅的接口。创意设计模式的其他示例包括单例模式和工厂模式。

public class Product {
    
    private int id;
    private String name;
    private BigDecimal price;
    private String color;
}

当处理包含大量属性的类时,仅依赖构造函数进行对象实例化会变得很麻烦且不易维护。创建多个构造函数来适应不同的属性组合并不是一个可扩展的解决方案。

public class Product {
    
    private int id;
    private String name;
    private BigDecimal price;
    private String color;

    public static class Builder {
        private Product product = new Product();

        public Builder id(int id){
            product.id = id;
            return this;
        }

        public Builder name(String name){
            product.name = name;
            return this;
        }

        public Builder price(BigDecimal price){
            product.price = price;
            return this;
        }

        public Builder color(String color){
            product.color = color;
            return this;
        }

        public Product build(){
            return product;
        }
    }
}

构建器模式通过提供灵活的对象构造机制来解决这个问题。通过利用 Product 类中的内部 Builder 类,我们可以指定在对象创建期间要初始化哪些属性以及要忽略哪些属性。

public class Product {
    
    private int id;
    private String name;
    private BigDecimal price;
    private String color;

    private static class Builder {
        private Product product = new Product();

        private Builder() {
            // Private constructor to prevent external instantiation
        }

        public Builder id(int id){
            product.id = id;
            return this;
        }

        public Builder name(String name){
            product.name = name;
            return this;
        }

        public Builder price(BigDecimal price){
            product.price = price;
            return this;
        }

        public Builder color(String color){
            product.color = color;
            return this;
        }

        public Product build(){
            return this.product;
        }
    }

    public static Builder builder(){
        return new Builder();
    }
}

我们还可以将 builder() 方法与私有构造函数一起使用。

public class Main {
    public static void main(String[] args) {
        Product p1 = Product.builder()
                .id(1)
                .name("Laptop")
                .price(new BigDecimal(1000))
                .color("Black")
                .build();
        System.out.println(p1);
    }
}

使用方法链,我们可以方便地以可读且富有表现力的方式设置 Product 对象所需的属性。 build() 方法完成对象构造并返回完全初始化的 Product 实例。

构建器模式提供了一种干净、直观的对象创建方法,提高了代码的可维护性和处理复杂对象初始化要求的灵活性。

真实用例

在 Spring Security 中配置 SecurityFilterChain 使用 Builder 模式。

 @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        http.cors().configurationSource(request -> {
            CorsConfiguration configuration = new CorsConfiguration();
            configuration.setAllowedOrigins(List.of("http://localhost:5173","https://municipal-hub-frontend.vercel.app"));
            configuration.setAllowedMethods(List.of("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
            configuration.setAllowCredentials(true);
            configuration.addExposedHeader("Message");
            configuration.setAllowedHeaders(List.of("Authorization", "Cache-Control", "Content-Type"));
            return configuration;
        })
                .and()
                .csrf()
                .disable()
                .authorizeHttpRequests()
                .requestMatchers(PUBLIC_URLS)
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(authEntryPoint)
                .accessDeniedHandler(customAccessDeniedHandler)
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.addFilterBefore(customAuthenticationFilter(http), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

装饰模式(Decorator Pattern)

装饰器模式是一种结构设计模式,允许动态地将行为添加到各个对象,而不影响同一类中其他对象的行为。它为扩展功能提供了子类化的灵活替代方案。

示例:通知系统

想象一个场景,我们需要通过电子邮件、短信、推送通知等多种渠道向用户发送通知。此外,我们可能需要根据用户偏好或系统要求组合这些操作。

在我们的示例中,我们有不同的通知发送者,例如 EmailSender 和 SMSSender。最初,我们创建这些发送者的单独实例来单独处理每个通知方法。

EmailSender e1 = new EmailSender();
SMSSender e2 = new SMSSender();

e1.send();
e2.send();

然而,随着系统的发展,我们可能需要根据不同的条件动态地组合这些动作。这就是装饰器模式发挥作用的地方。

NotificationSender s1 = new PushNotificationSender(new PopupSender(null));
NotificationSender s2 = new PopupSender(new PushNotificationSender(null));

在这里,NotificationSender 接口充当基础组件,而 PushNotificationSender 和 PopupSender 充当装饰器。每个装饰器都包装另一个NotificationSender,在将调用委托给包装的发送者之前或之后添加额外的行为。

public interface NotificationSender {
    void send();
}

public class PushNotificationSender implements NotificationSender {
    private final NotificationSender notificationSender;

    public PushNotificationSender(NotificationSender notificationSender) {
        this.notificationSender = notificationSender;
    }

    @Override
    public void send() {
        System.out.println("Sending Push notification");
        if (notificationSender != null) {
            notificationSender.send();
        }
    }
}

public class PopupSender implements NotificationSender {
    private final NotificationSender notificationSender;

    public PopupSender(NotificationSender notificationSender) {
        this.notificationSender = notificationSender;
    }

    @Override
    public void send() {
        System.out.println("Sending popup");
        if (notificationSender != null) {
            notificationSender.send();
        }
    }
}

通过使用装饰器动态组合不同的通知发送者,我们实现了灵活且可维护的解决方案。无需修改现有代码即可轻松添加新的通知方法,从而提高代码的重用性和可扩展性。

真实用例

在Java生态系统中,装饰器模式被广泛使用,特别是在Java I/O API提供的I/O流中。

public class Example {
    public static void main(String[] args) throws Exception {
        BufferedReader in =
            new BufferedReader(
                new InputStreamReader(
                    new FileInputStream("")
                )
            );
    }
}

FileInputStream:此类用于从文件中读取字节。它是一个基本的输入流实现,从指定文件读取字节。

InputStreamReader:该类是字节流到字符流的桥梁。它从输入流中读取字节并使用指定的字符集将它们解码为字符。在本例中,它包装了 FileInputStream,将字节转换为字符。

BufferedReader:此类从字符输入流中读取文本,缓冲字符,以便高效读取字符、数组和行。它包装了一个InputStreamReader,添加了缓冲功能。

该代码片段演示了装饰器如何堆叠在一起以向基本输入流添加功能。 BufferedReader 充当InputStreamReader 的装饰器,而InputStreamReader 又充当FileInputStream 的装饰器。这种分层方法允许通过附加缓冲有效地从文件中读取文本。

适配器模式(Adapter Pattern)

适配器模式是一种结构设计模式,允许具有不兼容接口的对象一起工作。它通过包装对象并公开与客户端要求兼容的不同接口,充当两个不兼容接口之间的桥梁。

关键概念:

装饰对象:与其他设计模式类似,适配器模式涉及包装对象。然而,其目的不同;它的目的是提供一种从一个界面更改为另一个界面的方法,而不是增强功能。

适应不同的表示:适配器模式的主要目标是适应不同模块或系统的同一对象的不同表示。它确保具有不同接口的组件之间的互操作性。

package main;

import dependency.DependencyNotificationSender;

public class AppNotificationSender implements NotificationSender {

    private final DependencyNotificationSender dependencyNotificationSender;

    public AppNotificationSender(DependencyNotificationSender dependencyNotificationSender) {
        this.dependencyNotificationSender = dependencyNotificationSender;
    }

    @Override
    public void sendNotification() {
        dependencyNotificationSender.send();
    }
}

public interface NotificationSender {
    void sendNotification();
}

package dependency;

public class DependencyNotificationSender {
    public void send() {
        System.out.println("Sending dependency notification");
    }
}

此示例说明了 AppNotificationSender 如何充当 DependencyNotificationSender 的适配器,从而能够在不更改现有代码的情况下使用通知发送者的不同表示形式。

真实用例

在 Spring Security 中,适配器模式的例子是用户实体对 UserDetails 接口的适配。 Spring Security 通常需要用户详细信息来实现 UserDetails 接口以进行身份验证和授权。为了实现这一点,开发人员可以创建一个自定义的 SecurityUser 类,该类包装实体类并实现 UserDetails 接口。这种调整不仅确保了与 Spring Security 的兼容性,而且通过分离与身份验证和用户管理相关的关注点来遵循单一职责原则。

import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;

import com.ayushsingh.cacmp_backend.models.roles.UserRole;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.PrePersist;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.Date;
import java.util.Objects;
import java.util.UUID;
import java.util.Set;

@Table(name = "user")
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id")
    private Long userId;

    @Column(name = "user_token", nullable = false, unique = true)
    private String userToken;

    @Column(name = "username", nullable = false, unique = true)
    private String username;

    @Column(name = "password", nullable = false)
    private String password;

    @Column(name = "name", nullable = false)
    private String name;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "users_user_role", joinColumns = @JoinColumn(name = "user_id",referencedColumnName = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id",referencedColumnName = "role_id"))
    private Set<UserRole> roles;

    @CreatedDate
    @CreationTimestamp
    @Column(name = "created_at", nullable = false, updatable = false)
    private Date createdAt;

    @LastModifiedDate
    @UpdateTimestamp
    @Column(name = "updated_at")
    private Date updatedAt;

    @PrePersist
    public void generateToken() {
        if (this.userToken == null) {
            this.userToken = UUID.randomUUID().toString();
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(username, user.username);
    }

    @Override
    public int hashCode() {
        return Objects.hash(username);
    }
}

我们可以创建一个单独的类SecurityUser来包装User实体类。

import java.util.Collection;
import java.util.stream.Collectors;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import com.ayushsingh.cacmp_backend.models.entities.User;
import com.ayushsingh.cacmp_backend.models.securityModels.authority.UserAuthority;

import lombok.AllArgsConstructor;

@AllArgsConstructor
public class SecurityUser implements UserDetails {

    private final User user;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return user.getRoles()
                .stream()
                .map(UserAuthority::new)
                .collect(Collectors.toList());

    }

    @Override
    public String getPassword() {
        return this.user.getPassword();
    }

    @Override
    public String getUsername() {
        return this.user.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

单例模式(Singleton Pattern)

单例模式是一种创建型设计模式,它将类的实例化限制为一个实例,并提供对该实例的全局访问点。

关键概念:

单实例创建:单例模式的主要目标是确保在整个应用程序的生命周期中仅创建该类的一个实例。

全局访问点:单例模式为项目中的任何类提供了一种轻松访问该类的单个实例的机制。这促进了集中管理和对共享资源的访问。

public class Main {
    public static void main(String[] args) {
        Singleton1 singleton1 = Singleton1.getInstance();
        System.out.println(singleton1);
    }
}
public class Singleton1 {
    private static Singleton1 SINGLETON = null;

    private Singleton1() {
        // Private constructor to prevent external instantiation
    }

    public static Singleton1 getInstance() {
        if (SINGLETON == null) {
            SINGLETON = new Singleton1();
        }
        return SINGLETON;
    }
}

这一实现确保在整个应用程序的生命周期中仅创建一个 Singleton1 实例,满足 Singleton 模式的要求。然而,代码中提供的单例模式的实现并不是线程安全的。当多个线程尝试同时访问 getInstance() 方法时,可能会导致创建单例类的多个实例,从而违反了模式的预期行为。

public class Singleton1 {

    private static Singleton1 SINGLETON = null;

    private Singleton1() {

    }

    public static synchronized Singleton1 getInstance() {
        if (SINGLETON == null) {
            SINGLETON = new Singleton1();
        }
        return SINGLETON;
    }
}

虽然同步整个 getInstance() 方法可以确保线程安全,但它会带来性能开销,特别是在多个线程可能同时竞争访问该方法的多线程环境中。更有效的方法是使用双重检查锁定来仅同步创建单例实例的方法的关键部分。这有助于最大限度地减少单例初始实例化后的同步开销。

public class Singleton1 {

    private static Singleton1 SINGLETON = null;

    private Singleton1() {
        // Private constructor to prevent external instantiation
    }

    public static Singleton1 getInstance() {
        // Only the first time, enter the synchronized block
        if (SINGLETON == null) {
            // If multiple threads enter the condition simultaneously,
            // they will be blocked by synchronized
            synchronized (Singleton1.class) {
                if (SINGLETON == null) {
                    SINGLETON = new Singleton1();
                }
            }
        }
        return SINGLETON;
    }
}

getInstance() 方法首先检查 SINGLETON 实例是否为 null。如果是,则表明尚未创建实例。

如果 SINGLETON 实例为 null,则该方法进入同步块。如果SINGLETON实例在同步块内仍然为空,表明在此期间没有其他线程创建实例,则创建Singleton1的新实例。

具有延迟初始化持有者类的单例模式

单例模式确保一个类只有一个实例,并提供对该实例的全局访问点。实现此模式的一种有效且线程安全的方法是使用嵌套类。

关键点:

延迟初始化:单例实例仅在第一次请求时创建,而不是在类加载时急切地创建。这种惰性初始化方法通过将实例创建推迟到实际需要时来节省资源。

线程安全:该实现无需显式同步即可确保线程安全。这是通过 Java 类加载器在类加载期间提供的固有同步来实现的。

public class Singleton2 {

    // Private constructor to prevent external instantiation
    private Singleton2() {
    }

    // SingletonHolder class holds the singleton instance
    private static final class SingletonHolder {
        // The singleton instance is created and initialized when the class is loaded
        private static final Singleton2 SINGLETON = new Singleton2();
    }

    // Public method to access the singleton instance
    public static Singleton2 getInstance() {
        // The getInstance() method returns the singleton instance
        // The instance is created only when getInstance() is called for the first time
        // The class loader ensures thread safety by loading the SingletonHolder class only once
        return SingletonHolder.SINGLETON;
    }
}

单例实例是在 SingletonHolder 类中延迟创建的。仅当第一次调用 getInstance() 方法时才对其进行初始化。当加载单例类时,SingletonHolder类不会加载到内存中,只有当有人调用 getInstance() 方法时,该类才会被加载并创建单例类实例。

Eager Initialization

Eager initialization 是在 Java 中实现单例模式的经典方法之一。在这种方法中,单例实例是在类加载期间急切创建的。单例实例在类加载后立即创建。这意味着一旦 JVM 访问或加载该类,该实例就可以立即使用。

public class Singleton5 {

    private static Singleton5 SINGLETON = new Singleton5();

    private Singleton5() {}

    public static Singleton5 getInstance() {
        return SINGLETON;
    }
}

真实用例

public class Main {
    public static void main(String[] args) {
        // Retrieve the Runtime singleton instance
        Runtime runtime = Runtime.getRuntime();
        
        // Use the runtime instance for system operations
        // (e.g., getting information about the Java Virtual Machine, executing system commands)

        // Example: Getting the total amount of memory in the Java Virtual Machine
        long totalMemory = runtime.totalMemory();
        System.out.println("Total Memory: " + totalMemory + " bytes");
    }
}

Runtime.getRuntime() 方法返回 Runtime 类的单例实例。该实例代表当前Java应用程序的运行时环境。一旦检索到单例实例,它就可以用于执行各种与系统相关的操作,例如获取有关 Java 虚拟机 (JVM) 的信息或执行系统命令。

单例设计模式与 Spring Bean 的单例范围

在Spring框架中,bean代表一个由Spring IoC(控制反转)容器管理的对象。 Bean 在 Spring 上下文中创建、管理和销毁,从而允许集中配置和依赖管理。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;

@Configuration
public class BeanConfig {

    @Bean
    @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
    public SingletonBean singletonBean1() {
        return new SingletonBean();
    }

    @Bean
    @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
    public SingletonBean singletonBean2() {
        return new SingletonBean();
    }

}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

public class SomeClass {

    @Autowired
    @Qualifier("singletonBean1")
    private SingletonBean beanOne;

    @Autowired
    @Qualifier("singletonBean2")
    private SingletonBean beanTwo;
}

在上面的例子中,虽然 singletonBean1 和 singletonBean2 都是相同的类型,但是两者都会按照指定的方式创建和注入。因此,我们可以看到 Singleton 设计模式与 Singleton Bean 范围不同。

工厂设计模式(Factory Design Pattern)

工厂模式是 Java 开发中广泛使用的基本创建设计模式。它提供了一种基于具有多个子类的公共超类创建对象的结构化方法。当根据某些条件或输入参数动态确定要实例化的特定子类时,此模式特别有用。通过将对象实例化的责任委托给工厂类,工厂模式提高了代码的灵活性、可扩展性和可维护性。

public class Main {
    public static void main(String[] args) {
        
        Vehicle car = CarFactory.create(CarFactory.CarType.Car2);
        System.out.println(car);
    }
}

public class CarFactory {
    public static Vehicle create(CarType carType) {
        // Create and return a specific type of car based on the CarType
        switch (carType) {
            case CAR1:
                return new Car1();
            case CAR2:
                return new Car2();
        }
        throw new IllegalArgumentException("Wrong car type");
    }

    public enum CarType {
        CAR1, CAR2;
    }
}

public class Car1 extends Car {
}

public class Car2 extends Car {
}

public abstract class Car implements Vehicle {
}

抽象工厂模式(Abstract Factory Pattern)

抽象工厂设计模式是一种创建模式,它提供了一个接口,用于创建相关或依赖对象系列,而无需指定它们的具体类。它通过引入多个工厂类来增强工厂模式,每个工厂类负责创建不同的对象系列。该模式通过封装对象创建逻辑并将其从客户端代码中抽象出来,提高了代码的可伸缩性、灵活性和可维护性。

在抽象工厂模式中,我们的目标是消除工厂模式中常见的条件语句,例如 if-else 或 switch 块。我们不是使用具有条件逻辑的单个工厂类来创建不同类型的对象,而是定义一组工厂接口或类,每个接口或类负责创建属于特定系列或类别的对象。

让我们通过一个例子来理解。

我们将定义两种车辆:汽车和摩托车。每个家族都有自己的工厂类,负责制造该类型的车辆。

public interface VehicleFactory {
    Vehicle create();
}

public class CarFactory implements VehicleFactory {
    @Override
    public Vehicle create() {
        return new Car();
    }
}

public class MotorbikeFactory implements VehicleFactory {
    @Override
    public Vehicle create() {
        return new Motorbike();
    }
}

public enum VehicleType {
    CAR, MOTORBIKE;
}

public class Main {
    public static void main(String[] args) {
        // Get the factory for creating cars
        VehicleFactory carFactory = VehicleFactory.create(VehicleType.CAR);
        // Create a car using the car factory
        Vehicle car = carFactory.create();
        System.out.println(car);

        // Get the factory for creating motorbikes
        VehicleFactory motorbikeFactory = VehicleFactory.create(VehicleType.MOTORBIKE);
        // Create a motorbike using the motorbike factory
        Vehicle motorbike = motorbikeFactory.create();
        System.out.println(motorbike);
    }
}

我们定义了两个具体的工厂类:CarFactory 和 MotorbikeFactory,每个类都实现了 VehicleFactory 接口。每个工厂负责制造相应类型的车辆(汽车或摩托车)。

真实用例

在 Java Persistence API (JPA)(Java 中对象关系映射的标准)中,EntityManagerFactory 是用于管理实体管理器实例的关键接口。它负责创建和配置实体管理器实例。

迭代器模式(Iterator Pattern)

迭代器模式是一种行为设计模式,它提供了一种按顺序访问组或集合的元素而不暴露其底层表示的方法。它封装了遍历元素的逻辑,允许客户端在不知道其内部结构的情况下遍历集合。集合这个词并不一定意味着集合框架。

public interface Iterator {

    boolean hasNext();

    Integer next();

    Integer get();

    boolean hasPrevious();

    Integer previous();
}

public class Container implements Iterator {
    private Integer[] values;
    private Integer cursor = -1;

    public Container(Integer[] values) {
        this.values = values;
    }

    @Override
    public boolean hasNext() {
        return cursor < values.length - 1;
    }

    @Override
    public Integer next() {
        if (hasNext()) {
            cursor++;
            return values[cursor];
        }
        return null;
    }

    @Override
    public Integer get() {
        return values[cursor];
    }

    @Override
    public boolean hasPrevious() {
        return cursor >= 1;
    }

    @Override
    public Integer previous() {
        if (hasPrevious()) {
            cursor--;
            return values[cursor];
        }
        return null;
    }
}

public class Main {
    public static void main(String[] args) {
        Integer[] values = new Integer[] { 2, 3, 4 };
        Container container = new Container(values);

        while (container.hasNext()) {
            Integer next = container.next();
            System.out.println(next);
        }

        while (container.hasPrevious()) {
            Integer previous = container.previous();
            System.out.println(previous);
        }
    }
}

在此示例中,我们有一个封装整数数组的 Container 类。 Container 类实现了 Iterator 接口,该接口定义了遍历集合的方法(hasNext()、next()、hasPrevious()、previous())。客户端代码可以迭代容器的元素,而无需知道集合的内部结构。

真实用例

一个非常常见的示例是集合框架本身,其中迭代器模式用于迭代存储在集合中的元素。

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Java");
        list.add("Python");
        list.add("C++");

        // Get an iterator for the list
        Iterator<String> iterator = list.iterator();

        // Iterate through the elements using the iterator
        while (iterator.hasNext()) {
            String element = iterator.next();
            System.out.println(element);
        }
    }
}

Conclusion

Java 设计模式是创建高效且可维护的软件的重要工具。它们为常见设计问题提供可重用的解决方案,增强代码的可读性和可扩展性。

将这些模式合并到 Java 开发中不仅可以提高代码质量,还可以提高生产力并促进长期维护。

相关文章
热点文章
精彩视频
Tags

站点地图 在线访客: 今日访问量: 昨日访问量: 总访问量: