单例模式属于创建型设计模式,它保证一个类仅有一个实例,并且提供一个全局访问点来获取该实例。下面为你详细阐述单例模式的好处和坏处。
好处
- 资源优化:单例模式能保证一个类只有一个实例,这对于那些创建和销毁开销大的对象(像数据库连接、线程池、缓存等)非常有用。比如,数据库连接频繁创建和销毁会消耗大量资源,使用单例模式可以确保只创建一个数据库连接实例,减少资源浪费。
- 全局访问:单例模式提供了一个全局访问点,使得系统中任何地方都可以访问该实例。这在多个模块需要共享同一个资源时非常方便,比如日志记录器,所有模块都可以通过单例日志记录器来记录日志。
- 数据一致性:由于只有一个实例存在,所有对该实例的操作都是针对同一个对象,避免了多个实例导致的数据不一致问题。例如,在多线程环境下,单例模式可以确保对共享资源的操作是线程安全的。
坏处
- 违反单一职责原则:单例模式的类既负责自身实例的创建和管理,又负责业务逻辑的实现,这违反了单一职责原则,使得类的职责过重,不利于代码的维护和扩展。
- 扩展性差:单例模式通常没有抽象层,难以进行扩展和修改。如果需要改变单例类的行为,可能需要直接修改类的代码,这违反了开闭原则。
- 多线程问题:虽然单例模式可以在多线程环境下保证只有一个实例,但实现线程安全的单例模式需要额外的同步机制,这会增加系统的复杂性和性能开销。
- 单元测试困难:单例模式的全局访问特性使得单元测试变得困难。因为单例类的状态可能会影响其他测试用例的执行结果,而且在测试过程中很难模拟不同的单例实例。
工厂模式概述
工厂模式是一种创建型设计模式,它提供了一种创建对象的方式,将对象的创建和使用分离。其核心思想是定义一个创建对象的接口,让子类决定实例化哪个类。这样做的好处是提高了代码的可维护性和可扩展性,使得代码更符合开闭原则,即对扩展开放,对修改关闭。
线程池中的工厂模式
在 Java 里,线程池用到了工厂模式,特别是在创建线程时。java.util.concurrent
包中的 ThreadFactory
接口就是工厂模式的体现。下面详细讲解线程池中的工厂模式:
1. ThreadFactory
接口
ThreadFactory
接口定义了创建线程的方法,代码如下:
public interface ThreadFactory {
Thread newThread(Runnable r);
}
该接口只有一个方法 newThread
,它接收一个 Runnable
对象作为参数,返回一个新的 Thread
对象。通过实现这个接口,我们可以自定义线程的创建逻辑。
2. 线程池使用 ThreadFactory
ThreadPoolExecutor
类在创建线程池时可以传入一个 ThreadFactory
对象,以此来指定线程的创建方式。以下是 ThreadPoolExecutor
的构造函数:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
这里的 threadFactory
参数就是用来创建线程的工厂。
3. 示例代码
下面是一个使用自定义 ThreadFactory
的线程池示例:
import java.util.concurrent.*;
// 自定义 ThreadFactory
class CustomThreadFactory implements ThreadFactory {
private final String namePrefix;
private int counter = 0;
public CustomThreadFactory(String namePrefix) {
this.namePrefix = namePrefix;
}
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, namePrefix + "-" + counter++);
// 可以在这里设置线程的其他属性,如优先级、是否为守护线程等
thread.setPriority(Thread.NORM_PRIORITY);
return thread;
}
}
public class ThreadPoolFactoryExample {
public static void main(String[] args) {
// 创建自定义 ThreadFactory
ThreadFactory customFactory = new CustomThreadFactory("CustomThread");
// 创建线程池
ExecutorService executorService = new ThreadPoolExecutor(
2,
5,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
customFactory
);
// 提交任务
for (int i = 0; i < 5; i++) {
final int taskId = i;
executorService.submit(() -> {
System.out.println("Task " + taskId + " is running on thread: " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Task " + taskId + " is completed.");
});
}
// 关闭线程池
executorService.shutdown();
}
}
代码解释
- 自定义
ThreadFactory
:CustomThreadFactory
类实现了ThreadFactory
接口,在newThread
方法中创建了一个新的线程,并为线程设置了自定义的名称和优先级。 - 创建线程池:在
main
方法中,我们创建了一个ThreadPoolExecutor
线程池,并传入了自定义的ThreadFactory
。 - 提交任务:通过
executorService.submit
方法提交了 5 个任务,每个任务会打印当前运行的线程名称和任务完成信息。 - 关闭线程池:使用
executorService.shutdown
方法关闭线程池。
工厂模式在线程池中的优势
- 可定制性:能够自定义线程的创建逻辑,例如设置线程的名称、优先级、是否为守护线程等。
- 代码复用:可以在多个线程池中复用同一个
ThreadFactory
,提高代码的复用性。 - 可维护性:将线程的创建和使用分离,使得代码更易于维护和扩展。
建造者模式的核心思想是将复杂对象的构建和表示分离,让相同的构建过程能创建出不同的表示。在 Android 和 Java 开发中,OkHttp
和 Retrofit
这两个网络请求库就很好地运用了建造者模式。下面将结合它们详细讲解。
OkHttp 中的建造者模式
OkHttp
是一个高效的 HTTP 客户端,它使用建造者模式来配置 OkHttpClient
实例。OkHttpClient
有很多可配置的属性,例如连接超时时间、读取超时时间、拦截器等。通过建造者模式,你可以逐步设置这些属性,最后构建出一个符合需求的 OkHttpClient
实例。
示例代码
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class OkHttpBuilderExample {
public static void main(String[] args) {
// 使用建造者模式构建 OkHttpClient
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build();
// 创建请求
Request request = new Request.Builder()
.url("https://www.example.com")
.build();
try {
// 发起请求
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
System.out.println(response.body().string());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
代码解释
OkHttpClient.Builder
:这是OkHttpClient
的建造者类,通过它可以逐步设置OkHttpClient
的各种属性,比如连接超时时间、读取超时时间等。.build()
方法:调用此方法后,会根据之前设置的属性构建出一个OkHttpClient
实例。
Retrofit 中的建造者模式
Retrofit
是一个类型安全的 HTTP 客户端,它基于 OkHttp
构建。Retrofit
同样使用建造者模式来配置 Retrofit
实例。
示例代码
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.GET;
interface ApiService {
@GET("users")
Call<String> getUsers();
}
public class RetrofitBuilderExample {
public static void main(String[] args) {
// 使用建造者模式构建 Retrofit 实例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(GsonConverterFactory.create())
.client(new OkHttpClient())
.build();
// 创建 API 服务实例
ApiService apiService = retrofit.create(ApiService.class);
// 发起请求
Call<String> call = apiService.getUsers();
call.enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
if (response.isSuccessful()) {
System.out.println(response.body());
}
}
@Override
public void onFailure(Call<String> call, Throwable t) {
t.printStackTrace();
}
});
}
}
代码解释
Retrofit.Builder
:这是Retrofit
的建造者类,通过它可以设置Retrofit
的各种属性,例如基础 URL、数据转换器工厂、使用的OkHttpClient
等。.build()
方法:调用此方法后,会根据之前设置的属性构建出一个Retrofit
实例。
建造者模式在 OkHttp 和 Retrofit 中的优势
- 可配置性强:可以根据具体需求灵活配置
OkHttpClient
和Retrofit
的各种属性,而不需要一次性传入所有参数。 - 代码可读性高:通过链式调用的方式设置属性,使代码更易读和维护。
- 构建过程清晰:将复杂对象的构建过程分解为多个步骤,使得构建过程更加清晰明了。
感谢观看!!!