设计模式 (1): 5 种创建型模式 (结合代码详解)
这个是我学习 23 种设计模式的第 1 篇,代码和文章都是自己的理解和编写的~
目录
引言:没模式,怎么生产对象?
- 全局变量:全局共享的对象、全局访问点
- New 对象:构造私有的对象
- 属性装配:setter方法
- 复制对象:增加具体对象的数量
直接生产的问题
对于 全局变量:
- 拓展性低
- 不能延时初始化
- 没有封装:
- 对外暴露自己,可能被 误改 或 恶意攻击
- 作用域不受限,随意访问(单例模式需要 先引用单例类)
对于 New 对象:
- 没有可拓展性,即便封装成函数,也只是提高复用性
- 复杂的属性配置,复杂对象很麻烦
- 随着具体种类的增加,种类变得难以维护
对于属性装配:
- setter方法逐个注入太臃肿,难以维护
对于复制对象:
- 通过构造器、setter/getter 复制对象的效率低,属性多 还容易出错
设计模式,解决痛点
- 单例模式 解决 全局变量 的问题
- 工厂模式/抽象工厂 解决 New 对象(族) 的问题
- 建造者模式 解决 属性装配 的问题
- 原型模式 解决 复制对象 的问题
1 单例模式
需考虑的问题:
- 是否线程安全
- 是否延迟创建
- 有无破坏单例的方法
饿汉单例
public class _01Eager {
// 不延迟单例
// 类初始化时创建单例
private static final _01Eager instance = new _01Eager();
private _01Eager(){}
public static _01Eager getInstance(){
return instance;
}
}
懒汉单例
public class _02Lazyer {
// 方法延迟单例
// 相比于 饿汉式,把 instance 的创建
// 从 类初始化时 移动到 方法调用时
private static _02Lazyer instance;
private _02Lazyer(){}
public static synchronized _02Lazyer getInstance(){
if(instance == null)
instance = new _02Lazyer();
return instance;
}
}
双重检验单例
public class _03DoubleCheck {
// 方法延迟单例
// 相比 懒汉式,缩小锁粒度
private static _03DoubleCheck instance;
private _03DoubleCheck(){}
public static _03DoubleCheck getInstance(){
if(instance == null){
synchronized (_03DoubleCheck.class){
if(instance == null) {
instance = new _03DoubleCheck();
}
}
}
return instance;
}
}
静态内部类单例
public class _04Static implements Serializable {
private static class Static {
private final static _04Static instance = new _04Static();
}
private _04Static(){}
public static _04Static getInstance(){
return Static.instance;
}
}
破坏单例 (反射、反序列化)
(导入序列化工具依赖:org.apache.commons.commons-lang3
)
public class StaticAttack {
StaticAttack(){}
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 测试 反射和反序列化 破坏 静态内部类
staticAttack();
}
public static void staticAttack() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 单例对象
_04Static obj = _04Static.getInstance();
isSingleton(obj, _04Static.getInstance()); // true
// 测试是否可以破坏单例
_04Static newObj1, newObj2;
// 反射创建 新对象 可以破坏单例
Constructor<_04Static> constructor = _04Static.class.getDeclaredConstructor();
constructor.setAccessible(true);
newObj1 = constructor.newInstance();
isSingleton(obj, newObj1); // false
// 反序列化创建 新对象 可以破坏单例
byte[] serialize = SerializationUtils.serialize(obj);
newObj2 = SerializationUtils.deserialize(serialize);
isSingleton(obj, newObj2); // false
}
public static void isSingleton(Object obj1, Object obj2){
System.out.println("是否是单例? " + (obj1 == obj2));
}
}
枚举类单例
// 枚举类可以避免 反射 和 反序列化 破坏单例
public enum _05Enum {
SINGLETON;
// 单例的方法
public String doSomething(){
return "处理结束";
}
}
// 调用单例的方法
_05Enum.SINGLETON.doSomething();
2 工厂模式
简单工厂模式
- 消费者:依赖 商店
- 商店:依赖 工厂,返回 具体产品
- 工厂:if - else 地创建 具体产品
- 产品:产品种类 通过 实现 很多抽象产品(接口)
public class sf {
// 抽象产品类 Vehicle
interface Vehicle {}
// Vehicle 的 具体产品
static class Car implements Vehicle {
public String toString() {
return "Car";
}
}
static class Bike implements Vehicle {
public String toString() {
return "Bike";
}
}
// 所有具体产品 都共用 抽象产品的工厂
static class VehicleFactory {
Vehicle createVehicle(String type){
if(type == null)
return null;
Vehicle vehicle = null;
if(type.equalsIgnoreCase("Car"))
vehicle = new Car();
else if(type.equalsIgnoreCase("Bike"))
vehicle = new Bike();
return vehicle;
}
}
}
工厂方法模式
public class fm {
// 抽象产品类 Vehicle
interface Vehicle {}
// Vehicle 的 具体产品
static class Car implements Vehicle {
public String toString() {
return "Car";
}
}
static class Bike implements Vehicle {
public String toString() {
return "Bike";
}
}
// Vehicle 的 抽象工厂
static abstract class VehicleFactory {
abstract Vehicle createVehicle();
}
// *继承* 抽象工厂
// 一个具体产品 对应 一个具体工厂
static class CarFactory extends VehicleFactory {
Vehicle createVehicle(){
return new Car();
}
}
static class BikeFactory extends VehicleFactory {
Vehicle createVehicle() {
return new Bike();
}
}
}
3 抽象工厂模式
代码实现
public class af {
// 抽象工厂用于产品族
// 产品族 和 各自的抽象工厂
interface Vehicle {}
interface Accessory {}
// interface Engine {} 产品族拓展的例子
interface VehicleFactory {
Vehicle createVehicle();
}
interface AccessoryFactory {
Accessory createAccessory();
}
// interface EngineFactory { Engine createEngine();}
// Vehicle 的 具体产品
static class Car implements Vehicle {
public String toString() {
return "Car";
}
}
static class Bike implements Vehicle {
public String toString() {
return "Bike";
}
}
// Accessory 的具体产品
static class CarAccessory implements Accessory {
public String toString() {
return "CarAccessory";
}
}
static class BikeAccessory implements Accessory {
public String toString() {
return "BikeAccessory";
}
}
// 每 *实现* 一个接口,产品族就拓展一类产品
// 产品族工厂 实现了 多个接口,实现产品族的可拓展性
static class CarFactory implements
VehicleFactory, AccessoryFactory //,EngineFactory
{
public Vehicle createVehicle() {
return new Car();
}
public Accessory createAccessory() {
return new CarAccessory();
}
}
static class BikeFactory implements
VehicleFactory, AccessoryFactory //,EngineFactory
{
public Vehicle createVehicle() {
return new Bike();
}
public Accessory createAccessory() {
return new BikeAccessory();
}
}
}
对比三种工厂模式
| 简单工厂模式:
- A 产品:A1 - A2
简单工厂
/ if-else \
A1 A2
| 工厂方法模式:
- A 产品:A1 - A2
A 抽象工厂(抽象类)
- 生产 A
/ 继承 \
A1 具体工厂 A2 具体工厂
- 生产 A1 - 生产 A2
| 抽象工厂模式:
- (A, B, ...) 产品* 族 *:A1 - A2 | B1 - B2 | C ..
A 抽象工厂(接口) B 抽象工厂(接口) C 抽象工厂
- 生产 A (接口) - 生产 B (接口)
\ 多实现 /
(A1, B1) 具体产品族工厂
- 生产 A1 (实现接口)
- 生产 B1 (实现接口)
可以看到,抽象工厂可以实现最复杂的功能,但是在简单情形下增加了复杂性
如何选择工厂方法和抽象工厂?
在实际应用中,需要根据具体的需求和场景选择合适的模式:
- 如果产品种类较少,可以使用工厂方法模式
- 如果产品种类较多,形成了产品族(并且 产品族 有可能 增删 产品),可以使用抽象工厂模式
4 建造者模式
可定制的建造者模式
public class Build2 {
@Data
@ToString
public static class Car {
private String name;
private String accessory;
private String engine;
}
public static class CarBuilder {
private Car car = new Car();
public CarBuilder appendName(String name) {
car.setName(name);
return this;
}
public CarBuilder appendAccessory(String acc) {
car.setAccessory(acc);
return this;
}
public CarBuilder appendEngine(String eng) {
car.setEngine(eng);
return this;
}
public Car build() {
Car Car = car;
// 构建完成之后初始化 car
car = new Car();
return Car;
}
}
}
不可定制的建造者 "工厂" 模式
public class Build {
@Data
@ToString
public static class Car {
private String name;
private String accessory;
private String engine;
}
// 私有化建造者
private static class CarBuilder {
private Car car = new Car();
public CarBuilder appendName(String name) {
car.setName(name);
return this;
}
public CarBuilder appendAccessory(String acc) {
car.setAccessory(acc);
return this;
}
public CarBuilder appendEngine(String eng) {
car.setEngine(eng);
return this;
}
public Car build() {
Car Car = car;
// 构建完成之后初始化 car
car = new Car();
return Car;
}
}
// 对外提供 工厂,封装 建造过程
public static class CarFactory {
private final CarBuilder cb = new CarBuilder();
public Car getBWMCar(){
return cb.appendName("BWM666")
.appendAccessory("尊贵奢华车身")
.appendEngine("行星发动机")
.build();
}
public Car getTeslaCar(){
return cb.appendName("Tesla888")
.appendAccessory("自动驾驶模块")
.appendEngine("飞船发动机")
.build();
}
}
}
对比抽象工厂模式
看起来,抽象工厂和建造者都创建了很多内容
但是从多相关对象和单复杂对象的角度就可以发现他们的不同:
- 抽象工厂模式可以创建一系列的产品族,而建造者模式可以定义一系列具体成员的组装
因此:
- 抽象工厂模式更适合创建 一组相关对象,往往是多个不同类对象
- 建造者模式更适合创建 单个复杂对象,往往是单个复杂对象或者同类对象的集合(list、map)
5 原型模式
避免使用 参数构造器、set方法 复制对象,提高 复制 对象的性能
浅拷贝原型模式
public class Prototype implements Cloneable {
@Override
}