Android开发——设计模式技术总结
一、单例模式
1. 饿汉模式 (Eager Initialization)
实现方式:在类加载时就创建单例实例,而不是等到第一次使用时才创建。
代码示例:
public class Singleton {private static Singleton instance = new Singleton();private Singleton(){}public static Singleton getInstance(){return instance;}
}
优点:
线程安全:由于实例是在类加载时创建的,所以天然是线程安全的。在任何线程访问
getInstance()
方法之前,实例就已经存在了,不需要进行同步控制。实现简单:代码简洁,易于理解。
缺点:
非懒加载:无论是否使用,实例都会在类加载时被创建。如果单例的创建过程比较耗时,或者单例对象占用大量资源,而程序又一直没有用到它,就会造成资源浪费。
2. 双重检查锁模式 (Double-Checked Locking, DCL)
实现方式:在 getInstance()
方法中使用 synchronized
关键字,并且在同步块内部和外部都进行一次 null
检查。
代码示例:
public class Singleton {private static volatile Singleton instance;private Singleton(){}public static Singleton getInstance(){if(instance == null){synchronized (Singleton.class){if(instance == null){instance = new Singleton();}}}return instance;}
}
为什么采用它:
双重检查锁模式是解决多线程下懒加载单例问题的首选方案,因为它兼顾了性能和线程安全。
性能提升:与在整个方法上加
synchronized
的懒汉模式相比,它只在第一次创建实例时才进行同步,之后的调用都无需同步,大大提高了访问效率。线程安全:通过使用
volatile
关键字,确保了instance
变量的可见性和有序性。这解决了指令重排可能导致的问题,保证了当一个线程看到instance
不为null
时,其构造方法已经执行完毕,是一个完整的对象。懒加载:只有在第一次调用
getInstance()
方法时,才会创建实例,避免了不必要的资源开销。
3. 静态内部类模式 (Static Inner Class)多并发条件下
实现方式:将单例实例的创建放在一个私有的静态内部类中。
代码示例:
public class Singleton {private Singleton(){}private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance(){return SingletonHolder.INSTANCE;}
}
为什么采用它:
静态内部类模式是多线程下最推荐的单例实现方式之一,因为它完美地结合了懒加载和线程安全,并且实现起来比双重检查锁模式更简单。
线程安全:JVM 保证了类加载过程的线程安全。
SingletonHolder
类只有在第一次调用getInstance()
方法时才会被加载,并且只会加载一次,因此INSTANCE
实例也只会创建一次,天然地避免了多线程问题。懒加载:实例是在调用
getInstance()
方法时才创建,实现了懒加载。实现简洁:没有复杂的同步代码,代码更清晰易懂。
主要应用:
MediaSettingDbManager
使用 @Singleton
注解来确保其数据库实例在整个应用程序生命周期中是唯一的。这种单例模式是管理数据库实例的最佳实践,因为它能有效解决多个数据库连接带来的资源消耗问题,并确保数据的一致性。通过将数据库实例设计成单例,所有的数据读写操作都将通过同一个实例进行,避免了因多个实例同步不及时而导致的数据不一致问题。此外,它还避免了重复创建数据库连接这个耗时的过程,从而显著提升了应用的性能和启动速度。
二、工厂模式
工厂模式主要包括三种类型:简单工厂模式、工厂方法模式和抽象工厂模式。它们的核心都是将对象的创建过程封装起来,从而降低客户端与具体产品之间的耦合。简单工厂模式通过一个单一的工厂类来创建所有产品,优点是实现简单,但缺点是当需要新增产品时必须修改工厂类,不符合开闭原则。为了解决这个问题,工厂方法模式应运而生,它为每一种产品都提供一个独立的工厂,这完美遵循了开闭原则,更易于扩展,但会增加类的数量。抽象工厂模式则更进一步,用于创建一系列相互关联的产品族,它确保了产品族内部的一致性,但其结构更为复杂,并且在新增产品种类时,往往需要修改所有工厂。简单来说,简单工厂适用于简单场景,工厂方法适用于需要频繁扩展新产品的场景,而抽象工厂则适用于需要创建完整产品族的复杂场景。
ViewModel中的工厂方法模式
当我们在使用 ViewModel 的时候,通常不会直接用 new ViewModel()
来创建实例,而是通过 ViewModelProvider
来获取。ViewModelProvider
就是一个工厂,它内部有一个 Factory
接口,这个接口就是工厂方法模式的抽象工厂。
ViewModel中工厂方法模式的作用和意义:
解耦和职责分离:ViewModel 负责处理业务逻辑和数据,而不关心自己如何被创建。
ViewModelProvider.Factory
则专门负责创建 ViewModel 实例。这符合单一职责原则,让 ViewModel 专注于自己的核心任务。支持带参数的ViewModel:如果你的 ViewModel 构造函数需要参数(比如 Repository、Context、或用户ID等),普通的
new ViewModel()
无法满足。通过自定义一个实现了ViewModelProvider.Factory
接口的工厂类,你就可以在工厂的create()
方法中传入这些必要的参数,并正确地创建 ViewModel 实例。处理生命周期:ViewModel 的生命周期由
ViewModelProvider
管理。ViewModelProvider
会检查是否已经有一个特定类的 ViewModel 实例存在于当前生命周期作用域内。如果存在,它会返回现有实例;如果不存在,它会调用你提供的Factory
来创建一个新实例。这确保了 ViewModel 实例在屏幕旋转等配置变更时能够存活下来,避免了不必要的重复创建。
示例解析
假设你有一个 UserViewModel
需要一个 UserRepository
作为参数来获取用户数据。
1. 抽象产品(ViewModel
)和具体产品(UserViewModel
)
public class UserViewModel extends ViewModel {private UserRepository userRepository;public UserViewModel(UserRepository userRepository) {this.userRepository = userRepository;}public LiveData<User> getUser(String userId) {return userRepository.getUser(userId);}
}
2. 抽象工厂(ViewModelProvider.Factory
)和具体工厂(UserViewModelFactory
)
这个 UserViewModelFactory
就是工厂方法模式的具体工厂,它实现了 ViewModelProvider.Factory
接口,负责创建 UserViewModel
。
public class UserViewModelFactory implements ViewModelProvider.Factory {private UserRepository userRepository;public UserViewModelFactory(UserRepository userRepository) {this.userRepository = userRepository;}@NonNull@Overridepublic <T extends ViewModel> T create(@NonNull Class<T> modelClass) {// 在这里创建具体的ViewModel实例,并将UserRepository传入if (modelClass.isAssignableFrom(UserViewModel.class)) {return (T) new UserViewModel(userRepository);}throw new IllegalArgumentException("Unknown ViewModel class");}
}
3. 客户端(Activity/Fragment)
客户端不再直接 new UserViewModel()
,而是通过工厂来获取实例。
public class ProfileFragment extends Fragment {private UserViewModel userViewModel;@Overridepublic void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 假设已经有了一个UserRepository实例UserRepository userRepository = new UserRepository();// 创建工厂实例UserViewModelFactory factory = new UserViewModelFactory(userRepository);// 通过ViewModelProvider获取ViewModel实例,这里使用了工厂方法模式userViewModel = new ViewModelProvider(this, factory).get(UserViewModel.class);}
}
在这个例子中,ViewModelProvider
扮演了工厂管理者的角色,而 UserViewModelFactory
则是真正的创建者,它决定了如何实例化 UserViewModel
。这种模式将 ViewModel 的创建逻辑与它的使用完全解耦,使得代码更易于测试和维护。
线程池中的工厂方法模式
在 Java 的 Executors
类中,提供了许多静态工厂方法来创建不同类型的线程池,例如 newFixedThreadPool()
、newCachedThreadPool()
、newSingleThreadExecutor()
等。这些方法就是工厂方法模式的典型应用。
线程池中工厂方法模式的作用和意义:
简化创建过程:线程池的构造参数非常复杂,包括核心线程数、最大线程数、空闲线程存活时间、阻塞队列、线程工厂等。
Executors
类的静态工厂方法将这些复杂的创建细节封装起来,让开发者无需关心具体的实现参数,只需选择适合自己场景的方法即可,如“我需要一个固定大小的线程池”或“我需要一个按需创建的线程池”。提供标准化的实现:
Executors
类提供了几种预定义的、经过优化的线程池配置,这些配置已经过大量实践验证,可以满足大部分常见需求。解耦和灵活性:开发者只需要使用
ExecutorService
接口,而无需与具体的ThreadPoolExecutor
类耦合。这使得在不改变客户端代码的情况下,可以轻松切换不同类型的线程池实现。
示例解析
我们可以将 Executors
类视为一个抽象工厂,而其中的每个静态方法(如 newFixedThreadPool()
)则是一个具体工厂,每个方法都会返回一个特定配置的线程池实例(即具体产品)。
1. 抽象产品(ExecutorService
)和具体产品(ThreadPoolExecutor
)
在 Java 中,ExecutorService
是一个接口,代表线程池服务。而 ThreadPoolExecutor
是它的一个具体实现类。
// 抽象产品:线程池接口
public interface ExecutorService extends Executor {// ...
}// 具体产品:线程池实现类
public class ThreadPoolExecutor extends AbstractExecutorService {// 构造函数非常复杂public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {// ...}
}
2. 抽象工厂(Executors
类)和具体工厂方法
Executors
类本身不是一个接口,但它提供了一系列静态工厂方法,充当了工厂方法模式中具体工厂的角色。
// Execuors 类,提供各种工厂方法
public class Executors {// 工厂方法:创建一个固定大小的线程池public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());}// 工厂方法:创建一个按需创建新线程的线程池public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());}// 其他工厂方法...
}
3. 客户端使用
开发者只需要调用 Executors
提供的工厂方法,即可获取一个配置好的线程池实例,而无需关心内部的复杂参数。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class MyAndroidApp {// 客户端代码,直接使用工厂方法public void executeTasks() {// 创建一个固定大小为4的线程池ExecutorService executorService = Executors.newFixedThreadPool(4);// 提交任务executorService.submit(() -> {System.out.println("执行后台任务 1");});executorService.submit(() -> {System.out.println("执行后台任务 2");});// 在应用退出时关闭线程池executorService.shutdown();}
}
总结
Executors
类通过提供一系列静态工厂方法,完美地将线程池的创建逻辑与使用逻辑分离开来,这正是工厂方法模式的核心思想。它隐藏了 ThreadPoolExecutor
复杂构造函数的细节,使得线程池的创建和使用变得简单、高效,并且易于维护。
三、其他设计模式应用
建造者模式通过一个独立的“建造者”对象,将复杂对象的构建过程与其表示分离,让你可以用清晰的链式调用来一步步设置对象的属性,最后一次性创建出最终对象,从而解决了构造函数参数过多、可读性差的问题。(OkHttp,Retrofit初始化配置)
责任链模式则是通过将请求处理者连接成一条链,让请求沿着这条链传递,直到有一个处理者能够处理它为止,这种方式将请求的发送者和处理者解耦,使得系统更加灵活、可扩展,并且每个处理者都职责单一。(OkHttp拦截器)