创建型模式
创建型模式的作用就是创建对象,说到创建一个对象,最熟悉的就是 new 一个对象,然后 set 相关属性。但是,在很多场景下,我们需要给客户端提供更加友好的创建对象的方式,尤其是那种我们定义了类,但是需要提供给其他开发者用的时候。
简单工厂模式
简单工厂模式通常就是一个工厂类 XxxFactory,里面有一个静态方法,根据我们不同的参数,返回不同的派生自同一个父类(或实现同一接口)的实例对象。
工厂模式
引入工厂模式,是因为我们往往需要使用两个或两个以上的工厂。
XxxFactory工厂接口A,有个方法。XxxFactory工厂类A1,XxxFactory工厂类A2实现了那个接口里的方法。每个模式中和简单工厂模式一样。
抽象工厂模式
当涉及到产品族的时候,就需要引入抽象工厂模式了。
违反了对修改关闭,对扩展开放这个设计原则。
单例模式——1个类只有一个实例
枚举,枚举很特殊,它在类加载的时候会初始化里面的所有的实例,而且 JVM 保证了它们不会再被实例化,所以它天生就是单例的。
虽然我们平时很少看到用枚举来实现单例,但是在 RxJava 的源码中,有很多地方都用了枚举来实现单例。
饿汉模式:
- private构造方法(防止被实例化)
- 先实例一个static的,private static SingleObject instance = new SingleObject();
- 一个static获得该对象的方法,直接return
懒汉式/饱汉模式 的优化双重检测:
- private构造方法(防止被实例化)
- 先不实例出来,但要volatile,private static volatile Singleton instance = null;
- 一个static获得该对象的方法,非null直接return,null就加锁 synchronized在类上 再实例化。
public static Singleton getInstance() {
if (instance == null) {
// 加锁
synchronized (Singleton.class) {
// 这一次判断也是必须的,不然会有并发问题
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
// 双重检查:instance为null
嵌套类:嵌套类最经典,强烈推荐★★★
- private构造方法(防止被实例化)
- 实例化在嵌套类
- 一个static获得该对象的方法,调用嵌套类的那个实例。
public class Singleton3 {
private Singleton3() {}
// 主要是使用了 嵌套类可以访问外部类的静态属性和静态方法 的特性
// 实例化的部分在嵌套类中
private static class Holder {
private static Singleton3 instance = new Singleton3();
}
public static Singleton3 getInstance() {
return Holder.instance;
}
}
枚举类:所有单例实现唯一不会被破坏的
枚举类实现单例模式相当硬核,因为枚举类型是线程安全的,且只会装载一次。使用枚举类来实现单例模式,是所有的单例实现中唯一一种不会被破坏的单例模式实现。
- private构造方法(防止被实例化)
- 枚举类中:
- final对象,private构造方法实例化
- 获取枚举对象
- 一个static获得该对象的方法,调用枚举类的那个实例。
public class SingletonObject {
private SingletonObject() {
}
// 内部创建枚举类:枚举类型是线程安全的且只会装载一次
private enum Singleton {
INSTANCE;
private final SingletonObject instance;
Singleton() {
instance = new SingletonObject();
}
private SingletonObject getInstance() {
return instance;
}
}
public static SingletonObject getInstance() {
return Singleton.INSTANCE.getInstance();
}
}
建造者模式
XxxBuilder ,对于客户端来说,我们的使用通常都是一个模式的:
- 先 new 一个 Builder
- 然后可以链式地调用一堆方法:怎样链式调用?只要那些方法返回那个类就行。
- 最后再调用一次 build() 方法,我们需要的对象就有了
// 1.先 new 一个 Builder
// 2.然后可以链式地调用一堆方法:怎样链式调用?只要那些方法返回那个类就行。
// 3.最后再调用一次 build() 方法,我们需要的对象就有了
Food food = new FoodBuilder().a().b().c().build();
Food food = Food.builder().a().b().c().build();
中规中矩的建造者模式示例:
核心是:先把所有的属性都设置给 Builder,然后 build() 方法的时候,将这些属性复制给实际产生的对象。
User类
- 有一堆属性和私有构造方法
- 返回一个static的 UserBuilder对象:new Builder
- public build()方法:将 UserBuilder 中设置好的属性“复制”到 User 中,new出一个User对象,return。(最后一步)
UserBuilder在User类中
- 有一堆属性和私有构造方法
- 链式调用设置各个属性值,返回 this,即 UserBuilder
class User {
// 下面是“一堆”的属性
private String name;
private String password;
private String nickName;
private int age;
// 构造方法私有化,不然客户端就会直接调用构造方法了
private User(String name, String password, String nickName, int age) {
this.name = name;
this.password = password;
this.nickName = nickName;
this.age = age;
}
// 静态方法,用于生成一个 Builder,这个不一定要有,不过写这个方法是一个很好的习惯,
// 有些代码要求别人写 new User.UserBuilder().a()...build() 看上去就没那么好
public static UserBuilder builder() {
return new UserBuilder();
}
public static class UserBuilder {
// 下面是和 User 一模一样的一堆属性
private String name;
private String password;
private String nickName;
private int age;
private UserBuilder() {
}
// 链式调用设置各个属性值,返回 this,即 UserBuilder
public UserBuilder name(String name) {
this.name = name;
return this;
}
public UserBuilder password(String password) {
this.password = password;
return this;
}
public UserBuilder nickName(String nickName) {
this.nickName = nickName;
return this;
}
public UserBuilder age(int age) {
this.age = age;
return this;
}
// build() 方法负责将 UserBuilder 中设置好的属性“复制”到 User 中。
// 当然,可以在 “复制” 之前做点检验
public User build() {
if (name == null || password == null) {
throw new RuntimeException("用户名和密码必填");
}
if (age <= 0 || age >= 150) {
throw new RuntimeException("年龄不合法");
}
// 还可以做赋予”默认值“的功能
if (nickName == null) {
nickName = name;
}
return new User(name, password, nickName, age);
}
}
}
当属性很多,而且有些必填,有些选填的时候,这个模式会使代码清晰很多。我们可以在 Builder 的构造方法中强制让调用者提供必填字段,还有,在 build() 方法中校验各个参数比在 User 的构造方法中校验,代码要优雅一些。
使用 lombok:@Builder
强烈建议读者使用 lombok,用了 lombok 以后,上面的一大堆代码会变成如下这样:
@Builder
class User {
private String name;
private String password;
private String nickName;
private int age;
}
当然,如果你只是想要链式写法,不想要建造者模式,有个很简单的办法,User 的 getter 方法不变,所有的 setter 方法都让其 return this 就可以了,然后就可以像下面这样调用:
User user = new User().setName("").setPassword("").setAge(20);
原型模式
在原型实例的基础上产生新的实例。
Object 类中有一个 clone() 方法,它用于生成一个新的对象,当然,如果我们要调用这个方法,java 要求我们的类必须先实现 Cloneable 接口,此接口没有定义任何方法,但是不这么做的话,在 clone() 的时候,会抛出 CloneNotSupportedException 异常。
protected native Object clone() throws CloneNotSupportedException;
java 的克隆是浅克隆,碰到对象引用的时候,克隆出来的对象和原对象中的引用将指向同一个对象。通常实现深克隆的方法是将对象进行序列化,然后再进行反序列化。
原型模式了解到这里我觉得就够了,各种变着法子说这种代码或那种代码是原型模式,没什么意义。
创建型模式总结
作用:创建实例对象
简单工厂模式最简单;工厂模式在简单工厂模式的基础上增加了选择工厂的维度,需要第一步选择合适的工厂;抽象工厂模式有产品族的概念,如果各个产品是存在兼容性问题的,就要用抽象工厂模式。
单例模式,为了保证全局使用的是同一对象,一方面是安全性考虑,一方面是为了节省资源;
建造者模式专门对付属性很多的那种类,为了让代码更优美;
原型模式用得最少,了解和 Object 类中的 clone() 方法相关的知识即可。
引用:设计模式也可以这么简单