这篇文章主要是针对之前博客的下列文章的总结版本:
主要为了在学习了 Kotlin 之后,将 Java 的设计模式实现与 Kotin 的实现放在一起做一个对比。
一、创建型模式
单例模式(Java)
Double Check Lock + volatile 版本:
public class Singleton {
/** 使用 volatile 保证对该变量的写入对另一个线程可见 */
private volatile static Singleton mSingleton = null;
private Singleton() {
}
public static Singleton getInstance() {
if (mSingleton == null) {
synchronized(Singleton.class) {
if (mSingleton == null) {
mSingleton = new Singleton();
}
}
}
return mSingleton;
}
}
为什么要 Double Check Lock 和 volatile,因为 mSingleton = new Singleton()
这一句翻译成字节码指令可能对应多条,它不是一次性完成的原子性操作,会分成三个步骤完成:
- 给实例分配内存,
- 调用构造函数初始化实例,
- 给变量赋值操作。
这三个步骤中后面两步可能发生指令重排序,即顺序为1-3-2,从而导致双重锁定失效,另一个线程同时访问就会拿到一个还没来得及初始化完毕的对象实例,出现问题。
volatile 在这里的主要作用是禁止指令重排序。另外 volatile 还可以保证线程可见性(但不保证原子性)。
静态内部类版本:
public class Singleton {
private Singleton(){
}
/**
* 静态内部类,内部类的实例与外部类的实例没有绑定关系,
* 只有被调用到时才会加载,从而实现延迟加载
*/
private static class SingletonHolder {
/** 静态初始化器,由 JVM 类加载过程来保证线程安全 */
private static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
注意这个写法,只有getInstance()
方法是public
的,其他静态内部类及其内部静态变量都是private
的。
当getInstance
方法第一次被调用的时候,会导致JVM 虚拟机加载 SingletonHolder
类,它的静态成员变量得到初始化;从而创建 Singleton
的实例。
补充:JVM加载类的过程:加载 -> 验证 -> 准备 -> 解析 -> 初始化
在准备阶段会为SingletonHolder
中的静态成员instance
赋值为null
,在编译时会收集静态赋值语句等组成类构造器<cinit>()
,在初始化阶段会执行类构造器来初始化一个类。
触发 JVM 初始化一个类有以下几种情况:
- 使用
new
关键字创建对象 - 访问类的静态成员变量 或 对类的静态成员变量进行赋值
- 调用类的静态方法
- 反射调用类时,如
Class.forName()
- 初始化子类时,会先初始化其父类(如果父类还没有进行过初始化的话)
- 遇到启动类时,如果一个类被标记为启动类(即包含
main
方法),虚拟机会先初始化这个主类。 - 实现带有默认方法的接口的类被初始化时(拥有被
default
关键字修饰的接口方法的类) - 使用 JDK7 新加入的动态语言支持时
MethodHandle
显然当调用Singleton.getInstance() -> SingletonHolder.instance
时,命中上面第 2 条,JVM 就会执行 SingletonHolder
这个类的初始化,然后调用其类构造器<cinit>()
,这个时候就会初始化Singleton instance
静态成员对象的实例。
在这个过程中, JVM 会保证SingletonHolder
这个类的<clinit>()
方法被正确的加锁同步,在多线程访问的情况下,只会有一个线程看到这个类的初始化过程,而其他线程是不可见的, 其它线程都需要等待,直到<clinit>()
方法执行完毕。所以是线程安全的。
这种方式既保证了线程安全性,又保证了对象的唯一性。并且还是懒加载,只有被调用时才会初始化。
枚举单例:
public enum Singleton {
INSTANCE;
public void doSomething() {
// todo
}
}
Android源码中的单例:
context.getSystemService(name)
:context
的实现类ContextImpl
中使用静态的Map
对象存储ServiceFecther
对象(恶汉)- 线程内的单例:
ThreadLocal
,如Choreographer
和Looper
中都使用ThreadLocal
绑定当前线程,同一个ThreadLocal
对象在同一个线程内返回的对象是唯一的。 - 进程内的单例:如
ActivityManager
类的getService()
方法返回的IActivityManagerSingleton
单例在进程内唯一。 - 进程间的单例:如
ServiceManager
,不同进程之间内存是隔离的,在进程间共享的单例其实是由binder
驱动来保证不同的进程共用同一个对象的。对所有的进程而言,ServiceManager
对应同一个binder
句柄(通过0
号Handle
句柄引用),binder句柄处理完不同进程的请求后会转发给ServiceManager
所在的进程。
Q:怎样在应用里做一个跨进程的单例?
- 单例对象类必须实现
Parcelable
接口,成员变量除了基本数据类型外都必须实现Parcelable
接口 - 通过 AIDL 接口, 拿到远端进程的
IBinder
对象,再进行Binder
调用远端接口的业务方法,将单例对象作为参数发布给远端进程 - 远端进程在业务方法中将接受到单例对象保存下来使用
单例模式(kotlin)
在 kotlin 中 object
类是天生的单例模式。
object Singleton {
var x: Int = 2
fun y() {
}
}
这个代码翻译成 Java 字节码后发现它其实就是恶汉模式的单例。
如果是一个普通类想生成单例,可以使用伴生对象 companion object
来生成:
class Singleton {
companion object {
var x: Int = 2
fun y() {
}
}
}
如果要实现 Java 中静态内部类版本的单例模式,可以像下面这样写:
class Singleton private constructor() {
private object Holder {
val INSTANCE = Singleton()
}
companion object {
val instance = Holder.INSTANCE
}
}
object
不能定义构造函数,但可以定义init
块:
object Singleton {
var x: Int = 2
fun y(){
}
init {
//object不能定义构造函数,但可以定义init块
}
}
如果要在Java中使用kotlin的单例,最好在成员属性和成员方法上分别加上 @JvmField
和 @JvmStatic
注解:
object Singleton {
@JvmField var x: Int = 2
@JvmStatic fun y() {
}
}
@JvmField
的作用是生成java的静态成员 不会生成getter
和setter
方法。@JvmStatic
的作用是生成java的静态方法。
object
修饰的类内部方法相当于静态方法,但是伪静态,也就是内部会生成一个静态的成员对象,对类的方法调用实际上是调用的内部静态成员对象的方法,只有在方法上添加@JvmStatic
才会真正的生成静态方法。
普通kotlin类(非单例)中使用使用JvmField
和JvmStatic
:
class Foo {
//普通类中使用JvmField和JvmStatic
companion object {
@JvmField var x: Int = 2
@JvmStatic fun y(){
}
}
}
注意,加的注解@JvmField
和@JvmStatic
只针对java平台的可用,其他平台并不可行。
单例的object
类仍可以继承类:
object Singleton: Runnable{
override fun run() {
}
}
工厂方法模式(Java)
- 定义了一个创建对象的接口,但是由子类来决定要实例化的是哪一个类。工厂方法使类的实例化延迟到子类中。
public abstract class Product {
public void method1() {
}
public abstract void method();
}
public class ConcreteProductA extends Product {
@Override
public void method2() {
//ProductA的业务处理
}
}
public class ConcreteProductB extends Product {
@Override
public void method2() {
//ProductB的业务处理
}
}
public abstract class Factory {
/**
* 此方法必须返回一个Product类型的对象
*/
public abstract <T extends Product> T createProduct(Class<T> c);
}
public class ConcreteFactory extends Factory {
@Override
public <T extends Product> T createProduct(Class<T> c) {
//创建Product的逻辑,这里是直接调用Class的newInstance方法
Product product = null;
try {
product = (Product) Class.forName(c.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T)product;
}
}
public class Client {
public static void main(String[] args) {
Creator creator = new ConcreteCreator();
Product productA = creator.createProduct(ConcreteProductA.class);
Product productB = creator.createProduct(ConcreteProductB.class);
......
}
}
简单工厂模式:如果实际当中只需要一个工厂类,那么就不需要抽象的工厂基类,可以把创建产品的方法改为静态方法。
public class HumanFactory {
@Override
public static <T extends Human> T createHuman(Class<T> c) {
Human human = null;
try {
human = (Human) Class.forName(c.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T) human;
}
}
多个工厂类:在实际当中如果初始化创建对象的过程比较复杂,比如不同的产品可能会设置不同的参数,这个时候创建产品的方法不能共用,所以这时就需要为每一个产品类创建一个工厂类。
可生成单例模式的工厂类:既然工厂方法是用来生产对象的,那么就可以对工厂方法做简单的修改,只返回一个对象,就变成了单例模式。
public class Singleton {
//不允许通过new产生对象
private Singleton(){
}
public void doSomething() {
//业务处理
}
}
public class SingletonFactory {
private static Singleton singleton;
static {
try {
Class<?> clazz = Class.forName(Singleton.class.getName());
//获取无参的构造函数
Constructor<?> constructor = clazz.getDeclaredConstructor();
//设置无参的构造函数可访问
constructor.setAccessible(true);
//创建一个实例对象
singleton = (Singleton) constructor.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
public static Singleton getSingleton() {
return singleton;
}
}
可复用缓存的工厂类:如果创建对象的过程比较复杂,或者非常耗时,可以在工厂类内部对已创建的对象进行缓存,以备下次使用。
public class ProductFactory {
private static final Map<String, Product> productMap = new HashMap<>();
public static synchronized Product createProduct(String type) {
Product product = null;
//如果缓存中有该对象实例直接使用
if (productMap.containsKey(type)) {
product = productMap.get(type);
} else {
if (type.equals("ProductA")) {
product = new ConcreteProductA();
} else {
product = new ConcreteProductB();
}
//把创建的对象缓存起来
productMap.put(type, product);
}
return product;
}
}
Android源码中的工厂方法模式:
- 集合类的
iterator()
返回的是一个新的迭代器对象,其实就是一个工厂方法。 Activity
的onCreate()
方法某种角度上可以看作是一个用于创建ContentView
的工厂方法,该ContentView
用于填充PhoneWindow
的DecorView
的内容区。
抽象工厂模式(Java)
- 为创建一组相关或依赖的对象提供一个接口,而无需指定它们的具体类。
/**
* 抽象工厂
*/
public interface AbstractFactory {
/**创建A产品家族的产品*/
public AbstractProductA createProductA();
/**创建B产品家族的产品*/
public AbstractProductB createProductB();
}
public class ConcreteFactory1 implements AbstractFactory {
@Override
public AbstractProductA createProductA() {
//创建一个来自A产品家族的产品
return new ProductA1();
}
@Override
public AbstractProductB createProductB() {
//创建一个来自B产品家族的产品
return new ProductB1();
}
}
public class ConcreteFactory2 implements AbstractFactory {
@Override
public AbstractProductA createProductA() {
//创建一个来自A产品家族的产品
return new ProductA2();
}
@Override
public AbstractProductB createProductB() {
//创建一个来自B产品家族的产品
return new ProductB2();
}
}
/**
* 产品A家族
*/
public abstract class AbstractProductA {
/**每个产品共有的方法*/
public void shareMethod() {
}
/**每个产品不同实现的方法*/
public abstract void doSomething();
}
public class ProductA1 extends AbstractProductA {
@Override
public void doSomething() {
System.out.println("产品A1的实现方法");
}
}
public class ProductA2 extends AbstractProductA {
@Override
public void doSomething() {
System.out.println("产品A2的实现方法");
}
}
/**
* 产品B家族
*/
public abstract class AbstractProductB {
/**每个产品共有的方法*/
public void shareMethod() {
}
/**每个产品不同实现的方法*/
public abstract void doSomething();
}
public class ProductB1 extends AbstractProductB {
@Override
public void doSomething() {
System.out.println("产品B1的实现方法");
}
}
public class ProductB2 extends AbstractProductB {
@Override
public void doSomething() {
System.out.println("产品B2的实现方法");
}
}
public class Client {
public static void main(String[] args) {
//创建两个工厂
AbstractFactory factory1 = new ConcreteFactory1();
AbstractFactory factory2 = new ConcreteFactory2();
//使用factory1来创建一组产品,它们来自不同的产品家族
AbstractProductA productA1 = factory1.createProductA();
AbstractProductB productB1 = factory1.createProductB();
//使用factory2来创建一组产品,它们来自不同的产品家族
AbstractProductA productA2 = factory2.createProductA();
AbstractProductB productB2 = factory2.createProductB();
//do something...
}
}
抽象工厂模式其实就是每个工厂类是为生产某一类相关性很强的产品类族专门特供的版本。
工厂方法模式(Kotlin)
用单例代替工厂类
我们已经知道 Kotlin 支持用 object
来实现 Java 中的单例模式。所以我们可以使用一个 object
单例来代替一个工厂类。
object ComputerFactory {
// 用 object 代替 class
fun produce(type: ComputerType): Computer {
return when (type) {
ComputerType.PC -> PC()
ComputerType.Server -> Server()
}
}
}
fun main() {
val compter = ComputerFactory.produce(ComputerType.PC)
println(compter.cpu)
}
我们可以通过operator
操作符重载invoke
方法来代替produce
,从而进一步简化表达:
object ComputerFactory {
operator fun invoke(type: ComputerType): Computer {
return when (type) {
ComputerType.PC -> PC()
ComputerType.Server -> Server()
}
}
}
fun main() {
val compter = ComputerFactory(ComputerType.PC)
println(compter.cpu)
}
伴生对象创建静态工厂方法
interface Computer {
val cpu: String
companion object {
operator fun invoke(type: ComputerType): Computer {
return when (type) {
ComputerType.PC -> PC()
ComputerType.Server -> Server()
}
}
}
}
class PC(override val cpu: String = "Core") : Computer
class Server(override val cpu: String = "Xeon") : Computer
enum class ComputerType {
PC, Server }
fun main() {
val compter = Computer(ComputerType.PC)
println(compter.cpu)
}
这样就可以在一个接口类中添加伴生对象的方式来创建静态工厂方法。
伴生对象也可以指定名字:
interface Computer {
val cpu: String
companion object Factory {
operator fun invoke(type: ComputerType): Computer {
return when (type) {
ComputerType.PC -> PC()
ComputerType.Server -> Server()
}
}
}
}
fun main() {
val compter = Computer.Factory(ComputerType.PC)
println(compter.cpu)
}
为伴生对象添加扩展方法
主要是针对三方类库中无法直接修改源码的类。
fun Computer.Companion.fromCPU(cpu: String): ComputerType? = when(cpu) {
"Core" -> ComputerType.PC
"Xeon" -> ComputerType.Server
else -> null
}
指定伴生对象名字的写法:
fun Computer.Factory.fromCPU(cpu: String): ComputerType? = when(cpu) {
"Core" -> ComputerType.PC
"Xeon" -> ComputerType.Server
else -> null
}
调用:
fun main() {
val type = Computer.fromCPU("Core")
println(type)
}
抽象工厂模式(Kotlin)
Kotlin 中的可以使用内联函数 inline + reified
关键字来简化抽象工厂模式:
class Dell: Computer {
}
class Asus: Computer {
}
class Acer: Computer {
}
class DellFactory: AbstractFactory() {
override fun produce() = Dell()
}
class AsusFactory: AbstractFactory() {
override fun produce() = Asus()
}
class AcerFactory: AbstractFactory() {
override fun produce() = Acer()
}
abstract class AbstractFactory {
abstract fun produce(): Computer
companion object {
inline operator fun <reified T : Computer> invoke(): AbstractFactory = when(T::class) {
Dell::class -> DellFactory()
Asus::class -> AsusFactory()
Acer::class -> AcerFactory()
else -> throw IllegalArgumentException()
}
}
}
fun main() {
val dellFactory = AbstractFactory<Dell>()
val dell = dellFactory.produce()
println(dell)
}
建造者模式(Java)
- 封装一个产品的构建过程,并允许按步骤构造。
- 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
public class Robot {
private final String code;
private final String battery;
private final Integer height;
private final Integer weight;
private Robot(String code, String battery, Integer height, Integer weight) {
this.code = code;
this.battery = battery;
this.height = height;
this.weight = weight;
}
public static class Builder {
private final String code;
private String battery;
private Integer height;
private Integer weight;
public Builder(String code) {
this.code = code;
}
public Builder setBattery(String battery) {
this.battery = battery;
return this;
}
public Builder setHeight(int height) {
this.height = height;
return this;
}
public Builder setWeight(int weight) {
this.weight = weight;
return this;
}