PLC结构化文本设计模式——工厂方法模式(Factory Method Pattern)
PLC Structured Text Design Patterns
PLC结构化文本设计模式——工厂方法模式(Factory Method Pattern)
介绍
本章先后分别介绍简单工厂模式(Simple Factory Pattern)和工厂方法模式(Factory Method Pattern)。
-
- 简单工厂模式(Simple Factory Pattern)
该模式不属于经典的23种设计模式,独立的工厂类用来创建不同的对象,根据传入的参数生成目标对象实例,返回的对象一般会有一个共同的父类或统一的接口。简单工厂模式是学习其它工厂模式的基础。
-
- 工厂方法模式(Factory Method Pattern)
定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。工厂方法模式又简称为工厂模式,也可称为多态工厂模式,它是一种创建型模式。
使用场景
- 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
- 客户端只需要知道传入工厂类的参数,对于如何创建对象并不关心
- 在不同的条件下创建不同的实例,比如:自动化设备根据不同的状况生成不同的报警信息、MES交互时根据需求生成本地和中控的生产信息等。
优缺点
-
- 优点
调用者只需要知道对象的名称即可创建对象。
扩展性高,如果需要增加新产品,只需扩展一个工厂类即可。
屏蔽了产品的具体实现,调用者只关心产品的接口。
-
- 缺点
每次增加一个产品时,都需要增加一个具体类和对应的工厂,使系统中类的数量成倍增加,增加了系统的复杂度和具体类的依赖。
伪代码
- 简单工厂模式
创建基础接口,后续所有接口均继承此接口
INTERFACE I_Interface EXTENDS __system.IQueryInterface
气缸接口包含三个方法和两个属性,方法MoveToRestPosition气缸运动到休息位(原位)、方法MoveToWorkPosition气缸运动到工作位、方法ResetMove复位气缸。
INTERFACE I_Cylinder EXTENDS I_Interface
METHOD MoveToRestPosition : HRESULT
VAR_INPUT
END_VAR
METHOD MoveToWorkPosition : HRESULT
VAR_INPUT
END_VAR
METHOD ResetMove : HRESULT
VAR_INPUT
END_VAR
PROPERTY IsAtRestPosition : BOOL
Get()
PROPERTY IsAtWorkPosition : BOOL
Get()
枚举气缸种类。
TYPE E_CylinderType :
(
Unknown,
FeedbackRest,
FeedbackWork,
FeedbackWorkAndRest,
NoFeedback
);
END_TYPE
实现只有一个Rest反馈信号的气缸类。
{attribute 'enable_dynamic_creation'}
FUNCTION_BLOCK FB_Cylinder_FeedbackRest IMPLEMENTS I_Cylinder
VAR
bInRestSensor : BOOL; // 实际气缸反馈信号
bMoveToWorkSignal : BOOL; // 实际输出信号
bMoveToRestSignal : BOOL; // 实际输出信号
END_VAR
------
PROPERTY IsAtRestPosition : BOOL
Get:
IsAtRestPosition := bInRestSensor;
------
PROPERTY IsAtWorkPosition : BOOL
Get:
IsAtWorkPosition := bInRestSensor = FALSE;
------
METHOD MoveToRestPosition : HRESULT
bMoveToRestSignal := TRUE;
bMoveToWorkSignal := FALSE;
ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='[FB_Cylinder_FeedbackRest]:Move To Rest Position', strArg := '');
------
METHOD MoveToWorkPosition : HRESULT
bMoveToRestSignal := FALSE;
bMoveToWorkSignal := TRUE;
ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_MSGBOX, msgFmtStr :='[FB_Cylinder_FeedbackRest]:Move To Work Position', strArg := '');
------
METHOD ResetMove : HRESULT
bMoveToRestSignal := FALSE;
bMoveToWorkSignal := FALSE;
实现只有一个Work反馈信号的气缸类。
{attribute 'enable_dynamic_creation'}
FUNCTION_BLOCK FB_Cylinder_FeedbackWork IMPLEMENTS I_Cylinder
VAR
bInWorkSensor : BOOL; // 实际气缸反馈信号
bMoveToWorkSignal : BOOL; // 实际输出信号
bMoveToRestSignal : BOOL; // 实际输出信号
END_VAR
------
PROPERTY IsAtRestPosition : BOOL
Get:
IsAtRestPosition := bInWorkSensor = FALSE;
------
PROPERTY IsAtWorkPosition : BOOL
Get:
IsAtWorkPosition := bInWorkSensor;
------
METHOD MoveToRestPosition : HRESULT
bMoveToWorkSignal := FALSE;
bMoveToRestSignal := TRUE;
ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='[FB_Cylinder_FeedbackWork]:Move To Rest Position', strArg := '');
------
METHOD MoveToWorkPosition : HRESULT
bMoveToWorkSignal := TRUE;
bMoveToRestSignal := FALSE;
ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='[FB_Cylinder_FeedbackWork]:Move To Work Position', strArg := '');
------
METHOD ResetMove : HRESULT
bMoveToWorkSignal := FALSE;
bMoveToRestSignal := FALSE;
实现有两个Work和Rest反馈信号的气缸类。
{attribute 'enable_dynamic_creation'}
FUNCTION_BLOCK FB_Cylinder_FeedbackWorkAndRest IMPLEMENTS I_Cylinder
VAR
bInWorkSensor : BOOL; // 实际气缸反馈信号
bInRestSensor : BOOL; // 实际气缸反馈信号
bMoveToWorkSignal : BOOL; // 实际输出信号
bMoveToRestSignal : BOOL; // 实际输出信号
END_VAR
------
PROPERTY IsAtRestPosition : BOOL
Get:
IsAtRestPosition := bInRestSensor;
------
PROPERTY IsAtWorkPosition : BOOL
Get:
IsAtWorkPosition := bInWorkSensor;
------
METHOD MoveToRestPosition : HRESULT
bMoveToWorkSignal := FALSE;
bMoveToRestSignal := TRUE;
ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='[FB_Cylinder_FeedbackWorkAndRest]:Move To Rest Position', strArg := '');
------
METHOD MoveToWorkPosition : HRESULT
bMoveToWorkSignal := TRUE;
bMoveToRestSignal := FALSE;
ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='[FB_Cylinder_FeedbackWorkAndRest]:Move To Work Position', strArg := '');
------
METHOD ResetMove : HRESULT
bMoveToWorkSignal := FALSE;
bMoveToRestSignal := FALSE;
实现无反馈信号的气缸类。
{attribute 'enable_dynamic_creation'}
FUNCTION_BLOCK FB_Cylinder_NoFeedback IMPLEMENTS I_Cylinder
VAR
bMoveToWorkSignal : BOOL; // 实际输出信号
bMoveToRestSignal : BOOL; // 实际输出信号
END_VAR
------
PROPERTY IsAtRestPosition : BOOL
Get:
IsAtRestPosition := bMoveToRestSignal= TRUE AND bMoveToWorkSignal = FALSE;
------
PROPERTY IsAtWorkPosition : BOOL
Get:
IsAtWorkPosition := bMoveToRestSignal= FALSE AND bMoveToWorkSignal = TRUE;
------
METHOD MoveToRestPosition : HRESULT
bMoveToWorkSignal := FALSE;
bMoveToRestSignal := TRUE;
ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='[FB_Cylinder_NoFeedback]:Move To Rest Position', strArg := '');
-----
METHOD MoveToWorkPosition : HRESULT
bMoveToWorkSignal := TRUE;
bMoveToRestSignal := FALSE;
ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='[FB_Cylinder_NoFeedback]:Move To Work Position', strArg := '');
-----
METHOD ResetMove : HRESULT
bMoveToWorkSignal := FALSE;
bMoveToRestSignal := FALSE;
创建简单工厂类,该类主要两个方法GetCylinderObject返回生成的气缸对象实例,方法ReleaseMemory释放创建对象的内存。
(* 气缸简单工厂 *)
FUNCTION_BLOCK FB_CylinderSimpleFactory
VAR
// 只有Rest信号的气缸
pCylinder_FeedbackRest : POINTER TO FB_Cylinder_FeedbackRest;
// 只有Work信号的气缸
pCylinder_FeedbackWork : POINTER TO FB_Cylinder_FeedbackWork;
// Work和Rest信号的气缸
pCylinder_FeedbackWorkAndRest : POINTER TO FB_Cylinder_FeedbackWorkAndRest;
// 没有信号反馈的气缸
pCylinder_NoFeedback : POINTER TO FB_Cylinder_NoFeedback;
iCylinder : I_Cylinder;
END_VAR
------
METHOD GetCylinderObject : I_Cylinder
VAR_INPUT
cylinderType : E_CylinderType;
END_VAR
CASE cylinderType OF
E_CylinderType.FeedbackRest:
pCylinder_FeedbackRest := __NEW(FB_Cylinder_FeedbackRest);
IF pCylinder_FeedbackRest <> 0 THEN
GetCylinderObject := pCylinder_FeedbackRest^;
END_IF
E_CylinderType.FeedbackWork:
pCylinder_FeedbackWork := __NEW(FB_Cylinder_FeedbackWork);
IF pCylinder_FeedbackWork <> 0 THEN
GetCylinderObject := pCylinder_FeedbackWork^;
END_IF
E_CylinderType.FeedbackWorkAndRest:
pCylinder_FeedbackWorkAndRest := __NEW(FB_Cylinder_FeedbackWorkAndRest);
IF pCylinder_FeedbackWorkAndRest <> 0 THEN
GetCylinderObject := pCylinder_FeedbackWorkAndRest^;
END_IF
E_CylinderType.NoFeedback:
pCylinder_NoFeedback := __NEW(FB_Cylinder_NoFeedback);
IF pCylinder_NoFeedback <> 0 THEN
GetCylinderObject := pCylinder_NoFeedback^;
END_IF
END_CASE
------
METHOD ReleaseMemory : HRESULT
VAR_INPUT
END_VAR
IF (pCylinder_FeedbackRest <> 0) THEN
__DELETE(pCylinder_FeedbackRest);
pCylinder_FeedbackRest := 0;
END_IF
IF (pCylinder_FeedbackWork <> 0) THEN
__DELETE(pCylinder_FeedbackWork);
pCylinder_FeedbackWork := 0;
END_IF
IF (pCylinder_FeedbackWorkAndRest <> 0) THEN
__DELETE(pCylinder_FeedbackWorkAndRest);
pCylinder_FeedbackWorkAndRest := 0;
END_IF
IF (pCylinder_NoFeedback <> 0) THEN
__DELETE(pCylinder_NoFeedback);
pCylinder_NoFeedback := 0;
END_IF
------
METHOD FB_exit : BOOL
VAR_INPUT
bInCopyCode : BOOL; // if TRUE, the exit method is called for exiting an instance that is copied afterwards (online change).
END_VAR
IF (pCylinder_FeedbackRest <> 0) THEN
__DELETE(pCylinder_FeedbackRest);
pCylinder_FeedbackRest := 0;
END_IF
IF (pCylinder_FeedbackWork <> 0) THEN
__DELETE(pCylinder_FeedbackWork);
pCylinder_FeedbackWork := 0;
END_IF
IF (pCylinder_FeedbackWorkAndRest <> 0) THEN
__DELETE(pCylinder_FeedbackWorkAndRest);
pCylinder_FeedbackWorkAndRest := 0;
END_IF
IF (pCylinder_NoFeedback <> 0) THEN
__DELETE(pCylinder_NoFeedback);
pCylinder_NoFeedback := 0;
END_IF
在Main主程序中实例化简单工厂,声明itfCylinder接口变量接收简单工厂创建的气缸实例。
PROGRAM MAIN
VAR
// 简单工厂测试
fbCylinderSimpleFactory : FB_CylinderSimpleFactory;
itfCylinder : I_Cylinder;
eCylinderType : E_CylinderType := E_CylinderType.Unknown;
bTest : BOOL;
END_VAR
(* 简单工厂 *)
IF bTest THEN
bTest := FALSE;
itfCylinder := fbCylinderSimpleFactory.GetCylinderObject(eCylinderType);
IF itfCylinder <> 0 THEN
itfCylinder.MoveToWorkPosition();
itfCylinder.MoveToRestPosition();
END_IF
fbCylinderSimpleFactory.ReleaseMemory();
END_IF
运行在线更改eCylinderType值并写入,输入日志。
MSG | 'PlcTask' (350): [FB_Cylinder_FeedbackRest]:Move To Work Position
MSG | 'PlcTask' (350): [FB_Cylinder_FeedbackRest]:Move To Rest Position
MSG | 'PlcTask' (350): [FB_Cylinder_FeedbackRest]:Move To Work Position
MSG | 'PlcTask' (350): [FB_Cylinder_FeedbackRest]:Move To Rest Position
MSG |'PlcTask' (350): [FB_Cylinder_FeedbackWork]:Move To Work Position
MSG |'PlcTask' (350): [FB_Cylinder_FeedbackWork]:Move To Rest Position
MSG |'PlcTask' (350): [FB_Cylinder_FeedbackWork]:Move To Work Position
MSG |'PlcTask' (350): [FB_Cylinder_FeedbackWork]:Move To Rest Position
MSG |'PlcTask' (350): [FB_Cylinder_FeedbackWorkAndRest]:Move To Work Position
MSG |'PlcTask' (350): [FB_Cylinder_FeedbackWorkAndRest]:Move To Work Position
MSG |'PlcTask' (350): [FB_Cylinder_FeedbackWorkAndRest]:Move To Rest Position
MSG |'PlcTask' (350): [FB_Cylinder_FeedbackWorkAndRest]:Move To Rest Position
MSG |'PlcTask' (350): [FB_Cylinder_NoFeedback]:Move To Work Position
MSG |'PlcTask' (350): [FB_Cylinder_NoFeedback]:Move To Work Position
MSG |'PlcTask' (350): [FB_Cylinder_NoFeedback]:Move To Rest Position
MSG |'PlcTask' (350): [FB_Cylinder_NoFeedback]:Move To Rest Position
理论上日志只输出
Move To Work Position和Move To Rest Position两条结果,上面实际上多输出了一条,经过测试官方的ADSLOGSTR函数本来就会输出两条相同的信息。
- 工厂方法模式
简单工厂模式将产品的类型均置于工厂内部,由消费者根据参数选择生成目标产品实例。若新增产品种类,如:气缸输出控制由两个变成一个,即一个输出信号控制气缸伸缩。此时就需要新增气缸种类并修改简单工厂内部逻辑。不符合开闭原则,对扩展开放,对修改关闭。同时若产品种类非常多,也会造成工厂内部过于庞大,不易维护。
这时工厂方法模式可以很好解决这个问题,防止工厂成员爆炸以及后续方便扩展,以下示例是对上述简单工厂的修改。
创建一个抽象工厂类,包含两个抽象方法成员,GetCylinderObject获取气缸对象,ReleaseMemory释放创建对象内存。
FUNCTION_BLOCK ABSTRACT FB_CylinderAbstractFactory
VAR
END_VAR
------
METHOD ABSTRACT GetCylinderObject : I_Cylinder
VAR_INPUT
END_VAR
------
METHOD ABSTRACT ReleaseMemory : HRESULT
VAR_INPUT
END_VAR
FB_CylinderFeedbackRestFactory继承于FB_CylinderAbstractFactory抽象工厂类,并实现两个抽象方法GetCylinderObject和ReleaseMemory。从代码可以看出FB_CylinderFeedbackRestFactory工厂类只生产FB_Cylinder_FeedbackRest类型的气缸。
FUNCTION_BLOCK FB_CylinderFeedbackRestFactory EXTENDS FB_CylinderAbstractFactory
VAR
pCylinder_FeedbackRest : POINTER TO FB_Cylinder_FeedbackRest;
END_VAR
------
METHOD GetCylinderObject : I_Cylinder
VAR_INPUT
END_VAR
pCylinder_FeedbackRest := __NEW(FB_Cylinder_FeedbackRest);
IF pCylinder_FeedbackRest <> 0 THEN
GetCylinderObject := pCylinder_FeedbackRest^;
END_IF
------
METHOD ReleaseMemory : HRESULT
VAR_INPUT
END_VAR
IF (pCylinder_FeedbackRest <> 0) THEN
__DELETE(pCylinder_FeedbackRest);
END_IF
------
METHOD FB_exit : BOOL
VAR_INPUT
bInCopyCode : BOOL; // if TRUE, the exit method is called for exiting an instance that is copied afterwards (online change).
END_VAR
IF (pCylinder_FeedbackRest <> 0) THEN
__DELETE(pCylinder_FeedbackRest);
pCylinder_FeedbackRest := 0;
END_IF
解释同上
FUNCTION_BLOCK FB_CylinderFeedbackWorkFactory EXTENDS FB_CylinderAbstractFactory
VAR
pCylinder_FeedbackWork : POINTER TO FB_Cylinder_FeedbackWork;
END_VAR
------
METHOD GetCylinderObject : I_Cylinder
VAR_INPUT
END_VAR
pCylinder_FeedbackWork := __NEW(FB_Cylinder_FeedbackWork);
IF pCylinder_FeedbackWork <> 0 THEN
GetCylinderObject := pCylinder_FeedbackWork^;
END_IF
------
METHOD ReleaseMemory : HRESULT
VAR_INPUT
END_VAR
IF (pCylinder_FeedbackWork <> 0) THEN
__DELETE(pCylinder_FeedbackWork);
END_IF
------
METHOD FB_exit : BOOL
VAR_INPUT
bInCopyCode : BOOL; // if TRUE, the exit method is called for exiting an instance that is copied afterwards (online change).
END_VAR
IF (pCylinder_FeedbackWork <> 0) THEN
__DELETE(pCylinder_FeedbackWork);
END_IF
解释同上
FUNCTION_BLOCK FB_CylinderFeedbackWorkAndRestFactory EXTENDS FB_CylinderAbstractFactory
VAR
pCylinder_FeedbackWorkAndRest : POINTER TO FB_Cylinder_FeedbackWorkAndRest;
END_VAR
------
METHOD GetCylinderObject : I_Cylinder
VAR_INPUT
END_VAR
pCylinder_FeedbackWorkAndRest := __NEW(FB_Cylinder_FeedbackWorkAndRest);
IF pCylinder_FeedbackWorkAndRest <> 0 THEN
GetCylinderObject := pCylinder_FeedbackWorkAndRest^;
END_IF
------
METHOD ReleaseMemory : HRESULT
VAR_INPUT
END_VAR
IF (pCylinder_FeedbackWorkAndRest <> 0) THEN
__DELETE(pCylinder_FeedbackWorkAndRest);
END_IF
------
METHOD FB_exit : BOOL
VAR_INPUT
bInCopyCode : BOOL; // if TRUE, the exit method is called for exiting an instance that is copied afterwards (online change).
END_VAR
IF (pCylinder_FeedbackWorkAndRest <> 0) THEN
__DELETE(pCylinder_FeedbackWorkAndRest);
END_IF
解释同上
FUNCTION_BLOCK FB_CylinderNoFeedbackFactory EXTENDS FB_CylinderAbstractFactory
VAR
pCylinder_NoFeedback : POINTER TO FB_Cylinder_NoFeedback;
END_VAR
------
METHOD GetCylinderObject : I_Cylinder
VAR_INPUT
END_VAR
pCylinder_NoFeedback := __NEW(FB_Cylinder_NoFeedback);
IF pCylinder_NoFeedback <> 0 THEN
GetCylinderObject := pCylinder_NoFeedback^;
END_IF
------
METHOD ReleaseMemory : HRESULT
VAR_INPUT
END_VAR
IF (pCylinder_NoFeedback <> 0) THEN
__DELETE(pCylinder_NoFeedback);
END_IF
------
METHOD FB_exit : BOOL
VAR_INPUT
bInCopyCode : BOOL; // if TRUE, the exit method is called for exiting an instance that is copied afterwards (online change).
END_VAR
IF (pCylinder_NoFeedback <> 0) THEN
__DELETE(pCylinder_NoFeedback);
END_IF
在主程序中声明生产各种类型气缸的工厂,并创建其抽象工厂的引用。
PROGRAM MAIN
VAR
itfCylinder : I_Cylinder;
eCylinderType : E_CylinderType := E_CylinderType.Unknown;
bTest : BOOL;
refCylinderAbstractFactory : REFERENCE TO FB_CylinderAbstractFactory;
fbCylinderFeedbackRestFactory : FB_CylinderFeedbackRestFactory;
fbCylinderFeedbackWorkFactory : FB_CylinderFeedbackWorkFactory;
fbCylinderFeedbackWorkAndRestFactory : FB_CylinderFeedbackWorkAndRestFactory;
fbCylinderNoFeedbackFactory : FB_CylinderNoFeedbackFactory;
END_VAR
IF bTest THEN
bTest := FALSE;
// 抽象工厂引用接收目标工厂类型实例
IF eCylinderType = E_CylinderType.FeedbackRest THEN
refCylinderAbstractFactory REF= fbCylinderFeedbackRestFactory;
ELSIF eCylinderType = E_CylinderType.FeedbackWork THEN
refCylinderAbstractFactory REF= fbCylinderFeedbackWorkFactory;
ELSIF eCylinderType = E_CylinderType.FeedbackWorkAndRest THEN
refCylinderAbstractFactory REF= fbCylinderFeedbackWorkAndRestFactory;
ELSIF eCylinderType = E_CylinderType.NoFeedback THEN
refCylinderAbstractFactory REF= fbCylinderNoFeedbackFactory;
ELSE
//
END_IF
IF __ISVALIDREF(refCylinderAbstractFactory) THEN
itfCylinder := refCylinderAbstractFactory.GetCylinderObject();
END_IF
IF itfCylinder <> 0 THEN
itfCylinder.MoveToWorkPosition();
itfCylinder.MoveToRestPosition();
END_IF
IF __ISVALIDREF(refCylinderAbstractFactory) THEN
// 释放内存
refCylinderAbstractFactory.ReleaseMemory();
END_IF
END_IF
由于PLC无法像高级语言那样,声明抽象类型接收子类实例。所以这里使用
REFERENCE引用类型来接收子类实例。也可以采用接口,自行实现吧。
运行在线更改eCylinderType值并写入,输入日志。输出结果与简单工厂模式一致。
MSG | 'PlcTask' (350): [FB_Cylinder_FeedbackRest]:Move To Work Position
MSG | 'PlcTask' (350): [FB_Cylinder_FeedbackRest]:Move To Rest Position
MSG | 'PlcTask' (350): [FB_Cylinder_FeedbackRest]:Move To Work Position
MSG | 'PlcTask' (350): [FB_Cylinder_FeedbackRest]:Move To Rest Position
MSG |'PlcTask' (350): [FB_Cylinder_FeedbackWork]:Move To Work Position
MSG |'PlcTask' (350): [FB_Cylinder_FeedbackWork]:Move To Rest Position
MSG |'PlcTask' (350): [FB_Cylinder_FeedbackWork]:Move To Work Position
MSG |'PlcTask' (350): [FB_Cylinder_FeedbackWork]:Move To Rest Position
MSG |'PlcTask' (350): [FB_Cylinder_FeedbackWorkAndRest]:Move To Work Position
MSG |'PlcTask' (350): [FB_Cylinder_FeedbackWorkAndRest]:Move To Work Position
MSG |'PlcTask' (350): [FB_Cylinder_FeedbackWorkAndRest]:Move To Rest Position
MSG |'PlcTask' (350): [FB_Cylinder_FeedbackWorkAndRest]:Move To Rest Position
MSG |'PlcTask' (350): [FB_Cylinder_NoFeedback]:Move To Work Position
MSG |'PlcTask' (350): [FB_Cylinder_NoFeedback]:Move To Work Position
MSG |'PlcTask' (350): [FB_Cylinder_NoFeedback]:Move To Rest Position
MSG |'PlcTask' (350): [FB_Cylinder_NoFeedback]:Move To Rest Position
与简单工厂不同的是,新增气缸类型只需要创建相应的工厂类即可。符合单一职责原则和开闭原则,工厂只生产一种产品,将工厂与其它类型产品解耦,新增产品不影响已存在的工厂类。

浙公网安备 33010602011771号