创建型模式:提供创建对象的机制,提升已有代码的灵活性和 可复⽤性。
PS:博客根据it老齐大话设计模式课程课件进行整理,IT老齐 视频学习网站: https://www.itlaoqi.com
包含的设计模式:
⼯⼚⽅法模式、抽象⼯⼚模式、单例模式、建造者模式、原型模式
工厂模式
根据需求提供创建对象的最佳方式;通过一个统一的接口创建所需对象,不对外暴露创建细节;
代码背景
根据用户需求选择对应电脑
电脑接口类
public interface Computer {
public String describe(); }
外星人电脑类
public class Alienware implements Computer{ @Override public String describe() { return "外星⼈ALIENWARE m15 R7 15.6英⼨⾼端游戏本 12代i7 32G RTX3060 QHD 240Hz ⾼刷屏 轻薄笔记本电脑2765QB"; } }
办公轻薄本类
public class Macbook implements Computer{ @Override public String describe() { return "Apple MacBook Pro 13.3 ⼋核M1芯⽚ 8G 256G SSD 深空灰 笔记本电脑 轻薄本 MYD82CH/A"; } }
注意:如果我们让客户自己来选择电脑,把控制权交易客户是不对的,因为这样就破坏“迪⽶特法则”,所以选择权交还给电脑店这⼀⽅。
工厂类
帮助顾客进⾏决策,这样控制权掌握
public class ShopFactory { public Computer suggest(String purpose){ Computer computer = null; if(purpose.equals("⽇常办公")){ return new Macbook(); }else if(purpose.equals("3A游戏")){ return new Alienware(); } return computer; } }
顾客操作类
public class Customer { public static void main(String[] args) { ShopAssistant shopAssistant = new ShopAssistant(); Computer c = shopAssistant.suggest("3A游戏"); System.out.println(c.describe()); } }
结论:使用工厂模式来创建所需对象,可以很好的降低调用者和程序之间的耦合,使得调用者在不需要知道过多信息的情况下获取到符合需求的操作对象;
抽象工厂模式
顾名思义即为⼯⼚的⼯⼚,通过构建顶层的抽象⼯⼚和抽象的产品,屏蔽系列产品的构建过程
代码背景
公司早期接⼊七⽜云OSS(对象存储服务)上传图⽚与视频,后因业务调整,公司要求额外⽀持阿⾥ 云、腾讯云等其他云服务商,并且可以对客户提供外界访问,现需要在不破坏原有代码逻辑情况下,实现对任意三⽅云⼚商的接⼊。
图片接口类
public interface OssImage { public String getThumb() ; }
视频接口类
public interface OssVideo {public String get1080P(); }
抽象工厂类
import OssImage; import OssVideo; public interface AbstractOssFactory { public OssImage uploadImage(byte[] bytes); public OssVideo uploadVideo(byte[] bytes); }
七牛云实现
工厂实现类
import AbstractOssFactory; import OssImage; import OssVideo; import QiniuOssImage; import QiniuOssVideo; public class QiniuOssFactory implements AbstractOssFactory { @Override public OssImage uploadImage(byte[] bytes) { return new QiniuOssImage(bytes,"IT⽼⻬"); } @Override public OssVideo uploadVideo(byte[] bytes) { return new QiniuOssVideo(bytes,"IT⽼⻬"); } }
七牛云图片实现类
import OssImage; public class QiniuOssImage implements OssImage { private byte[] bytes; public QiniuOssImage(byte[] bytes,String watermark){ this.bytes = bytes; System.out.println("[七⽜云]图⽚已上传⾄七⽜云OSS,URL:http://oss.qiniu.com/xxxxxxx.jpg"); } @Override public String getThumb() { return "http://oss.qiniu.com/xxxxxxx_thumb.jpg"; } }
七牛云视频实现类
import OssVideo; public class QiniuOssVideo implements OssVideo { private byte[] bytes; public QiniuOssVideo(byte[] bytes, String watermark) { this.bytes = bytes; System.out.println("[七⽜云]视频已上传⾄阿⾥云OSS,URL:http://oss.qiniu.com/xxx.mp4"); } @Override public String get1080P() { return "http://oss.qiniu.com/xxx_1080p_3500.mp4"; } }
阿里云实现
阿里云工厂
import AbstractOssFactory; import OssImage; import OssVideo; import AliyunOssImage; import AliyunOssVideo; public class AliyunOssFactory implements AbstractOssFactory { @Override public OssImage uploadImage(byte[] bytes) { return new AliyunOssImage(bytes,"IT⽼⻬",true); } @Override public OssVideo uploadVideo(byte[] bytes) { return new AliyunOssVideo(bytes,"IT⽼⻬"); } }
阿里云图片实现类
import OssImage; public class AliyunOssImage implements OssImage { private byte[] bytes; public AliyunOssImage(byte[] bytes, String watermark,boolean transparent){ this.bytes = bytes; System.out.println("[阿⾥云]图⽚已上传⾄阿⾥云OSS,URL:http://oss.aliyun.com/xxxxxxx.jpg"); } @Override public String getThumb() { return "http://oss.aliyun.com/xxxxxxx_thumb.jpg"; } }
阿里云视频实现类
import OssVideo; public class AliyunOssVideo implements OssVideo { private byte[] bytes; public AliyunOssVideo(byte[] bytes, String watermark) { this.bytes = bytes; System.out.println("[阿⾥云]视频已上传⾄阿⾥云OSS,URL:http://oss.aliyun.com/xxx.mp4"); } @Override public String get1080P() { return "http://oss.aliyun.com/xxx_1080p.mp4"; } }
客户调用类
import AliyunOssFactory; import AbstractOssFactory; import OssImage; import OssVideo; import QiniuOssFactory; public class Client { public static void main(String[] args) { // AbstractOssFactory factory = new AliyunOssFactory(); AbstractOssFactory factory = new QiniuOssFactory();
OssImage ossImage = factory.uploadImage(new byte[1024]); OssVideo ossVideo = factory.uploadVideo(new byte[1024]); System.out.println(ossImage.getThumb()); System.out.println(ossImage.getWatermark()); System.out.println(ossImage.getEnhance()); System.out.println(ossVideo.get720P()); System.out.println(ossVideo.get1080P()); } }
结论:抽象工厂模式,在工厂模式的基础上进行一层抽象,在工厂之上建立一个总工厂,用户可以通过总工厂来创建指定类型的子工厂来实现自己的操作;抽象工厂模式和与⼯⼚模式⼀样,都是⽤来解决接⼝选择的问题;
单例模式
⼀个类只允许创建⼀个对象(或者实例),这种设计模式就叫作单例设计模式,简称单例模式。
单例模式的创建方式
饿汉式,懒汉式,静态内部类,枚举
饿汉式
类加载时直接实例化单例对象
public class IdGenerator { private AtomicLong id = new AtomicLong(0); private static final IdGenerator instance = new IdGenerator(); private IdGenerator() {} public static IdGenerator getInstance() { return instance; } public long getId() { return id.incrementAndGet(); } }
缺点:不⽀持延迟加载,如果实例占⽤资源多(⽐如占⽤内存多)或初始化 耗时⻓(⽐如需要加载各种配置⽂件),提前初始化实例是⼀种浪费资源的⾏为
懒汉式
第⼀次调⽤时实例化对象(延迟加载)
public class IdGenerator { private AtomicLong id = new AtomicLong(0); private static IdGenerator instance; private IdGenerator() {} public static synchronized IdGenerator getInstance() { if (instance == null) { instance = new IdGenerator(); } return instance; } public long getId() { return id.incrementAndGet(); } }
缺点:synchronized会使得这个函数的并发度很低,如果频繁地⽤到,那频繁加锁、释放锁会导致性能瓶颈
懒汉式-双重校验
加锁之前先进行一次判断,满足条件直接返回,从而减少频繁加锁问题
public class IdGenerator { private AtomicLong id = new AtomicLong(0); private static IdGenerator instance; private IdGenerator() {} public static IdGenerator getInstance() { if (instance == null) { synchronized(IdGenerator.class) { // 此处为类级别的锁 if (instance == null) { instance = new IdGenerator(); } } } return instance; } public long getId() { return id.incrementAndGet(); } }
缺点:在低版本的java中,存在指令重排序的问题,从而导致 IdGenerator 对象被 new 出来
静态内部类
通过静态内部类的方式创建对象,点类似饿汉式,但⼜能做到了延迟加载
public class IdGenerator { private AtomicLong id = new AtomicLong(0); private IdGenerator() {} private static class SingletonHolder{ private static final IdGenerator instance = new IdGenerator(); } public static IdGenerator getInstance() { return SingletonHolder.instance; } public long getId() { return id.incrementAndGet(); } }
分析:内部类在外部类被加载时是不会创建的,只有调用 getInstance() ⽅法时,内部类才会被加载并且会创建 instance,instance 的唯⼀性、创建过程的线程安全性,都由JVM来保证,这种方式既保证了线程安全,⼜能做到延迟加载
枚举
基于枚举类型的单例实现。这种实现⽅式通过 Java枚举类型本身的特性,是最简单实现单例的⽅式,保证了实例创建的线程安全性和实例的唯⼀性
public enum IdGenerator { INSTANCE; private AtomicLong id = new AtomicLong(0); public long getId() { return id.incrementAndGet(); } public static void main(String[] args) { IdGenerator instance = IdGenerator.INSTANCE; System.out.println(instance.getId()); } }
注意:枚举方式创建的对象能做到真正的单例,其他几种方式创建的单例对象会被反射和序列化的方式破坏单例
反射破坏单例
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class ReflectAttack { public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { Class<?> classType = IdGenerator.class; Constructor<?> c = classType.getDeclaredConstructor(null); c.setAccessible(true); IdGenerator e1 = (IdGenerator)c.newInstance(); IdGenerator e2 = IdGenerator.getInstance(); System.out.println(e1==e2); } }
注意:输出结果为false,说明e1和e2不是同一个对象,单例被破坏。
序列化破坏单例
这里IdGenerator类要实现一下Serializable类,不然无法序列化
public static void main(String[] args) throws IOException, ClassNotFoundException
{ IdGenerator e1= null; IdGenerator e2 = IdGenerator.getInstance(); FileOutputStream fos = new FileOutputStream("IdGenerator.obj"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(e2); oos.flush(); oos.close(); FileInputStream fis = new FileInputStream("IdGenerator.obj"); ObjectInputStream ois = new ObjectInputStream(fis); e1 = (IdGenerator)ois.readObject(); System.out.println(e2==e1);
}
注意:输出结果为false,说明e1和e2不是同一个对象,单例被破坏。
建造者模式
摆脱超⻓构造⽅法参数的束缚的同时也保护了”不 可变对象“的密闭性
格式
⽬标类的构造⽅法要求传⼊Builder对象
Builder建造者类位于⽬标类内部且⽤static描述
Builder建造者对象提供内置属性与各种set⽅法,注意set⽅法返回 Builder对象本身
Builder建造者类提供build()⽅法实现⽬标类对象的创建
Builder
public class ⽬标类(){ //⽬标类的构造⽅法要求传⼊Builder对象 public ⽬标类(Builder builder){ } public 返回值 业务⽅法(参数列表){ //doSth } //Builder建造者类位于⽬标类内部且⽤static描述 public static class Builder(){ //Builder建造者对象提供内置属性与各种set⽅法,注意set⽅法返回Builder对象本身 private String xxx ; public Builder setXxx(String xxx) { this.xxx = xxx; return this; } //Builder建造者类提供build()⽅法实现⽬标类对象的创建 public ⽬标类 build() { //业务校验 return new ⽬标类(this); } } }
注意:Builder类中的属性最好有初始值,这样在调用时,可以减少属性未赋值的错误,或者在调用build()时加以判断说明
原型模式
⽤⼀个已经创建的实例作为原型,通过复制该原型对象来创建⼀个和原型相同或相似的新对象;原型实例指定了要创建的对象的种类。⽤这种⽅式创建对象⾮常⾼效,根本⽆须知道对象创建的细节。
优点
Java ⾃带的原型模式基于内存⼆进制流的复制,在性能上⽐直接 new ⼀个对象更加优良
可以使⽤深克隆⽅式保存对象的状态,使⽤原型模式将对象复制⼀份,并将其状态保存起来,简化了 创建对象的过程,以便在需要的时候使⽤(例如恢复到历史某⼀状态),可辅助实现撤销操作。
缺点
需要为每⼀个类都配置⼀个 clone ⽅法
clone ⽅法位于类的内部,当对已有类进⾏改造的时候,需要修改代码,违背了开闭原则。
当实现深克隆时,需要编写较为复杂的代码,⽽且当对象之间存在多重嵌套引⽤时,为了实现深克 隆,每⼀层对象对应的类都必须⽀持深克隆,实现起来会⽐较麻烦。因此,深克隆、浅克隆需要运⽤得当。
应用场景
对象之间相同或相似,即只是个别的⼏个属性不同的时候。
创建对象成本较⼤,例如初始化时间⻓,占⽤CPU太多,或者占⽤⽹络资源太多等,需要优化资源。
创建⼀个对象需要繁琐的数据准备或访问权限等,需要提⾼性能或者提⾼安全性。
系统中⼤量使⽤该类对象,且各个调⽤者都需要给它的属性重新赋值。
浅克隆与深克隆
浅克隆
创建⼀个新对象,新对象的属性和原来对象完全相同,对于⾮基本类型属性,仍指向原有属性 所指向的对象的内存地址
public class Employee implements Cloneable{ private String name; private Car car; public String getName() { return name; } public void setName(String name) { this.name = name; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } //重写Clone⽅法 @Override protected Object clone() throws CloneNotSupportedException { System.out.println("正在复制Employee对象"); return super.clone(); } }
注意:Employee类中有一个Car这个非基本类型的属性,当克隆Employee类时,克隆类中的Car属性和原始类中的Car属性指向的内存地址是同一块,这样会导致在原始类中修改Car属性中的数据,克隆类中Car属性中的值也会发生变化
深克隆
深克隆:创建⼀个新对象,属性中引⽤的其他对象也会被克隆,不再指向原有对象地址,一般基于Json实现对象深度Clone,不再需要实现Clonable接⼝与clone()⽅法
import com.google.gson.Gson; public class Employee{ private String name; private Car car; public String getName() { return name; } public void setName(String name) { this.name = name; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } //基于JSON实现深度序列化 public Employee deepClone(){ Gson gson = new Gson(); String json = gson.toJson(this); System.out.println(json); Employee cloneObject = gson.fromJson(json, Employee.class); return cloneObject; } }标签:return,IdGenerator,创建,bytes,模式,new,设计模式,public,String From: https://www.cnblogs.com/caixiaozi/p/16927910.html