创建型设计模式
- 1、单例模式
- 1.1 饿汉式 (静态常量)
- 1.2 饿汉式(静态代码块)
- 1.3 懒汉式 线程不安全
- 1.4 懒汉式 线程安全,同步方法
- 1.5 懒汉式 线程安全,同步代码块
- 1.6 双重检查【推荐】
- 1.7 静态内部类【推荐】
- 1.8 枚举【推荐】
- 2、工厂模式(简单工厂、工厂方法、抽象工厂)
- 2.1 简单工厂
- 2.2 工厂方法
- 2.3 抽象工厂
- 2.4 工作中常用工厂模式的形式
- 3、原型模式
- 4、建造者模式
- 5、创建者模式对比
- 5.1 工厂方法模式VS建造者模式
- 5.2 抽象工厂模式VS建造者模式
1、单例模式
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例 ,并且该类只提供一个取得其对象实例的方法静态方法 。
1.1 饿汉式 (静态常量)
结论:可用,类加载时创建,可能造成内存浪费。
class Singleton {
//1. 构造器私有化, 外部能new
private Singleton() {
}
//2.本类内部创建对象实例
private final static Singleton instance = new Singleton();
//3. 提供一个公有的静态方法,返回实例对象
public static Singleton getInstance() {
return instance;
}
}
1.2 饿汉式(静态代码块)
结论:同上
class Singleton {
//1. 构造器私有化, 外部能new
private Singleton() {
}
//2.本类内部创建对象实例
private static Singleton instance;
static { // 在静态代码块中,创建单例对象
instance = new Singleton();
}
//3. 提供一个公有的静态方法,返回实例对象
public static Singleton getInstance() {
return instance;
}
}
1.3 懒汉式 线程不安全
结论:线程不安全,当两个线程都进入if判断,则创建两个实例。不是单例。
class Singleton {
private static Singleton instance;
private Singleton() {}
//提供一个静态的公有方法,当使用到该方法时,才去创建 instance
//即懒汉式
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
1.4 懒汉式 线程安全,同步方法
结论:线程安全,效率低。
// 懒汉式(线程安全,同步方法)
class Singleton {
private static Singleton instance;
private Singleton() {}
//提供一个静态的公有方法,加入同步处理的代码,解决线程安全问题
//即懒汉式
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
1.5 懒汉式 线程安全,同步代码块
错误,同1.1.3 线程不安全,两个线程都进入if判断,则创建两个实例。不是单例。
1.6 双重检查【推荐】
结论:推荐该方法
// 懒汉式(线程安全,同步方法)
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;
}
}
1.7 静态内部类【推荐】
结论:推荐使用
1、采用了类装载的机制来保证初始化实例时只有一个线程 。
2、静态内部类方式在 Singleton 类被装载时并不会立即实例化,而是在需要实例化时,调用 getInstance 方法,才会装载。JVM 帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的 。
// 静态内部类完成, 推荐使用
class Singleton {
private static volatile Singleton instance;
//构造器私有化
private Singleton() {}
//写一个静态内部类,该类中有一个静态属性 Singleton
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
//提供一个静态的公有方法,直接返回SingletonInstance.INSTANCE
public static synchronized Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
1.8 枚举【推荐】
结论:推荐使用。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象
//使用枚举,可以实现单例, 推荐
enum Singleton {
INSTANCE; //属性
public void sayOK() {
System.out.println("ok~");
}
}
2、工厂模式(简单工厂、工厂方法、抽象工厂)
2.1 简单工厂
【概念】:简单工厂模式是由一个工厂对象决定创建出哪一 种产品类 的实例 。简单工厂模式是工厂模式家族中最简单实用的模式
【结构】:抽象产品:定义了产品的规范,描述了产品的主要特性和功能。
具体产品:实现或者继承抽象产品的子类
具体工厂:提供了创建产品的方法,调用者通过该方法来获取产品。
【缺点】:增加新产品时还是需要修改工厂类的代码,违背了“开闭原则”。
【实现】:
public class SimpleCoffeeFactory {
public Coffee createCoffee(String type) {
Coffee coffee = null;
if("americano".equals(type)) {
coffee = new AmericanoCoffee();
} else if("latte".equals(type)) {
coffee = new LatteCoffee();
}
return coffee;
}
}
静态工厂模式
public class SimpleCoffeeFactory {
public static Coffee createCoffee(String type) {
Coffee coffee = null;
if("americano".equals(type)) {
coffee = new AmericanoCoffee();
} else if("latte".equals(type)) {
coffee = new LatteCoffee();
}
return coffe;
}
}
2.2 工厂方法
【概念】:定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。
【结构】:抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
【缺点】:每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。产生类爆炸。
【实现】:
//抽象工厂
public interface CoffeeFactory {
Coffee createCoffee();
}
//具体工厂
public class LatteCoffeeFactory implements CoffeeFactory {
public Coffee createCoffee() {
return new LatteCoffee();
}
}
public class AmericanCoffeeFactory implements CoffeeFactory {
public Coffee createCoffee() {
return new AmericanCoffee();
}
}
//咖啡店类:
public class CoffeeStore {
private CoffeeFactory factory;
public CoffeeStore(CoffeeFactory factory) {
this.factory = factory;
}
public Coffee orderCoffee() {
Coffee coffee = factory.createCoffee();
coffee.addMilk();
coffee.addsugar();
return coffee;
}
}
2.3 抽象工厂
【概念】:抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
【缺点】:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。
【结构】:抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。
具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它 同具体工厂之间是多对一的关系。
【实现】:
//抽象工厂
public interface DessertFactory {
Coffee createCoffee();
Dessert createDessert();
}
//具体工厂
//美式甜点工厂
public class AmericanDessertFactory implements DessertFactory {
public Coffee createCoffee() {
return new AmericanCoffee();
}
public Dessert createDessert() {
return new MatchaMousse();
}
}
//意大利风味甜点工厂
public class ItalyDessertFactory implements DessertFactory {
public Coffee createCoffee() {
return new LatteCoffee();
}
public Dessert createDessert() {
return new Tiramisu();
}
}
2.4 工作中常用工厂模式的形式
简单工厂+配置文件解除耦合
工作实际开发中用到的工厂模式形式
3、原型模式
【概念】:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象。
【结构】:抽象原型类:规定了具体原型对象必须实现的的 clone() 方法。
具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
访问类:使用具体原型类中的 clone() 方法来复制新的对象。
【分类】:浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型的属性,仍指向原有属性所指向的对象的内存地址。
深克隆:创建一个新对象,属性中引用的其他对象的属性
也会被克隆,不再指向原有对象地址。
【实现】:1、浅克隆
//奖状类
public class Citation implements Cloneable {
private Student stu;
public Student getStu() {
return stu;
}
public void setStu(Student stu) {
this.stu = stu;
}
void show() {
System.out.println(stu.getName() + "同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!");
}
@Override
public Citation clone() throws CloneNotSupportedException {
return (Citation) super.clone();
}
}
//测试类
public class CitationTest {
public static void main(String[] args) throws CloneNotSupportedException {
Citation c1 = new Citation();
Student stu = new Student("张三", "西安");
c1.setStu(stu);
//复制奖状
Citation c2 = c1.clone();
//获取c2奖状所属学生对象
Student stu1 = c2.getStu();
stu1.setName("李四");
//判断stu对象和stu1对象是否是同一个对象
System.out.println("stu和stu1是同一个对象?" + (stu == stu1));
c1.show();
c2.show();
}
}
2、深克隆
public class CitationTest1 {
public static void main(String[] args) throws Exception {
Citation c1 = new Citation();
Student stu = new Student("张三", "西安");
c1.setStu(stu);
//创建对象输出流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Think\\Desktop\\b.txt"));
//将c1对象写出到文件中
oos.writeObject(c1);
oos.close();
//创建对象出入流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Think\\Desktop\\b.txt"));
//读取对象
Citation c2 = (Citation) ois.readObject();
//获取c2奖状所属学生对象
Student stu1 = c2.getStu();
stu1.setName("李四");
//判断stu对象和stu1对象是否是同一个对象
System.out.println("stu和stu1是同一个对象?" + (stu == stu1));
c1.show();
c2.show();
}
}
4、建造者模式
【概念】:将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。如cpu、内存条以及显示屏等
与 组装好电脑
。
【结构】:抽象建造者类(Builder)
:这个接口规定要实现复杂对象的那些部分的创建,并不涉及具体的部件对象的创建。
具体建造者类(ConcreteBuilder)
:实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。在构造过程完成后,提供产品的实例。
产品类(Product):要创建的复杂对象。
指挥者类(Director)
:调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。
【缺点】:建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
【实现】:
1、抽象建造者类(Builder):
// 抽象 builder 类
public abstract class Builder {
protected Bike mBike = new Bike();//允许子类继承
public abstract void buildFrame();
public abstract void buildSeat();
public abstract Bike createBike();
}
2、具体建造者类(ConcreteBuilder)
public class MobikeBuilder extends Builder {
@Override
public void buildFrame() {
mBike.setFrame("铝合金车架");
}
@Override
public void buildSeat() {
mBike.setSeat("真皮车座");
}
@Override
public Bike createBike() {
return mBike;
}
}
3、指挥者类(Director)
//指挥者类
public class Director {
private Builder mBuilder;
public Director(Builder builder) {
mBuilder = builder;
}
public Bike construct() {
mBuilder.buildFrame();
mBuilder.buildSeat();
return mBuilder.createBike();
}
}
4、简化系统结构,可以把指挥者类和抽象建造者进行结合
// 抽象 builder 类
public abstract class Builder {
protected Bike mBike = new Bike();
public abstract void buildFrame();
public abstract void buildSeat();
public abstract Bike createBike();
public Bike construct() {
this.buildFrame();
this.BuildSeat();
return this.createBike();
}
}
【链式调用】
public class Phone {
private String cpu;
private String screen;
private String memory;
private String mainboard;
private Phone(Builder builder) {
cpu = builder.cpu;
screen = builder.screen;
memory = builder.memory;
mainboard = builder.mainboard;
}
public static final class Builder {
private String cpu;
private String screen;
private String memory;
private String mainboard;
public Builder() {}
public Builder cpu(String val) {
cpu = val;
return this;
}
public Builder screen(String val) {
screen = val;
return this;
}
public Builder memory(String val) {
memory = val;
return this;
}
public Builder mainboard(String val) {
mainboard = val;
return this;
}
public Phone build() {
return new Phone(this);}
}
@Override
public String toString() {
return "Phone{" +
"cpu='" + cpu + '\'' +
", screen='" + screen + '\'' +
", memory='" + memory + '\'' +
", mainboard='" + mainboard + '\'' +
'}';
}
}
public class Client {
public static void main(String[] args) {
Phone phone = new Phone.Builder()
.cpu("intel")
.mainboard("华硕")
.memory("金士顿")
.screen("三星")
.build();
System.out.println(phone);
}
}
5、创建者模式对比
5.1 工厂方法模式VS建造者模式
工厂方法模式注重的是整体对象的创建方式;而建造者模式注重的是部件构建的过程,意在通过一步一步地精确构造创建出一个复杂的对象。
我们举个简单例子来说明两者的差异,如要制造一个超人,如果使用工厂方法模式,直接产生出来的就是一个力大无穷、能够飞翔、内裤外穿的超人;而如果使用建造者模式,则需要组装手、头、脚、躯干等部分,然后再把内裤外穿,于是一个超人就诞生了。
5.2 抽象工厂模式VS建造者模式