Java设计模式---创建型模式
本章Java设计模式的创建型模式的介绍,是通过学习视频记录的笔记,欢迎留言指出错误点
创建型模式(五种):单例模式、工厂方法模式、抽象工厂模式、原型模式、建造者模式
1. 单例模式
概念:涉及到一个单一的类(这个类只能创建一个对象),创建的是自己的对象,同时只能单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
主要以下角色:
单例类:只能创建一个实例的类
访问类:使用单例类(测试类的使用单例类)
分类:
- 饿汉式:类加载就会导致该实例对象被创建
- 懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建
1.1 饿汉式
/** * 单例模式 * 饿汉式:静态成员变量 */ public class Singleton { //1.私有构造方法,外界无法实例化 private Singleton(){} //2.在本类中创建本来对象 private static Singleton instance = new Singleton(); //3.提供一个公共的访问方式,让外界获取该对象 public static Singleton getInstance(){ return instance; } } /** * 单例模式 * 饿汉式:静态代码块 */ public class Singleton2 { //1.私有构造方法,外界无法实例化 private Singleton2(){} //2.声明Singleton2类型的变量 private static Singleton2 instance;//null //3.在静态代码块中进行赋值 static { instance = new Singleton2(); } //对外提供获取该类对象的方法 public static Singleton2 getInstance(){ return instance; } }
1.2 懒汉式:
/** * 单例模式 * 懒汉式:线程不安全 * * 线程不安全分析: * if判断和内存执行代码是非原子性,其次instance = new Singleton3();无法保证执行顺序 * 不满足原子性或顺序性的线程是不安全的 * 注: * 可见性:当线程A对共享变量进行修改,其他线程立即可见 * 原子性:一个或者多个操作的执行,不回被其他因素打扰,要么全部执行,要么都不执行 * int a=0;//具有原子性 * int b=a;//不具有原子性,分两步,第一步获取a的值,然后将值写入工作内存给b赋值。 * set方法是原子性,++自增不是原子性 * 有序性:程序按照代码编写的先后顺序执行 * 指令重排:cpu为了提供程序执行效率,会对指令执行的顺序进行重排 * int a = 1; int b = 2;会被指令重排 * int a = 1; int b = a;不会被指令重排 */ public class Singleton3 { private static Singleton3 instance = null; private Singleton3(){} public static Singleton3 getInstance(){ if (instance == null){ /** * 创建对象有三个步骤: * (1)初始化内存空间 * (2)初始化对象 * (3)设置instance指向刚分配的内存地址 * 步骤2) 3)会指令重排,可能造成同时两个对象都instance==null 为true */ instance = new Singleton3(); } return instance; } } /** * 单例模式 * 懒汉式:线程安全 Singleton3的getInstance用synchronized修饰 */ /** * 单例模式 * 懒汉式:双重检查锁 */ public class Singleton4 { private static volatile Singleton4 instance = null; private Singleton4(){} public static Singleton4 getInstance(){ if (instance == null){ synchronized (instance){ if (instance == null) { instance = new Singleton4(); } } } return instance; } } /** * 静态内部类 */ public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }
最佳实战:
一般情况下,不建议使用懒汉方式,建议使用饿汉方式只有在要明确实现 lazy loading 效果时,才会使用静态内部类如果涉及到反序列化创建对象时,可以尝试使用第 枚举方式如果有其他特殊的需求,可以考虑使用双检锁方式
2. 工厂模式
分类:
- 简单工厂
- 工厂方法
- 抽象工厂
2.1 简单工厂模式
结构:
抽象产品:定义产品规范,描述产品的主要特性和功能(coffee)
具体产品:实现或继承抽象产品的子类(AmericanCoffee LatteCoffee)
具体工厂:提供创建产品的方法,调用者通过该方法来获取产品(SimpleCoffeeFactory)
public abstract class Coffee { public abstract String getName(); //加糖 public void addsugar() { System.out.println("加糖"); } //加奶 public void addMilk() { System.out.println("加奶"); } } public class AmericanCoffee extends Coffee { public String getName() { return "美式咖啡"; } } public class LatteCoffee extends Coffee { public String getName() { return "拿铁咖啡"; } } /** * 简单工厂 * 新增新的品种咖啡,需要改动工厂代码,违背的开闭原则 */ public class SimpleCoffeeFactory { public Coffee createCoffee(String type){ Coffee coffee = null; if ("americano".equals(type)){ coffee = new AmericanCoffee(); }else if ("latte".equals(type)){ coffee = new LatteCoffee(); } return coffee; } }
2.2 工厂方法模式
概念:定义一个用户创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到工厂的子类
结构:
抽象工厂:提供了创建产品的接口,调用者通过访问具体工厂的工厂方法来创建产品(CoffeeFactory)
具体工厂:主要是实现抽象工厂的抽象方法,完成具体产品的创建(AmericanCoffeeFactory LatteCoffeeFactory)
抽象产品:定义产品规范,描述产品的主要特性和功能(coffee)
具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应(CoffeeStore)
/** * 工厂方法模式: * * 抽象工厂 */ public interface CoffeeFactory { Coffee createCoffee(); } /** * 具体工厂 */ public class AmericanCoffeeFactory implements CoffeeFactory{ public Coffee createCoffee() { return new AmericanCoffee(); } } /** * 具体工厂 */ public class LatteCoffeeFactory implements CoffeeFactory{ public Coffee createCoffee() { return new LatteCoffee(); } } /** * 咖啡店 */ public class CoffeeStore { private CoffeeFactory coffeeFactory; public CoffeeStore(CoffeeFactory coffeeFactory){ this.coffeeFactory = coffeeFactory; } public Coffee orderCoffee(){ Coffee coffee = coffeeFactory.createCoffee(); coffee.addMilk(); coffee.addsugar(); return coffee; } } //测试 CoffeeFactory factory = new AmericanCoffeeFactory(); CoffeeStore store = new CoffeeStore(factory); Coffee coffee = store.orderCoffee(); System.out.println(coffee.getName());
优点:
- 用户只需要知道具体工厂名称就可得到所要的产品,无须知道产品的具体创建过程
- 在系统增加新的产品时,只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则
缺点:
- 每增加一个产品就要增加一个具体产品类和对应的具体工厂类,增加系统复杂度
3. 抽象工厂模式
概念:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构
结构:
(工厂方法,为了跟抽象工厂对比)
抽象工厂:提供了创建产品的接口,调用者通过访问具体工厂的工厂方法来创建产品(CoffeeFactory)
具体工厂:主要是实现抽象工厂的抽象方法,完成具体产品的创建(AmericanCoffeeFactory LatteCoffeeFactory)
抽象产品:定义产品规范,描述产品的主要特性和功能(coffee)
具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应(CoffeeStore)
(抽象工厂)
抽象工厂:提供了创建产品的接口,包含多个创建产品的方法,可以创建多个不同等级的产品(DessertFactory)
具体工厂:主要是实现抽象工厂的抽象方法,完成具体产品的创建(AmericanDessertFactory ItalyDessertFactory)
抽象产品:定义产品规范,描述产品的主要特性和功能,抽象工厂模式有多个抽象产品(coffee Dessert)
具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应
/** * 甜品抽象类 */ public abstract class Dessert { public abstract void show(); } /** * 抹茶慕斯类 */ public class MatchaMousse extends Dessert { public void show() { System.out.println("抹茶慕斯"); } } /** * 提拉米苏类 */ public class Trimisu extends Dessert { public void show() { System.out.println("提拉米苏"); } } /** * 抽象工厂模式 * 抽象工厂 */ 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 Trimisu(); } } //测试 AmericanDessertFactory factory = new AmericanDessertFactory(); Coffee coffee = factory.createCoffee(); Dessert dessert = factory.createDessert(); System.out.println(coffee.getName()); dessert.show();
模式扩展:简单工厂+配置文件
bean.properties american=com.project.pattern.factory.AmericanCoffee latte=com.project.pattern.factory.LatteCoffee /** * 模式扩展:简单工厂+配置文件 */ public class CoffeeFactory { //加载配置文件,获取配置文件中配置的全类名,并创建该类的对象进行存储 //1,定义容器对象存储咖啡对象 private static HashMap<String,Coffee> map = new HashMap<String, Coffee>(); //2,加载配置文件,只需要加载一次 static { //2.1 创建Properties对象 Properties p = new Properties(); //2.2 调用p对象中的load方法进行配置文件的加载 InputStream is = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties"); try { p.load(is); //从p集合中获取全类名并创建对象 Set<Object> keys = p.keySet(); for (Object key : keys) { String className = p.getProperty((String) key); //通过反射技术创建对象 Class clazz = Class.forName(className); Coffee coffee = (Coffee) clazz.newInstance(); //将名称和对象存储到容器中 map.put((String)key,coffee); } } catch (Exception e) { e.printStackTrace(); } } public static Coffee createCoffee(String name){ return map.get(name); } } //测试 Coffee american = CoffeeFactory.createCoffee("american"); System.out.println(american.getName()); Coffee latte = CoffeeFactory.createCoffee("latte"); System.out.println(latte.getName());
优点:
- 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象
缺点:
- 当产品族中需要增加一个新的产品时,所有工厂类都需要进行修改
使用场景:
- 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等
- 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋
- 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构
如:输入法换皮肤,一整套一起换。生成不同操作系统的程序
4. 原型模式
概念:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象
结构:
抽象原型类:规定了具体原型对象必须实现的clone()方法
具体原型类:实现抽象原型类的clone()方法,它是可被复制的对象
访问类:使用具体原型类中的clone()方法来复制新的对象
实现:
分为浅克隆和深克隆
浅克隆:创建一个新对象,新对象的属性和原来完全相同,非基本类型属性,仍指向原有属性索指向的对象的内存地址
深克隆:创建一个新对象,属性中引用其他对象也会被克隆,但是不再指向原有对象地址
对深克隆的分析:
stu和stu1对象为同一个对象,就会产生将stu1对象中的name改为“李四”,两个Citation对象都显示李四。浅克隆会对具体原型类(Citation)中的引用类型的属性进行引用复制,所以会出现两个都是李四的情况(错误版本的测试),需要用深克隆,即为使用对象流
/** * 原型模式 * 浅克隆 * Java中的Object类中提供了 clone() 方法来实现浅克隆。 * Cloneable 接口是上面的类图中的抽象原型类,而实现了Cloneable接口的子实现类就是具体的原型类 * * 使用场景: * 对象的创建非常复杂,可以使用原型模式快捷的创建对象 * 性能和安全要求比较高 */ public class Realizetype implements Cloneable{ private String name; private String age; public Realizetype(){ System.out.println("具体的原型对象创建完成!"); } @Override public Realizetype clone() throws CloneNotSupportedException{ System.out.println("具体原型复制成功!"); return (Realizetype) super.clone(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } } //测试 Realizetype r1 = new Realizetype(); r1.setName("测试"); r1.setAge("19"); Realizetype r2 = r1.clone(); System.out.println("对象r1和r2是同一个对象?" + (r1 == r2));
/** * 原型模式 * 深克隆 */ public class Citation implements Cloneable, Serializable { private Student stu; public Student getStu() { return stu; } public void setStu(Student stu) { this.stu = stu; } public void show() { System.out.println(stu.getName() + "同学:在2020学年第一学期中表现优秀,被评为三好学生。特发此状!"); } @Override public Citation clone() throws CloneNotSupportedException { return (Citation) super.clone(); } } public class Student implements Serializable { private String name; private String address; public Student(String name, String address) { this.name = name; this.address = address; } public Student() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } } /** * 深克隆 错误版测试 */ 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(); /** * 深克隆 正确版测试 */ Citation c1 = new Citation(); Student stu = new Student("张三", "西安"); c1.setStu(stu); //创建对象输出流对象 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Administrator\\Desktop\\b.txt")); oos.writeObject(c1); oos.close(); //创建对象输入流对象 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Administrator\\Desktop\\b.txt")); Citation c2 = (Citation) ois.readObject(); Student stu1 = c2.getStu(); stu1.setName("李四"); System.out.println("stu和stu1是同一个对象?" + (stu == stu1)); c1.show(); c2.show();
使用场景:
- 对象的创建非常复杂,可以使用原型模式快捷的创建对象
- 性能和安全要求比较高
5. 建造者模式
概念:将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示
分离部件的构造(Builder)和装配(Director)
适用于:某个对象的构建过程复杂的情况,需要产品有较多的共同点
结构:
抽象建造者类:这个接口规定要实现复杂对象的那些部分的创建,并不涉及具体的部件对象的创建(Builer)
具体建构者类:实现Builder接口,完成复杂产品的各个部件的具体创建方法。在构造过程完成后,提供产品的实例(MobikeBuilder OfoBuilder)
产品类:要创建的复杂对象(bike)
指挥者类:调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建(Director)
/** * 产品对象 */ public class Bike { private String frame;//车架 private String seat;//车座 public String getFrame() { return frame; } public void setFrame(String frame) { this.frame = frame; } public String getSeat() { return seat; } public void setSeat(String seat) { this.seat = seat; } } /** * 建造者模式 构造 * 抽象 builder 类 */ public abstract class Builder { protected Bike bike = new Bike(); public abstract void buildFrame(); public abstract void buildSeat(); public abstract Bike createBike(); //抽象建造者和指挥者类结合,可以去掉Director public Bike construct(){ this.buildFrame(); this.buildSeat(); return this.createBike(); } } /** * 建造者模式 装配 * 指挥者类 */ public class Director { private Builder builder; public Director(Builder builder){ this.builder = builder; } public Bike construct(){ builder.buildFrame(); builder.buildSeat(); return builder.createBike(); } } //摩拜单车Builder类 public class MobikeBuilder extends Builder{ @Override public void buildFrame() { bike.setFrame("铝合金车架"); } @Override public void buildSeat() { bike.setSeat("真皮车座"); } @Override public Bike createBike() { return bike; } } //ofo单车Builder类 public class OfoBuilder extends Builder{ @Override public void buildFrame() { bike.setFrame("碳纤维车架"); } @Override public void buildSeat() { bike.setSeat("橡胶车座"); } @Override public Bike createBike() { return bike; } } /** * 抽象建造者和指挥者类分开 */ public static void main(String[] args) { showBike(new OfoBuilder()); System.out.println("-------------"); showBike(new MobikeBuilder()); } private static void showBike(Builder builder){ Director director = new Director(builder); Bike bike = director.construct(); System.out.println(bike.getFrame()); System.out.println(bike.getSeat()); } /** * 抽象建造者和指挥者类结合 */ public static void main(String[] args) { showBike(new OfoBuilder()); System.out.println("-------------"); showBike(new MobikeBuilder()); } private static void showBike(Builder builder){ Bike bike = builder.construct(); System.out.println(bike.getFrame()); System.out.println(bike.getSeat()); }
模式扩展
/** * 建造者模式的扩展 * 类构造器传入的参数多,可读性差,就可以利用构造者模式重构 */ 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 + '\'' + '}'; } } //测试 Phone phone = new Phone.Builder() .cpu("intel") .mainboard("华硕") .memory("金士顿") .screen("三星") .build(); System.out.println(phone);
优点:
- 封装性好。可以有效的封装变化,一般产品类和建造者类比较稳定
- 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象
- 可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也要更方便使用程序来控制创建过程。
- 进行扩展。如果有新需求,通过实现一个新的建造者类就可以完成,符合开闭原则
缺点:
- 需要具有较多的共同点,才适用
使用场景:
- 创建的对象较复杂,由多个部件构成,各部件面临着复杂的变化,但构件间的建造顺序是稳定的
- 创建复杂对象的算法独立于该对象的组成部分以及它们的装配方式,即产品的构建过程和最终的表示是独立的
6. 创建者模式对比
6.1 工厂方法模式VS建造者模式
工厂方法模式注重的是整体对象的创建方式;而建造者模式注重的是部件构建的过程,意在通过一步一步地精确构造创建出一个复杂的对象。
我们举个简单例子来说明两者的差异,如要制造一个超人,如果使用工厂方法模式,直接产生出来的就是一个力大无穷、能够飞翔、内裤外穿的超人;而如果使用建造者模式,则需要组装手、头、脚、躯干等部分,然后再把内裤外穿,于是一个超人就诞生了。
6.2 抽象工厂模式VS建造者模式
抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式则是不需要关心构建过程,只关心什么产品由什么工厂生产即可。
建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。
如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车。