PLC结构化文本设计模式——建造者模式(Builder Pattern)

PLC Structured Text Design Patterns

PLC结构化文本设计模式——建造者模式(Builder Pattern)

介绍

建造者模式是一种创建型设计模式,它允许你创建复杂对象的步骤与表示方式相分离。
建造者模式是一种创建型设计模式,它的主要目的是将一个复杂对象的构建过程与其表示相分离,从而可以创建具有不同表示形式的对象。
——Java 建造者模式|菜鸟教程

使用场景

与工厂模式的区别是:建造者模式更加关注于零件装配的顺序。——Java 建造者模式|菜鸟教程

优缺点

  • 优点

    分离构建过程和表示,使得构建过程更加灵活,可以构建不同的表示。

    可以更好地控制构建过程,隐藏具体构建细节。

    代码复用性高,可以在不同的构建过程中重复使用相同的建造者。
    ——Java 建造者模式|菜鸟教程

  • 缺点
    如果产品的属性较少,建造者模式可能会导致代码冗余。

    增加了系统的类和对象数量。
    ——Java 建造者模式|菜鸟教程

伪代码

下面代码示例是根据其它文章评论提出的问题而设计的。这里代码仅作学习参考,可能存在未知的Bug,勿照搬照套。

博主可以给个OOP的实际应用例子吗,光看的话根本想不到什么在plc应用上比较优点的地方,我的想法就是如果将一个时间输入拆分为时分秒的三个接口,比如1小时72min 65s 这种输入能够合并成一个 2h 13min 5s 这种比较规范的时间 ,但是并不能想出来在TC3、codesys中的OOP 程序实现该如何写

INTERFACE I_Interface EXTENDS __SYSTEM.IQueryInterface

创建时间构造器接口,方法GetTime将设置的时间转换成返回FB_Time时间类型。方法Reset将内部时间字段时、分、秒存储的数据复位清空,其余方法SetHoursSetMinutesSetSeconds用于设置时间。

INTERFACE I_TimeBuilder EXTENDS I_Interface
------
METHOD GetTime : FB_Time
VAR_INPUT
END_VAR
------
METHOD Reset : HRESULT
VAR_INPUT
END_VAR
------
METHOD SetHours : HRESULT
VAR_INPUT
	hours : UINT;
END_VAR
------
METHOD SetMinutes : HRESULT
VAR_INPUT
	minutes : UINT;
END_VAR
------
METHOD SetSeconds : HRESULT
VAR_INPUT
	seconds : UINT;
END_VAR

创建时间FB_Time类,用于存储相关的时间数据。

FUNCTION_BLOCK FB_Time
VAR
	hour   : UINT; // 时
	minute : UINT; // 分
	second : UINT; // 秒
END_VAR
------
METHOD Clear : HRESULT
VAR_INPUT
END_VAR

hour := 0;
minute := 0;
second := 0;
------
PROPERTY P_Hours : UINT
Get:
    P_Hours := hour;
Set:
    hour := P_Hours;
------
PROPERTY P_Minutes : UINT
Get:
    P_Minutes := minute;
Set:
    minute := P_Minutes;
------
PROPERTY P_Seconds : UINT
Get:
    P_Seconds := second;
Set:
    second := P_Seconds;
------
METHOD ToTimeString : STRING
VAR
	sTemp : STRING;
END_VAR

// 拼接字符串,拼接格式:时:分:秒
sTemp := CONCAT(TO_STRING(hour),':');
sTemp := CONCAT(sTemp,TO_STRING(minute));
sTemp := CONCAT(sTemp,':');
sTemp := CONCAT(sTemp,TO_STRING(second));
ToTimeString := sTemp;
ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='ToTimeString:%s', strArg := sTemp);

创建FB_TimeBuilder时间构造器,并实现接口I_TimeBuilder,其中SetHoursSetMinutesSetSeconds将输入的整型时间转换成时分秒格式。

FUNCTION_BLOCK FB_TimeBuilder IMPLEMENTS I_TimeBuilder
VAR
	fbTime : FB_Time;
END_VAR
------
METHOD GetTime : FB_Time
VAR_INPUT
END_VAR

GetTime := THIS^.fbTime;
------
METHOD Reset : HRESULT
VAR_INPUT
END_VAR

fbTime.Clear();
------
METHOD SetHours : HRESULT
VAR_INPUT
	hours	: UINT;
END_VAR

fbTime.P_Hours := fbTime.P_Hours + hours;
------
METHOD SetMinutes : HRESULT
VAR_INPUT
	minutes	: UINT;
END_VAR
VAR
	temp : UINT;
END_VAR

// 取余数
temp := minutes / 60;
// 满60分钟累加到小时
IF temp > 0 THEN
	fbTime.P_Hours := fbTime.P_Hours + temp;
END_IF
// 
fbTime.P_Minutes := fbTime.P_Minutes + minutes MOD 60;
------
METHOD SetSeconds : HRESULT
VAR_INPUT
	seconds	: UINT;
END_VAR
VAR
	temp : UINT;
END_VAR

temp := seconds / 60;
IF	temp > 0 THEN
	fbTime.P_Minutes := fbTime.P_Minutes + temp;
END_IF
fbTime.P_Seconds := seconds MOD 60;

创建FB_TimeDirector指挥者/指导者,负责调用建造者的方法来构建产品,指导者并不了解具体的构建过程,只关心产品的构建顺序和方式。

FUNCTION_BLOCK FB_TimeDirector
VAR
	_iTimeBuilder : I_TimeBuilder;
END_VAR
------
METHOD FB_init : BOOL
VAR_INPUT
	bInitRetains : BOOL; // if TRUE, the retain variables are initialized (warm start / cold start)
	bInCopyCode : BOOL;  // if TRUE, the instance afterwards gets moved into the copy code (online change)
	iTimeBuilder : I_TimeBuilder;
END_VAR

_iTimeBuilder := iTimeBuilder;
------
METHOD Construct : FB_Time
VAR_INPUT
	hour   : UINT; // 时
	minute : UINT; // 分
	second : UINT; // 秒
END_VAR

_iTimeBuilder.Reset();
_iTimeBuilder.SetSeconds(second);
_iTimeBuilder.SetMinutes(minute);
_iTimeBuilder.SetHours(hour);
Construct := _iTimeBuilder.GetTime();

方法Construct内部,执行次序分别是ResetSetSecondsSetMinutesSetHours确保每次构造时间先清空_iTimeBuilder实例内部数据,然后以秒-分-时的次序累加计算。

主程序中运行测试,在方法Construct参数内输入1小时72分65秒,并声明对应变量接收结果:2小时13分5秒。

| 'PlcTask' (350): ToTimeString:2:13:5

PROGRAM MAIN
VAR
	fbTimeBuilder  : FB_TimeBuilder;
	fbTimeDirector : FB_TimeDirector(fbTimeBuilder);
	fbTime 		   : FB_Time;
	sTime 		   : STRING;
END_VAR

fbTime := fbTimeDirector.Construct(1,72,65);
sTime := fbTime.ToTimeString();

至此,建造者示例完成,上述代码中可以根据实际需要更改,比如:Construct输入的是一个字符串,那么内部就需要对字符串解析。还有FB_Time内的ToTimeString方法也可以改成输出自定义结构体类型等。这些都不重要,感兴趣自行实现。

示例代码中某些方法实现不够优雅,ToTimeString方法实现只是将一行sTemp := CONCAT(TO_STRING(hour),CONCAT(':',CONCAT(TO_STRING(minute),CONCAT(':',TO_STRING(second)))));拆分成下面这种,代码可读性虽然强了一点,但是还不够强,接下来对下面代码进行优化。

METHOD ToTimeString : STRING
VAR
	sTemp : STRING;
END_VAR

sTemp := CONCAT(TO_STRING(hour),':');
sTemp := CONCAT(sTemp,TO_STRING(minute));
sTemp := CONCAT(sTemp,':');
sTemp := CONCAT(sTemp,TO_STRING(second));
ToTimeString := sTemp;

同样这段代码也需要优化。

METHOD Construct : FB_Time
VAR_INPUT
	hour   : UINT; // 时
	minute : UINT; // 分
	second : UINT; // 秒
END_VAR

_iTimeBuilder.Reset();
_iTimeBuilder.SetSeconds(second);
_iTimeBuilder.SetMinutes(minute);
_iTimeBuilder.SetHours(hour);

Construct := _iTimeBuilder.GetTime();

下面将对FB_TimeFB_TimeBuilder进行优化,采用Fluent编程方式实现。首先将I_TimeBuilder接口某些方法的返回值改成I_TimeBuilder

INTERFACE I_TimeBuilder EXTENDS I_Interface
------
METHOD GetTime : FB_Time
VAR_INPUT
END_VAR
------
METHOD Reset : I_TimeBuilder
VAR_INPUT
END_VAR
------
METHOD SetHours : I_TimeBuilder
VAR_INPUT
	hours : UINT;
END_VAR
------
METHOD SetMinutes : I_TimeBuilder
VAR_INPUT
	minutes : UINT;
END_VAR
------
METHOD SetSeconds : I_TimeBuilder
VAR_INPUT
	seconds : UINT;
END_VAR

修改FB_TimeBuilder方法实现,主体实现没有变,只是增加了方法名:=THIS^,也就是方法的返回值指向自身实例。

FUNCTION_BLOCK FB_TimeBuilder IMPLEMENTS I_TimeBuilder
VAR
	fbTime : FB_Time;
END_VAR
------
METHOD GetTime : FB_Time
VAR_INPUT
END_VAR

GetTime := THIS^.fbTime;
------
METHOD Reset : I_TimeBuilder
VAR_INPUT
END_VAR

fbTime.Clear();
Reset := THIS^;
------
METHOD SetHours : I_TimeBuilder
VAR_INPUT
	hours	: UINT;
END_VAR

fbTime.P_Hours := fbTime.P_Hours + hours;
SetHours := THIS^;
------
METHOD SetMinutes : I_TimeBuilder
VAR_INPUT
	minutes	: UINT;
END_VAR
VAR
	temp : UINT;
END_VAR

// 取余数
temp := minutes / 60;
// 满60分钟累加到小时
IF temp > 0 THEN
	fbTime.P_Hours := fbTime.P_Hours + temp;
END_IF
fbTime.P_Minutes := fbTime.P_Minutes + minutes MOD 60;

SetMinutes := THIS^;
------
METHOD SetSeconds : I_TimeBuilder
VAR_INPUT
	seconds	: UINT;
END_VAR
VAR
	temp : UINT;
END_VAR

temp := seconds / 60;
IF	temp > 0 THEN
	fbTime.P_Minutes := fbTime.P_Minutes + temp;
END_IF
fbTime.P_Seconds := seconds MOD 60;

SetSeconds := THIS^;

新增接口I_StringBuilder字符串构造器,用来构造时间格式化字符串。

INTERFACE I_StringBuilder EXTENDS I_Interface
------
METHOD Append : I_StringBuilder
VAR_INPUT
	sObject : STRING;
END_VAR
------
METHOD Clear : I_StringBuilder
VAR_INPUT
END_VAR
------
METHOD ToString : STRING
VAR_INPUT
END_VAR

创建FB_StringBuilder字符串构造器实体类,实现I_StringBuilder接口。

FUNCTION_BLOCK FB_StringBuilder IMPLEMENTS I_StringBuilder
VAR
	sContent : STRING;
END_VAR
------
METHOD Append : I_StringBuilder
VAR_INPUT
	sObject : STRING;
END_VAR

sContent := CONCAT(sContent,sObject);
Append := THIS^;
------
METHOD Clear : I_StringBuilder

sContent := '';
Clear := THIS^;
------
METHOD ToString : STRING

ToString := sContent;

FB_Time类只更改ToTimeString方法,其它成员均保持不变。

FUNCTION_BLOCK FB_Time
VAR
	hour   : UINT; // 时
	minute : UINT; // 分
	second : UINT; // 秒
	fbStringBuilder : FB_StringBuilder;
END_VAR
------
METHOD ToTimeString : STRING

// 拼接字符串
ToTimeString := fbStringBuilder.Clear()
							   .Append(TO_STRING(hour))
							   .Append(':')
							   .Append(TO_STRING(minute))
							   .Append(':')
							   .Append(TO_STRING(second))
							   .ToString();
ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='ToTimeString:%s', strArg := ToTimeString);                               

FB_TimeDirector类只更改Construct方法,其它成员均保持不变。

FUNCTION_BLOCK FB_TimeDirector
VAR
	_iTimeBuilder : I_TimeBuilder;
END_VAR
------
METHOD Construct : FB_Time
VAR_INPUT
	hour   : UINT; // 时
	minute : UINT; // 分
	second : UINT; // 秒
END_VAR

_iTimeBuilder.Reset()
			 .SetSeconds(second)
			 .SetMinutes(minute)
			 .SetHours(hour);

Construct := _iTimeBuilder.GetTime();

主程序MAIN未作任何修改,直接运行程序,输出结果保持与优化之前一致。

| 'PlcTask' (350): ToTimeString:2:13:5

至此本章结束,此篇刚好补齐之前PLC结构化文本(ST)——指针和引用(Pointer&Reference)章节内提及的流式编程,接口方法的实现

posted @ 2025-09-12 15:33  J_Sheng  阅读(25)  评论(0)    收藏  举报