当前位置: 首页 > news >正文

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

PLC Structured Text Design Patterns

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

介绍

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

使用场景

  • 需要生成的对象具有复杂的内部结构。——Java 建造者模式|菜鸟教程
  • 需要生成的对象内部属性相互依赖。——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_INPUThours : UINT;
END_VAR
------
METHOD SetMinutes : HRESULT
VAR_INPUTminutes : UINT;
END_VAR
------
METHOD SetSeconds : HRESULT
VAR_INPUTseconds : UINT;
END_VAR

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

FUNCTION_BLOCK FB_Time
VARhour   : UINT; // 时minute : UINT; // 分second : UINT; // 秒
END_VAR
------
METHOD Clear : HRESULT
VAR_INPUT
END_VARhour := 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
VARsTemp : 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
VARfbTime : FB_Time;
END_VAR
------
METHOD GetTime : FB_Time
VAR_INPUT
END_VARGetTime := THIS^.fbTime;
------
METHOD Reset : HRESULT
VAR_INPUT
END_VARfbTime.Clear();
------
METHOD SetHours : HRESULT
VAR_INPUThours	: UINT;
END_VARfbTime.P_Hours := fbTime.P_Hours + hours;
------
METHOD SetMinutes : HRESULT
VAR_INPUTminutes	: UINT;
END_VAR
VARtemp : UINT;
END_VAR// 取余数
temp := minutes / 60;
// 满60分钟累加到小时
IF temp > 0 THENfbTime.P_Hours := fbTime.P_Hours + temp;
END_IF
// 
fbTime.P_Minutes := fbTime.P_Minutes + minutes MOD 60;
------
METHOD SetSeconds : HRESULT
VAR_INPUTseconds	: UINT;
END_VAR
VARtemp : UINT;
END_VARtemp := seconds / 60;
IF	temp > 0 THENfbTime.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_INPUTbInitRetains : 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_INPUThour   : 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
VARfbTimeBuilder  : FB_TimeBuilder;fbTimeDirector : FB_TimeDirector(fbTimeBuilder);fbTime 		   : FB_Time;sTime 		   : STRING;
END_VARfbTime := 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
VARsTemp : STRING;
END_VARsTemp := 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_INPUThour   : 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_INPUThours : UINT;
END_VAR
------
METHOD SetMinutes : I_TimeBuilder
VAR_INPUTminutes : UINT;
END_VAR
------
METHOD SetSeconds : I_TimeBuilder
VAR_INPUTseconds : UINT;
END_VAR

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

FUNCTION_BLOCK FB_TimeBuilder IMPLEMENTS I_TimeBuilder
VARfbTime : FB_Time;
END_VAR
------
METHOD GetTime : FB_Time
VAR_INPUT
END_VARGetTime := THIS^.fbTime;
------
METHOD Reset : I_TimeBuilder
VAR_INPUT
END_VARfbTime.Clear();
Reset := THIS^;
------
METHOD SetHours : I_TimeBuilder
VAR_INPUThours	: UINT;
END_VARfbTime.P_Hours := fbTime.P_Hours + hours;
SetHours := THIS^;
------
METHOD SetMinutes : I_TimeBuilder
VAR_INPUTminutes	: UINT;
END_VAR
VARtemp : UINT;
END_VAR// 取余数
temp := minutes / 60;
// 满60分钟累加到小时
IF temp > 0 THENfbTime.P_Hours := fbTime.P_Hours + temp;
END_IF
fbTime.P_Minutes := fbTime.P_Minutes + minutes MOD 60;SetMinutes := THIS^;
------
METHOD SetSeconds : I_TimeBuilder
VAR_INPUTseconds	: UINT;
END_VAR
VARtemp : UINT;
END_VARtemp := seconds / 60;
IF	temp > 0 THENfbTime.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_INPUTsObject : 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
VARsContent : STRING;
END_VAR
------
METHOD Append : I_StringBuilder
VAR_INPUTsObject : STRING;
END_VARsContent := CONCAT(sContent,sObject);
Append := THIS^;
------
METHOD Clear : I_StringBuildersContent := '';
Clear := THIS^;
------
METHOD ToString : STRINGToString := sContent;

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

FUNCTION_BLOCK FB_Time
VARhour   : 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_INPUThour   : 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)章节内提及的流式编程,接口方法的实现

http://www.wxhsa.cn/company.asp?id=2019

相关文章:

  • C++ - STL - 迭代器
  • MATLAB的智能扫地机器人工作过程仿真
  • linux redis 8.2.1软件开机启动redis.service与etc下的rc.local配置2种方式
  • 在GA中添加Tag-GetDynamicSpecSourceTags().AddTag(NewTag)
  • python如何在函数中使用全局变量?
  • 296、贾生
  • ubuntu 24.04部署mysql8.0.41(glibc2.17)
  • C++ - STL - 键值对pair
  • 第四天学习:LSTM
  • MATLAB的稀疏自编码器实现
  • 题解:P2157 [SDOI2009] 学校食堂
  • LLM 应用开发中的常见模式
  • vue3 与 element-plus
  • 可爱的二维数据结构们
  • 网络安全相关职业
  • 202005_CTFHUB_Redis流量
  • langchain学习之路
  • 通义灵码产品演示: 数据库设计与数据分析
  • win10安装mysql,MySQL5.7详细教程
  • 第二周作业
  • ubuntu 24编译安装libssl.so.1.0.0
  • 9月8-13日小记 - L
  • Task2:利用 Basnet 将Task1中的所有图片转化为显著性图片
  • 代码随想录算法训练营第一天| 704.二分查找、27.移除元素、977.有序数组的平方
  • 让天下没有难查的故障:2025 阿里云 AI 原生编程挑战赛正式启动
  • kuka机器人程序备份
  • AI 测试工具20款
  • VMware安装NOI linux系统教程
  • 强制横屏 ios
  • 张量链式法则(下篇):揭秘Transpose、Summation等复杂算子反向传播,彻底掌握深度学习求导精髓!