设计模式最佳实践代码总结 - 创建型设计模式篇
文章目录
- [设计模式最佳实践代码总结 - 创建型设计模式篇](#设计模式最佳实践代码总结 - 创建型设计模式篇)
- 创建型设计模式
创建型设计模式
简单工厂模式最佳实践
简单工厂模式(Simple Factory Pattern)又叫作静态工厂方法模式(StaticFactory Method Pattern),简单来说,简单工厂模式有一个具体的工厂类,可以生成多个不同的产品,属于创建型设计模式。简单工厂模式不在GoF 23种设计模式之列。
在Spring框架中,可以使用@Value注解从YAML配置文件中获取值,并使用这些值来决定创建哪个类的实例。以下是一个简单的例子,展示了如何使用简单工厂模式结合Spring来根据YAML配置文件中的配置创建不同的对象实例。
首先,我们定义一个简单的接口和几个实现类:
java
public interface MessageService {
void sendMessage(String msg);
}
public class EmailService implements MessageService {
@Override
public void sendMessage(String msg) {
System.out.println("Sending email with message: " + msg);
}
}
public class SMSService implements MessageService {
@Override
public void sendMessage(String msg) {
System.out.println("Sending SMS with message: " + msg);
}
}
接下来,我们创建一个简单工厂类,它将根据配置决定创建哪个服务实例:
java
public class MessageServiceFactory {
public static MessageService getMessageService(String serviceType) {
if ("email".equalsIgnoreCase(serviceType)) {
return new EmailService();
} else if ("sms".equalsIgnoreCase(serviceType)) {
return new SMSService();
}
return null;
}
}
然后,我们可以在Spring的配置文件(application.yml)中定义服务类型:
yaml
message:
service: email
现在,我们可以使用@Value注解来获取这个配置值,并在Spring的配置类中使用它来决定创建哪个服务实例:
java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Value("${message.service}")
private String messageServiceType;
@Bean
public MessageService messageService() {
return MessageServiceFactory.getMessageService(messageServiceType);
}
}
最后,我们可以在Spring的上下文中使用这个MessageService bean:
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MessageClient {
private final MessageService messageService;
@Autowired
public MessageClient(MessageService messageService) {
this.messageService = messageService;
}
public void sendMessage(String msg) {
messageService.sendMessage(msg);
}
}
现在,如果你想要改变消息服务类型,只需要修改application.yml文件中的配置,Spring会自动重新创建MessageService bean,并使用新的配置。
请注意,这个例子假设你已经设置了Spring的配置文件解析器来读取YAML文件。如果你使用的是较新的Spring Boot版本,它默认支持YAML配置文件。如果你使用的是较老的Spring版本,你可能需要添加额外的依赖来支持YAML解析。
此外,这个例子没有处理配置错误的情况,你可能需要添加一些额外的错误处理代码来确保在配置文件中指定的服务类型是有效的。
以上示例中的MessageServiceFactory 采用写死的方式在实际代码中也不是很可取,可以做如下优化:
为了使用反射来动态创建MessageService的实例,我们可以将服务类型的全限定类名存储在配置文件中,并在简单工厂中使用反射来实例化这些类。以下是修改后的代码:
首先,我们在YAML配置文件中定义服务类型的全限定类名:
yaml
message:
service: com.example.service.EmailService
然后,我们修改简单工厂类,使用反射来创建实例:
java
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class MessageServiceFactory {
public static MessageService getMessageService(String className) {
try {
Class<?> clazz = Class.forName(className);
Constructor<?> constructor = clazz.getConstructor();
return (MessageService) constructor.newInstance();
} catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
// 处理异常,例如抛出自定义异常或者记录日志
return null;
}
}
}
工厂方法模式最佳实践
简单工厂和工厂方法的主要区别在于:
- 简单工厂模式通常在工厂类中使用多态性来创建不同的产品,而工厂方法模式通常在子类中实现创建对象的逻辑。
- 简单工厂模式通常在工厂类中使用条件语句来决定创建哪个产品,而工厂方法模式通常在子类中重写工厂方法来创建产品。
- 简单工厂模式通常只有一个工厂类,而工厂方法模式通常有多个工厂类,每个工厂类对应一个产品类。
- 简单工厂模式通常用于创建产品数量较少的情况,而工厂方法模式通常用于创建产品数量较多或者产品类型较多,且每种产品由不同的工厂创建的情况。
工厂方法模式是一种创建型设计模式,它提供了一种创建对象的接口,但允许子类决定实例化哪一个类。在Spring框架中,我们可以使用工厂方法模式来根据YAML配置文件中的配置动态创建对象。以下是一个使用工厂方法模式的示例:
首先,我们定义一个简单的接口和几个实现类:
java
public interface MessageService {
void sendMessage(String msg);
}
public class EmailService implements MessageService {
@Override
public void sendMessage(String msg) {
System.out.println("Sending email with message: " + msg);
}
}
public class SMSService implements MessageService {
@Override
public void sendMessage(String msg) {
System.out.println("Sending SMS with message: " + msg);
}
}
再次,我们定义一个工厂接口和工厂类的骨架:
java
public interface MessageServiceFactory {
MessageService createService();
}
public abstract class AbstractMessageServiceFactory implements MessageServiceFactory {
// 默认实现,如果需要的话可以在这里添加一些公共逻辑
}
然后,我们为每种服务类型创建一个具体的工厂类:
java
public class EmailServiceFactory extends AbstractMessageServiceFactory {
@Override
public MessageService createService() {
return new EmailService();
}
}
public class SMSServiceFactory extends AbstractMessageServiceFactory {
@Override
public MessageService createService() {
return new SMSService();
}
}
接下来,我们在YAML配置文件中定义工厂类的全限定类名:
yaml
message:
factory: com.example.service.EmailServiceFactory
现在,我们可以在Spring的配置类中使用@Value注解来获取配置的类名,并使用反射来创建实例:
java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Value("${message.factory}")
private String messageFactoryClassName;
@Bean
public MessageService messageService(ApplicationContext applicationContext) {
return applicationContext.getBean(messageFactoryClassName, MessageServiceFactory.class).createService();
}
}
最后,我们可以在Spring的上下文中使用这个MessageService bean:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MessageClient {
private final MessageService messageService;
@Autowired
public MessageClient(MessageService messageService) {
this.messageService = messageService;
}
public void sendMessage(String msg) {
messageService.sendMessage(msg);
}
}
抽象工厂模式最佳实践
抽象工厂模式(Abstract Factory Pattern)指提供一个创建一系列相关或相互依赖对象的接口,无须指定它们具体的类。意思是客户端不必指定产品的具体类型,创建多个产品族中的产品对象。
首先,我们定义一个抽象工厂接口和多个工厂类的骨架:
java
public interface MessageServiceFactory {
MessageService createMessageService();
}
public class EmailServiceFactory implements MessageServiceFactory {
@Override
public MessageService createMessageService() {
return new EmailService();
}
}
public class SMSServiceFactory implements MessageServiceFactory {
@Override
public MessageService createMessageService() {
return new SMSService();
}
}
然后,我们在YAML配置文件中定义工厂类的全限定类名:
yaml
message:
factory: com.example.service.EmailServiceFactory
现在,我们可以在Spring的配置类中使用@Value注解来获取配置的类名,并使用反射来创建实例:
yaml
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Value("${message.factory}")
private String messageFactoryClassName;
@Bean
public MessageServiceFactory messageServiceFactory() throws ClassNotFoundException {
Class<?> factoryClass = Class.forName(messageFactoryClassName);
return (MessageServiceFactory) factoryClass.newInstance();
}
@Bean
public MessageService messageService(MessageServiceFactory messageServiceFactory) {
return messageServiceFactory.createMessageService();
}
}
最后,我们可以在Spring的上下文中使用这个MessageService bean:
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MessageClient {
private final MessageService messageService;
@Autowired
public MessageClient(MessageService messageService) {
this.messageService = messageService;
}
public void sendMessage(String msg) {
messageService.sendMessage(msg);
}
}
在这个例子中,我们首先使用反射来根据配置的工厂类名创建工厂实例,然后调用其createMessageService方法来创建MessageService实例。这样,我们就可以根据配置动态地创建不同的服务实例。
请注意,这个例子假设配置的工厂类实现了MessageServiceFactory接口,并且可以被实例化。如果这些条件不满足,你可能需要调整代码以处理异常情况。
此外,这个例子没有处理类加载或实例化过程中可能发生的任何异常。在生产环境中,你可能需要添加更多的错误处理代码来确保配置的类名是有效的,并且类可以被成功加载和实例化。
这个例子假设所有的工厂类都在同一个包下。如果它们不在同一个包下,你需要使用全限定类名。
单例模式最佳实践
单例设计模式是一种创建型设计模式,它保证一个类只有一个实例,并提供一个全局访问点来访问这个实例。在Java中,实现单例模式通常有多种方法,其中最常见的是懒汉式和饿汉式。懒汉式是在第一次使用时实例化,而饿汉式是在类加载时实例化。以下是使用懒汉式单例模式的示例,并结合Spring框架来管理单例的生命周期:
首先,我们定义一个单例类:
java
public class SingletonService {
// 私有静态成员变量,存储唯一实例
private static SingletonService instance;
// 私有构造函数,防止外部实例化
private SingletonService() {}
// 公共的静态方法,用于获取实例
public static synchronized SingletonService getInstance() {
if (instance == null) {
instance = new SingletonService();
}
return instance;
}
// 其他方法和属性...
}
在上面的代码中,我们使用了一个私有静态变量来存储SingletonService的唯一实例,并在第一次调用getInstance方法时创建它。由于getInstance方法是同步的,以防止多线程环境下创建多个实例,这被称为懒汉式单例。
在Spring中,我们可以使用@Bean注解和singleton属性来确保Spring容器只创建一个SingletonService bean的实例:
java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean(singleton = true)
public SingletonService singletonService() {
return SingletonService.getInstance();
}
}
现在,Spring容器将管理SingletonService的单例生命周期。当你需要使用这个单例时,你可以自动注入它到你的组件中:
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class SomeComponent {
@Autowired
private SingletonService singletonService;
// 使用singletonService的方法...
}
请注意,由于SingletonService的getInstance方法是同步的,这可能会影响性能。如果性能是一个问题,你可以考虑使用双重检查锁定(double-checked locking)来优化懒汉式单例的实现,但要注意在多线程环境下正确地使用volatile关键字来确保实例的可见性。
java
public class SingletonService {
private static volatile SingletonService instance;
private SingletonService() {}
public static SingletonService getInstance() {
if (instance == null) {
synchronized (SingletonService.class) {
if (instance == null) {
instance = new SingletonService();
}
}
}
return instance;
}
// 其他方法和属性...
}
在上述代码中,我们使用双重检查锁定来减少同步的开销。只有在第一次调用时才需要同步,后续调用时不需要。volatile关键字确保instance变量在多线程环境下的一致性。
使用Spring框架,你可以完全依赖于它的依赖注入机制来管理单例的生命周期,而不需要手动编写单例的创建和管理代码。Spring容器会确保SingletonService bean在整个应用程序中只有一个实例。
原型模式最佳实践
原型模式(Prototype Pattern)是一种创建型设计模式,它允许一个对象通过复制自身来创建多个相似的对象,而不是通过传统的构造函数创建。在Java中,原型模式通常通过实现Cloneable接口和clone()方法来实现。
以下是一个使用原型模式和Spring框架的示例:
首先,我们定义一个原型类,实现Cloneable接口并提供一个clone()方法:
javascript
public class MessageService implements Cloneable {
private String serviceName;
// 构造函数、其他属性和方法...
@Override
public MessageService clone() {
try {
return (MessageService) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
// Getters and Setters...
}
在上述代码中,我们重写了clone()方法来创建MessageService对象的深拷贝。
然后,我们可以在Spring的配置类中注册这个原型bean:
java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
@Configuration
public class AppConfig {
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public MessageService messageService() {
return new MessageService();
}
}
在上面的配置中,我们设置了messageService bean的作用域为原型,这意味着每次请求时都会创建一个新的实例。
现在,我们可以在Spring的上下文中使用这个MessageService bean:
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MessageClient {
private final MessageService messageService;
@Autowired
public MessageClient(MessageService messageService) {
this.messageService = messageService;
}
public void sendMessage(String msg) {
MessageService clonedService = messageService.clone();
// 使用克隆的service实例...
}
}
在这个例子中,我们通过clone()方法创建了MessageService对象的副本。这样,我们就可以根据原型创建新的对象实例。
请注意,这个例子假设MessageService类实现了Cloneable接口,并且clone()方法可以被成功调用。如果这些条件不满足,你可能需要调整代码以处理异常情况。
此外,这个例子没有处理clone()方法可能抛出的CloneNotSupportedException异常。在生产环境中,你可能需要添加更多的错误处理代码来确保clone()方法可以被成功调用。
这个例子假设所有的MessageService实例都可以被安全地克隆。如果实例包含不可变的数据或者在克隆后不需要特殊处理,那么这个假设是正确的。如果需要处理更复杂的情况,你可能需要调整代码。
这种方法的一个潜在问题是,如果原型对象包含对其他资源的引用(如数据库连接、文件句柄等),那么在克隆对象时需要特别注意这些资源的处理,以避免资源泄露。
java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
@Configuration
public class AppConfig {
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public MessageService messageService() {
return new MessageService();
}
}
建造者模式最佳实践
建造者模式(Builder Pattern)是一种创建型设计模式,它允许你分步骤构建复杂对象,并可以避免构造函数参数过多或使用大量的可选参数。在Java中,建造者模式通常用于创建对象,特别是当对象的构造过程复杂或者包含多个构造参数时。
以下是一个使用建造者模式和Spring框架的示例:
首先,我们定义一个MessageService类和一个对应的建造者类:
java
public class MessageService {
private String serviceName;
private String serviceType;
private String serviceAddress;
private int servicePort;
// 私有构造函数,用于通过建造者创建对象
private MessageService(Builder builder) {
this.serviceName = builder.serviceName;
this.serviceType = builder.serviceType;
this.serviceAddress = builder.serviceAddress;
this.servicePort = builder.servicePort;
}
// Getters...
// 静态内部类,充当建造者
public static class Builder {
private String serviceName;
private String serviceType;
private String serviceAddress;
private int servicePort;
public Builder serviceName(String serviceName) {
this.serviceName = serviceName;
return this;
}
public Builder serviceType(String serviceType) {
this.serviceType = serviceType;
return this;
}
public Builder serviceAddress(String serviceAddress) {
this.serviceAddress = serviceAddress;
return this;
}
public Builder servicePort(int servicePort) {
this.servicePort = servicePort;
return this;
}
public MessageService build() {
return new MessageService(this);
}
}
}
在上面的代码中,我们使用了一个静态内部类Builder来构建MessageService对象。每个setter方法都返回Builder实例,以便可以链式调用。
然后,我们可以在Spring的配置类中使用建造者模式来创建MessageService bean:
java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public MessageService messageService() {
return new MessageService.Builder()
.serviceName("EmailService")
.serviceType("email")
.serviceAddress("localhost")
.servicePort(5000)
.build();
}
}
现在,我们可以在Spring的上下文中使用这个MessageService bean:
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MessageClient {
private final MessageService messageService;
@Autowired
public MessageClient(MessageService messageService) {
this.messageService = messageService;
}
public void sendMessage(String msg) {
// 使用messageService bean...
}
}
在这个例子中,我们使用建造者模式来创建MessageService对象,通过链式调用setter方法来设置各个参数。这样,我们就可以在保持代码可读性的同时,创建一个复杂的对象。
请注意,这个例子假设MessageService类有一个接受Builder实例的私有构造函数,并且Builder类有正确的方法来设置所有的参数。如果这些条件不满足,你可能需要调整代码以处理异常情况。
此外,这个例子没有处理Builder类可能抛出的异常。在生产环境中,你可能需要添加更多的错误处理代码来确保所有的setter方法都能正确调用。
这种方法的一个潜在问题是,如果MessageService对象的构建过程非常复杂,那么Builder类可能会变得非常大。在这种情况下,你可能需要考虑使用其他方法来简化构建过程,比如使用Java 8的Supplier接口或者使用Lombok库来简化代码。
java
import lombok.Builder;
import lombok.Getter;
@Builder
@Getter
public class MessageService {
private String serviceName;
private String serviceType;
private String serviceAddress;
private int servicePort;
// 私有构造函数,由Lombok的@Builder注解生成
private MessageService(String serviceName, String serviceType, String serviceAddress, int servicePort) {
this.serviceName = serviceName;
this.serviceType = serviceType;
this.serviceAddress = serviceAddress;
this.servicePort = servicePort;
}
// Getters...
}
ilder类可能会变得非常大。在这种情况下,你可能需要考虑使用其他方法来简化构建过程,比如使用Java 8的Supplier接口或者使用Lombok库来简化代码。
java
import lombok.Builder;
import lombok.Getter;
@Builder
@Getter
public class MessageService {
private String serviceName;
private String serviceType;
private String serviceAddress;
private int servicePort;
// 私有构造函数,由Lombok的@Builder注解生成
private MessageService(String serviceName, String serviceType, String serviceAddress, int servicePort) {
this.serviceName = serviceName;
this.serviceType = serviceType;
this.serviceAddress = serviceAddress;
this.servicePort = servicePort;
}
// Getters...
}