创建型模式
简单工厂
介绍:
简单工厂是一种编程习惯,也是一种建造型设计模式,提供了一种对象创建方式,无需向客户端暴露对象创建逻辑,只需要知道接口,即可创建不同的对象。
例子:
假设我们要创建不同类型的形状(如圆形、矩形等),我们可以使用简单工厂模式来实现。
Shape接口
public interface Shape {
void draw();
}
Circle类
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a Circle");
}
}
Rectangle类
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a Rectangle");
}
}
ShapeFactory类
public class ShapeFactory {
// 使用 getShape 方法获取形状类型的对象
public static Shape getShape(String shapeType) {
if (shapeType == null) {
return null;
}
if (shapeType.equalsIgnoreCase("CIRCLE")) {
return new Circle();
} else if (shapeType.equalsIgnoreCase("RECTANGLE")) {
return new Rectangle();
}
return null;
}
}
客户端代码
public class Client {
public static void main(String[] args) {
Shape shape1 = ShapeFactory.getShape("CIRCLE");
shape1.draw();
Shape shape2 = ShapeFactory.getShape("RECTANGLE");
shape2.draw();
}
}
UML图
优点
降低耦合度:将逻辑代码封装到工厂类中,客户端不需要知道创建的逻辑,只需要调用工厂方法即可,降低客户端的耦合度。
提高可扩展性:后续需要添加产品,只需要在工厂类扩展即可,不需要修改客户端代码,符合开闭原则。
缺点
一定程度上违反开闭原则:虽然简化了客户端代码,但是需要扩展的时候还是需要修改工厂类的逻辑,在一定程度上违反了开闭原则。
工厂类职责过重:随着类型的增加,导致工厂的代码逻辑越来越重。
工厂方法
介绍:
工厂方法模式是一种创建型设计模式,提供 ··············了一个创建对象的接口,让子类(具体工厂)决定创建的对象。后续如果有型对象新增时,只需要新增具体工厂类和具体产品即可,不需要修改原来的代码,符合开闭原则。
角色·······························
产品接口:定义产品的规范,所有具体产品类都必须实现该接口。
具体产品类:实现了产品接口的具体类。
创建者接口:声明工厂方法,该方法返回一个产品对象。
具体创建者类:实现了创建者接口,负责实际创建具体产品类的实例。
例子
假设我们要创建不同类型的交通工具(如汽车、自行车等),可以使用工厂方法模式来实现。
产品接口
// 产品接口
public interface Vehicle {
void drive();
}
具体产品类
// 具体产品类 - 汽车
public class Car implements Vehicle {
@Override
public void drive() {
System.out.println("Driving a Car");
}
}
// 具体产品类 - 自行车
public class Bicycle implements Vehicle {
@Override
public void drive() {
System.out.println("Riding a Bicycle");
}
}
创建者接口
// 创建者接口
public abstract class VehicleFactory {
// 工厂方法
public abstract Vehicle createVehicle();
}
具体创建者类
// 具体创建者类 - 汽车工厂
public class CarFactory extends VehicleFactory {
@Override
public Vehicle createVehicle() {
return new Car();
}
}
// 具体创建者类 - 自行车工厂
public class BicycleFactory extends VehicleFactory {
@Override
public Vehicle createVehicle() {
return new Bicycle();
}
}
客户端代码
public class Client {
public static void main(String[] args) {
// 创建汽车工厂并生产汽车
VehicleFactory carFactory = new CarFactory();
Vehicle car = carFactory.createVehicle();
car.drive();
// 创建自行车工厂并生产自行车
VehicleFactory bicycleFactory = new BicycleFactory();
Vehicle bicycle = bicycleFactory.createVehicle();
bicycle.drive();
}
}
UML
优点
开闭原则:新产品的添加不需要修改原有代码,符合开闭原则。
提高灵活性和可扩展性:客户端可以根据不同需求选择不同工厂创建对象。
职责单一:每一个类只负责创建一种产品。
缺点
类爆炸:随着系统的维护,类会越来越多,增加系统复杂性。
客户端需要了解工厂类:客户端需要知道创建的对象使用哪一个工厂创建。
抽象工厂
介绍
抽象工厂是一种创建型设计模式,提供了一个接口,用于创建一系类相关或相互依赖的对象,不需要指定具体的类。通常定义一个抽象工厂接口,由实现类决定创建的具体产品族
角色
抽象产品接口:定义每一类产品的规范,所有具体产品类都必须实现这些接口。
具体产品类:实现了抽象产品接口的具体类,通常每个具体工厂类会创建一组具体的产品。
抽象工厂接口:声明创建一系列相关产品的方法。
具体工厂类:实现了抽象工厂接口,负责实际创建具体的产品族。
例子
抽象产品接口
// 抽象产品接口 - 按钮
public interface Button {
void render();
}
// 抽象产品接口 - 复选框
public interface Checkbox {
void render();
}
具体产品类
// 具体产品类 - Windows按钮
public class WindowsButton implements Button {
@Override
public void render() {
System.out.println("Rendering a Windows button.");
}
}
// 具体产品类 - Mac按钮
public class MacButton implements Button {
@Override
public void render() {
System.out.println("Rendering a Mac button.");
}
}
// 具体产品类 - Windows复选框
public class WindowsCheckbox implements Checkbox {
@Override
public void render() {
System.out.println("Rendering a Windows checkbox.");
}
}
// 具体产品类 - Mac复选框
public class MacCheckbox implements Checkbox {
@Override
public void render() {
System.out.println("Rendering a Mac checkbox.");
}
}
抽象工厂接口
// 抽象工厂接口
public interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
具体工厂类
// 具体工厂类 - Windows工厂
public class WindowsFactory implements GUIFactory {
@Override
public Button createButton() {
return new WindowsButton();
}
@Override
public Checkbox createCheckbox() {
return new WindowsCheckbox();
}
}
// 具体工厂类 - Mac工厂
public class MacFactory implements GUIFactory {
@Override
public Button createButton() {
return new MacButton();
}
@Override
public Checkbox createCheckbox() {
return new MacCheckbox();
}
}
客户端代码
public class Client {
private Button button;
private Checkbox checkbox;
public Client(GUIFactory factory) {
button = factory.createButton();
checkbox = factory.createCheckbox();
}
public void renderUI() {
button.render();
checkbox.render();
}
public static void main(String[] args) {
// 创建Windows主题的UI
GUIFactory windowsFactory = new WindowsFactory();
Client windowsClient = new Client(windowsFactory);
windowsClient.renderUI();
// 创建Mac主题的UI
GUIFactory macFactory = new MacFactory();
Client macClient = new Client(macFactory);
macClient.renderUI();
}
}
UML
抽象工厂和工厂方法的区别
- 目的:工厂方法模式,定义一个创建单一产品对象的接口,让子类决定具体的实例化对象,抽象工厂方法模式,定义一个接口,用户创建一系类相关或相互依赖的接口,不需要指定他们具体的类。
- 关注点:工厂方法模式关注单一产品,抽象工厂模式关注一整套相关产品。
建造者模式
介绍
建造者模式是一种创建型设计模式,他允许你分步骤的创建一个复杂对象,它的目的是让一个复杂对象的构建和表示进行分离,使得不同的构建过程能够的到不同的表示。
例如我要配置一台电脑,我有好几个方案,方案一是配置为(4060显卡,32G内存,1T硬盘空间),方案二是配置为(3050显卡,16G内存,512G硬盘空间),我根据我的需要告诉装机师傅,我要按照什么方案配置,按照方案一配置一台性能好的电脑,按照方案二配置一台性能弱一点的电脑,我的电脑就是产品,方案就是具体建造者,师傅就是指挥者,这个过程实现了构建与表示分离,可以根据不同的构建方案出现不同的表示(电脑)。
角色
- 产品:一个复杂的对象
- 抽象建造者:定义了创建复杂产品个个内容的抽象方法。
- 具体建造者:实现了抽象建造者的接口,有它决定每一个内容的具体创建
- 指挥者:由他嗲用建造者的方法,决定整个构建过程。
例子
// 产品类:电脑
class Computer {
private String cpu;
private String memory;
private String hardDisk;
public void setCpu(String cpu) {
this.cpu = cpu;
}
public void setMemory(String memory) {
this.memory = memory;
}
public void setHardDisk(String hardDisk) {
this.hardDisk = hardDisk;
}
@Override
public String toString() {
return "CPU: " + cpu + ", Memory: " + memory + ", Hard Disk: " + hardDisk;
}
}
// 抽象建造者类
abstract class ComputerBuilder {
protected Computer computer;
public ComputerBuilder() {
this.computer = new Computer();
}
public abstract void setCPU(String cpu);
public abstract void setMemory(String memory);
public abstract void setHardDisk(String hardDisk);
public Computer getComputer() {
return computer;
}
}
// 具体建造者类:组装特定配置的电脑
class GamingComputerBuilder extends ComputerBuilder {
@Override
public void setCPU(String cpu) {
computer.setCpu(cpu);
}
@Override
public void setMemory(String memory) {
computer.setMemory(memory);
}
@Override
public void setHardDisk(String hardDisk) {
computer.setHardDisk(hardDisk);
}
}
// 指挥者类
class Director {
private ComputerBuilder builder;
public Director(ComputerBuilder builder) {
this.builder = builder;
}
public Computer constructComputer() {
builder.setCPU("Intel i9");
builder.setMemory("32GB");
builder.setHardDisk("1TB SSD");
return builder.getComputer();
}
}
// 主类,测试建造者模式
public class BuilderPatternExample {
public static void main(String[] args) {
// 创建具体建造者
GamingComputerBuilder gamingBuilder = new GamingComputerBuilder();
// 创建指挥者,并传入具体建造者
Director director = new Director(gamingBuilder);
// 指挥者构建电脑
Computer gamingComputer = director.constructComputer();
System.out.println(gamingComputer);
}
}
UML
单例模式
介绍
单例模式是一种创建型的设计模式,确保一个类只有一个实例化对象,并提供一个全局访问点,访问该对象。
饿汉模式
在类加载的时候初始化对象。
// 饿汉式单例模式
public class EagerSingleton {
// 在类加载时就创建好单例对象
private static final EagerSingleton INSTANCE = new EagerSingleton();
// 私有构造函数,防止外部通过 new 创建实例
private EagerSingleton() {}
// 提供一个全局访问点
public static EagerSingleton getInstance() {
return INSTANCE;
}
}
优点:实现简单,线程安全。
缺点:如果对象占用资源很多,一开始实例化又不使用,占用大量资源。
懒汉模式(非线程安全)
在类第一次调用的时候再初始化。
// 懒汉式单例模式(非线程安全)
public class LazySingleton {
private static LazySingleton INSTANCE;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new LazySingleton();
}
return INSTANCE;
}
}
懒汉式单例(线程安全,加锁)
// 懒汉式单例模式(线程安全,加锁)
public class ThreadSafeLazySingleton {
private static ThreadSafeLazySingleton INSTANCE;
private ThreadSafeLazySingleton() {}
public static synchronized ThreadSafeLazySingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new ThreadSafeLazySingleton();
}
return INSTANCE;
}
}
双重检查锁(DCL)单例
// 双重检查锁单例模式
public class DclSingleton {
// 使用 volatile 关键字保证可见性和禁止指令重排
private static volatile DclSingleton INSTANCE;
private DclSingleton() {}
public static DclSingleton getInstance() {
if (INSTANCE == null) {
synchronized (DclSingleton.class) {
if (INSTANCE == null) {
INSTANCE = new DclSingleton();
}
}
}
return INSTANCE;
}
}
了解一下volatile
什么是volatile:它是一个修饰符,用于修饰变量,它有以下两种功能。
- 可见性:被它修饰的变量,被修改时,新值可以马上别其他线程看到
- 禁止指令重排序:编译器和处理器为了提高效率,可能会将指令重新编排,导致意想不到的错误,使用了这个修饰符,可以禁止指令重新编排
为什么要只用volatile
举个例子,在执行new DclSingleton()的时候,会有以下几个步骤:
- 分配内存
- 初始化对象
- 设置默认值
- 执行构造器
- 将INSTANCE指向分配的内存地址
由于指令重排序,步骤2,3可能会被重排序,先将INSTANCE指向分配的内存地址,在初始化对象
线程A先执行将INSTANCE指向分配的内存地址,这个时候还没有初始化对象,线程B判断这个对象已经指向一个地址了,不为null,放回一个未初始化的对象,导致程序出现问题。