一、为什么需要创建型模式?
想象你正在搭积木。每次需要新零件时,要么现场切割木块(直接new对象),要么从标准化模具里取现成的(使用设计模式)。Pascal这种结构化语言虽然不像面向对象语言那样天生支持设计模式,但通过巧妙组织代码,同样能获得类似的好处。
创建型模式就像你的零件工厂,帮你解决:
- 对象创建过程复杂(比如需要多步初始化)
- 对象创建需要隔离(避免到处写new)
- 系统需要灵活切换对象类型
举个真实场景:我们电商系统要处理三种支付方式(支付宝/微信/银联),如果每次都在业务代码里写if...else来创建支付处理器,不仅难看,以后新增支付方式还得改几十个地方。
二、简单工厂模式实战
(示例技术栈:Free Pascal/Lazarus)
// 支付处理器基类
type
TPaymentHandler = class
public
procedure ProcessPayment(amount: Double); virtual; abstract;
end;
// 具体支付实现
type
TAlipayHandler = class(TPaymentHandler)
public
procedure ProcessPayment(amount: Double); override;
begin
WriteLn('支付宝处理支付: ', amount:0:2);
end;
end;
// 支付工厂
type
TPaymentFactory = class
public
class function CreateHandler(paymentType: string): TPaymentHandler;
end;
class function TPaymentFactory.CreateHandler(paymentType: string): TPaymentHandler;
begin
if paymentType = 'alipay' then
Result := TAlipayHandler.Create
else if paymentType = 'wechat' then
Result := TWechatHandler.Create
else
raise Exception.Create('不支持的支付类型');
end;
// 使用示例
var
handler: TPaymentHandler;
begin
handler := TPaymentFactory.CreateHandler('alipay');
try
handler.ProcessPayment(99.99);
finally
handler.Free;
end;
end.
这个方案把创建逻辑集中到了工厂类,业务代码只需要知道"我需要一个支付处理器",不用关心具体类型。但缺点也很明显——每新增支付方式都要修改工厂类,违反开闭原则。
三、工厂方法模式进阶
为了解决简单工厂的问题,我们升级成工厂方法模式:
// 抽象工厂
type
IPaymentHandlerFactory = interface
function CreateHandler: TPaymentHandler;
end;
// 支付宝工厂
type
TAlipayFactory = class(TInterfacedObject, IPaymentHandlerFactory)
function CreateHandler: TPaymentHandler;
end;
function TAlipayFactory.CreateHandler: TPaymentHandler;
begin
Result := TAlipayHandler.Create;
end;
// 在配置阶段注册工厂
var
factories: TDictionary<string, IPaymentHandlerFactory>;
begin
factories := TDictionary<string, IPaymentHandlerFactory>.Create;
factories.Add('alipay', TAlipayFactory.Create);
// 可以继续添加其他支付工厂...
// 使用时
handler := factories['alipay'].CreateHandler;
handler.ProcessPayment(88.88);
end.
这个版本的进步在于:
- 新增支付类型只需新建工厂类,不用改原有代码
- 可以通过配置文件动态决定使用哪个工厂
- 符合"对扩展开放,对修改关闭"的原则
四、建造者模式处理复杂对象
当对象需要多步构造时,比如我们的订单系统要生成包含商品、优惠、物流的复杂订单:
type
// 要构建的复杂对象
TOrder = class
public
Items: TArray<string>;
Discount: Double;
ShippingMethod: string;
end;
// 建造者接口
IOrderBuilder = interface
procedure AddItems(items: TArray<string>);
procedure ApplyDiscount(discount: Double);
procedure SetShipping(method: string);
function GetResult: TOrder;
end;
// 具体建造者
TStandardOrderBuilder = class(TInterfacedObject, IOrderBuilder)
private
FOrder: TOrder;
public
constructor Create;
procedure AddItems(items: TArray<string>);
begin
FOrder.Items := items;
end;
function GetResult: TOrder;
begin
Result := FOrder;
FOrder := nil; // 转移所有权
end;
// 其他方法实现...
end;
// 使用示例
var
builder: IOrderBuilder;
order: TOrder;
begin
builder := TStandardOrderBuilder.Create;
builder.AddItems(['商品A', '商品B']);
builder.ApplyDiscount(0.9);
builder.SetShipping('顺丰');
order := builder.GetResult;
// 使用order对象...
end;
建造者模式特别适合:
- 需要分步构造的对象
- 相同构造过程需要创建不同表现的对象
- 需要隔离复杂对象的创建细节
五、单例模式的正确姿势
全局配置管理器通常只需要一个实例:
type
TConfigManager = class
private
class var FInstance: TConfigManager;
constructor CreatePrivate; // 私有化构造
public
class function GetInstance: TConfigManager;
class procedure ReleaseInstance;
// 其他业务方法...
end;
class function TConfigManager.GetInstance: TConfigManager;
begin
if not Assigned(FInstance) then
FInstance := CreatePrivate;
Result := FInstance;
end;
// 线程安全改进版
class function TConfigManager.GetInstance: TConfigManager;
var
temp: TConfigManager;
begin
if not Assigned(FInstance) then
begin
temp := CreatePrivate;
if TInterlocked.ComparePointer(Pointer(FInstance), nil, Pointer(temp)) <> nil then
temp.Free;
end;
Result := FInstance;
end;
注意事项:
- 要考虑线程安全(如上面改进版所示)
- 单例生命周期通常与程序一致
- 过度使用单例会变成"高级全局变量"
六、原型模式优化性能
当创建对象成本很高时(比如要读取大文件初始化),我们可以克隆现有对象:
type
IPrototype = interface
function Clone: IPrototype;
end;
TExpensiveObject = class(TInterfacedObject, IPrototype)
private
FData: TArray<Byte>;
public
constructor CreateFromFile(const filename: string);
function Clone: IPrototype;
end;
function TExpensiveObject.Clone: IPrototype;
var
copy: TExpensiveObject;
begin
copy := TExpensiveObject.Create;
SetLength(copy.FData, Length(FData));
Move(FData[0], copy.FData[0], Length(FData));
Result := copy;
end;
适用场景:
- 对象创建需要大量计算或IO操作
- 系统需要频繁创建相似对象
- 需要避免使用类的构造器
七、模式选择指南
最后给个快速选型参考表:
| 问题场景 | 推荐模式 | 理由 |
|---|---|---|
| 需要统一创建入口 | 简单工厂 | 集中管理创建逻辑 |
| 需要灵活扩展产品类型 | 工厂方法 | 符合开闭原则 |
| 构造过程复杂 | 建造者 | 分离构造步骤 |
| 全局唯一访问点 | 单例 | 控制实例数量 |
| 创建成本高昂的对象 | 原型 | 通过克隆避免重复初始化 |
实际项目中常常组合使用这些模式。比如用工厂方法创建建造者,再由建造者生成最终对象。
记住:设计模式不是银弹,用对场景才是关键。下次当你发现代码里new关键字满天飞,或者构造逻辑重复N遍时,就该考虑引入合适的创建型模式了。
评论