对象创建的重要性
在业务代码中,对象的创建是很常见的代码,但在多年的职场生涯 里,我发现有大部分人喜欢在业务代码里直接创建对象,然后对对象设置属性,这样造成的问题就是:
业务代码被一堆创建对象的代码搞的很长,且这代码压根没有任何业务的意义。
业务需求的变化,导致对象创建的逻辑需要改变,这个时候,修改代码就会侵入到去修改业务代码,且容易改漏。
一个对象在业务代理里被多次赋值,导致排查问题时容易出现你以为这个对象的某个属性的值是 a, 一直看都觉得没有问题,最后发现在某处被设置成了 b,才导致的问题。
对象创建的设计模式
其实关于对象的的创建,有很好的设计模式指导我们编码,关于对象的创建模式有以下五种:单例模式(Singleton)、工厂方法模式(FactoryMethod)、抽象工厂模式(AbstractFactory)、建造者模式(Builder)、原型模式(Prototype)
这五种创建对象的方法,都有其对应的解决场景。
工厂模式告诉我们:
一个对象创建的知识是需要封装的,业务代码中不要体现对象是如何创建,这样当要新增不同类型的对象时,就不会修改到原来的业务代码。
单例模式解决的是:
当你有全局唯一对象实例的需求时,如全局唯一的日志配置中心,统一管理日志文件句柄,控制并发写入的同步机制,应该遵循怎样的写法,才能确保你的单例才能做到真正的单例。
建造者模式解决的是:
参数爆炸:当配置项增加到 20 个时,构造函数难以维护
可选参数处理困难:部分配置可能有默认值或非必填
构建过程不可控:无法分阶段或按需创建复杂对象
参数顺序混乱:同类参数容易填错顺序(例如 width 和 height)
原型模式解决的是:
当对象的创建成本高(如需要复杂计算、耗时操作)或需要动态创建相似对象时,传统的new
构造函数方式会导致源浪浪费(例如数据库连接)。
在这五个设计模式中,最核心且最需要熟练使用的就是工厂方法,杜绝业务代码包含创建对象过程的代码,builder 模式,单例模式和原型模式也是包在了工厂方法后面的。工厂方法有三种,简单工厂,工厂方法,抽象工厂。工厂方法的本质是对对象创建的细节做封闭, 消费者只需要关心要的是什么对象,而不要把创建的对象的过程抛出去。 简单工厂,工厂方法,抽象工厂,其实就是一层抽象,二层抽象,三层抽象。 那我们是不是可以再搞个四层抽象呢? 叫抽象抽象工厂,当然可以,只不过在我们实际编码中,三层抽象,基本就可以解决问题了。
三种工厂方法的代码实现
简单工厂
简单工厂,就是只对实际要创建的对象做抽象,然后提供工厂类,根据不同的策略来创建不同类型的实例。
示例代码:
// 产品接口
interface Product {
void operation();
}
// 具体产品A
class ConcreteProductA implements Product {
@Override
public void operation() {
System.out.println("ConcreteProductA operation");
}
}
// 具体产品B
class ConcreteProductB implements Product {
@Override
public void operation() {
System.out.println("ConcreteProductB operation");
}
}
// 简单工厂
class SimpleFactory {
public static Product createProduct(String type) {
return switch (type) {
case "A" -> new ConcreteProductA();
case "B" -> new ConcreteProductB();
default -> throw new IllegalArgumentException("Invalid product type");
};
}
}
// 使用示例
public class Client {
public static void main(String[] args) {
Product productA = SimpleFactory.createProduct("A");
productA.operation(); // 输出:ConcreteProductA operation
Product productB = SimpleFactory.createProduct("B");
productB.operation(); // 输出:ConcreteProductB operation
}
}
复制代码
这是简单工厂比较标准的实现,但在实际的编码过程中,我们创建的对象大部分都不会是多态的对象,大部分是简单的实体对象的创建,VO 对象的创建,DTO 对象的创建。我给他归个类,都叫数据对象的创建。这类对象的创建可以把简单工厂再简化一下,就是由类本身提供静态的方法,提供创建方法,方法名写明白调用这个方法创建出来的对象是干嘛的。 可以避免在业务逻辑代码了,new 对象的无意义代码的产生。
举个例子:
public class User {
private String name;
private String phone;
private int age;
private String address;
// 私有构造方法,防止直接实例化
private User(String name, String phone, int age, String address) {
this.name = name;
this.phone = phone;
this.age = age;
this.address = address;
}
// 静态方法用于创建 User 对象 - 用于新增
public static User createForAdd(UserCondition condition) {
return new User(
condition.getName(), condition.getPhone(), condition.getAge(), condition.getAddress());
}
// 静态方法用于创建 User 对象 - 用于更新
public static User createForUpdate(UserCondition condition) {
return new User(
condition.getName(), condition.getPhone(), condition.getAge(), condition.getAddress());
}
// Getter 和 Setter
public String getName() {
return name;
}
public String getPhone() {
return phone;
}
public int getAge() {
return age;
}
public String getAddress() {
return address;
}
}
复制代码
我的一个编码习惯,数据对象一般不会提供 set 方法, 这个对象创建出来,属性里的值是什么,就是什么,在其间不会提供修改的方法, 但会提供一个 builder 的实现,让你重新构建一个新的对象。
工厂方法
工厂方法,其实就是把工厂再做一次抽象处理,就是会对 Factory 定义一个抽象。
示例代码:
// 产品接口
interface Product {
void operation();
}
// 具体产品A
class ConcreteProductA implements Product {
@Override
public void operation() {
System.out.println("ConcreteProductA operation");
}
}
// 具体产品B
class ConcreteProductB implements Product {
@Override
public void operation() {
System.out.println("ConcreteProductB operation");
}
}
// 抽象工厂
interface Factory {
Product createProduct();
}
// 具体工厂A
class ConcreteFactoryA implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
// 具体工厂B
class ConcreteFactoryB implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductB();
}
}
// 使用示例
public class Client {
public static void main(String[] args) {
Factory factoryA = new ConcreteFactoryA();
Product productA = factoryA.createProduct();
productA.operation(); // 输出:ConcreteProductA operation
Factory factoryB = new ConcreteFactoryB();
Product productB = factoryB.createProduct();
productB.operation(); // 输出:ConcreteProductB operation
}
}
复制代码
一般需要用到标准的工厂方法的模式的情况,一般都会在编写框架类的代码。
典型适用情况:
框架设计:定义框架标准接口,允许用户扩展具体实现
插件系统:每个插件提供自己的工厂实现
对象池管理:不同对象需要不同的创建策略
测试替身:为测试创建 Mock 对象
抽象工厂
抽象工厂,对工厂做了两次的抽象,解决需要产品族的业务模型。直接来看下他们的结构
三重抽象机制
产品等级结构:同一产品类型的继承体系(如 Button 系:WinButton/MacButton)
产品族维度:同一风格下的全套产品(如整个 macOS 风格的组件)
工厂抽象层:对产品族创建的抽象(GUIFactory
接口)
模式对比
总结:
大部分情况下,遇到最多的还是数据对象的创建,而这种对象的创建代码冗长且没有意义,放在业务代码里,直接导致的问题就是代码的臃肿,耦合性大,对象创建的代码复用性低,用简单工厂方法来创建数据对象的代码来替代在业务代码里 new 对象,是写好代码的第一步,也会最容易去实践的一步。同时,我们还需要学会,builder 模式,单例模式,工厂方法,抽象工厂方法,当遇到更为复杂的创建对象的过程时,可以帮助我们写出更容易扩展,更容易维护的代码。
评论