本章Java设计模式的创建型模式的介绍,是通过学习视频记录的笔记,欢迎留言指出错误点
创建型模式(五种):单例模式、工厂方法模式、抽象工厂模式、原型模式、建造者模式
概念:涉及到一个单一的类(这个类只能创建一个对象),创建的是自己的对象,同时只能单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
主要以下角色:
单例类:只能创建一个实例的类
访问类:使用单例类(测试类的使用单例类)
分类:
/**
* 单例模式
* 饿汉式:静态成员变量
*/
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;
}
}
/**
* 单例模式
* 懒汉式:线程不安全
*
* 线程不安全分析:
* 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 效果时,才会使用静态内部类如果涉及到反序列化创建对象时,可以尝试使用第 枚举方式如果有其他特殊的需求,可以考虑使用双检锁方式
分类:
结构:
抽象产品:定义产品规范,描述产品的主要特性和功能(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;
}
}
概念:定义一个用户创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到工厂的子类
结构:
抽象工厂:提供了创建产品的接口,调用者通过访问具体工厂的工厂方法来创建产品(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());
优点:
缺点:
概念:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构
结构:
(工厂方法,为了跟抽象工厂对比)
抽象工厂:提供了创建产品的接口,调用者通过访问具体工厂的工厂方法来创建产品(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());
优点:
缺点:
使用场景:
如:输入法换皮肤,一整套一起换。生成不同操作系统的程序
概念:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象
结构:
抽象原型类:规定了具体原型对象必须实现的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();
使用场景:
概念:将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示
分离部件的构造(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);
优点:
缺点:
使用场景:
工厂方法模式注重的是整体对象的创建方式;而建造者模式注重的是部件构建的过程,意在通过一步一步地精确构造创建出一个复杂的对象。
我们举个简单例子来说明两者的差异,如要制造一个超人,如果使用工厂方法模式,直接产生出来的就是一个力大无穷、能够飞翔、内裤外穿的超人;而如果使用建造者模式,则需要组装手、头、脚、躯干等部分,然后再把内裤外穿,于是一个超人就诞生了。
抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式则是不需要关心构建过程,只关心什么产品由什么工厂生产即可。
建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。
如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。