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

PLC结构化文本设计模式——原型模式(Prototype Pattern)

PLC Structured Text Design Patterns

PLC结构化文本设计模式——原型模式(Prototype Pattern)

介绍

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。——Java 原型模式|菜鸟教程

使用原型实例指定要创建对象的种类,并通过拷贝这些原型创建新的对象。与直接实例化类创建新对象不同,原型模式通过拷贝现有对象生成新对象——Java 原型模式|菜鸟教程

使用场景

  • 当创建一个对象需要消耗大量资源(如数据库查询、网络请求、复杂计算),或构造过程非常复杂时,原型模式可以通过复制已有对象来避免重复的高成本操作。

  • 当系统需要在运行时动态创建多种相似但略有差异的对象,且这些对象的类型可能无法提前预知时,原型模式可以通过克隆不同原型来快速生成新对象。

  • 当需要创建对象但无法访问其构造函数(如构造函数为私有),或构造函数参数复杂难以获取时,原型模式可以通过克隆已有实例绕过这些限制。

Tips:对于PLC来说FB构造函数(FB_init)明确禁止私有

  • 当需要保存对象在不同阶段的状态(如撤销操作),原型模式可以通过克隆当前状态来实现历史记录的保存。

  • 当需要创建大量结构相似、仅部分属性不同的对象时,原型模式可以通过克隆基准对象并修改差异部分,提高创建效率。

优缺点

  • 优点
    • 当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有的实例可以提高新实例的创建效率。

    • 可以使用深复制的方式保存对象的状态。将对象复制一份并将其状态保存起来,以便于在使用的时候使用,比如恢复到某一个历史状态,可以辅助实现撤销操作。

  • 缺点
    • 需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时,需要修改源代码,违背了开闭原则。

    • 为了支持深复制,当对象之间存在多重嵌套引用关系时,每一层对象都必须支持深复制,实现起来可能比较麻烦。

伪代码

创建基础接口类型,主要目的:为了后续接口类型转换,接口与接口和接口与指针之间的转换。

INTERFACE I_Interface EXTENDS __SYSTEM.IQueryInterface

创建可销毁/清除I_Disposable接口类型,扩展于I_Interface

INTERFACE I_Disposable EXTENDS I_InterfaceMETHOD Dispose
VAR_INPUT
END_VAR

创建可克隆I_Cloneable接口,扩展于I_Disposable

INTERFACE I_Cloneable EXTENDS I_DisposableMETHOD Clone : I_Cloneable
VAR_INPUT
END_VAR

创建产品参数接口I_ProductParameter,继承I_Cloneable

INTERFACE I_ProductParameter EXTENDS I_CloneablePROPERTY Height : REAL
Get()
Set()PROPERTY ID : INT
Get()
Set()PROPERTY Name : STRING
Get()
Set()PROPERTY Width : REAL
Get()
Set()

定义FB_ProductParameter类并实现I_ProductParameter接口。使用PLC动态创建对象实例需要开发人员手动去管理对象的生命周期,使用完需要销毁/释放内存,这区别于C#存在gc机制,自动回收内存。因此,需要实现方法Dispose,释放内存防止资源浪费,使用__New()创建对象实例时需要特别注意这一点。

{attribute 'enable_dynamic_creation'}
FUNCTION_BLOCK FB_ProductParameter IMPLEMENTS I_ProductParameter
VARsProductName 	: STRING;nProductId	 	: INT;nProductWidth	: REAL;nProductHeight	: REAL;
END_VAR
------
METHOD Clone : I_Cloneable
VARpProductParameter : POINTER TO FB_ProductParameter;
END_VARpProductParameter := __NEW(FB_ProductParameter);
(* 以下操作均为"浅拷贝" *)
// 内部成员(变量)按个拷贝
pProductParameter^.Name 	:= THIS^.sProductName;
pProductParameter^.ID 		:= THIS^.nProductId;
pProductParameter^.Width 	:= THIS^.nProductWidth;
pProductParameter^.Height 	:= THIS^.nProductHeight;
// 或者直接赋值
// pProductParameter^ := THIS^;
Clone := pProductParameter^;
------
METHOD Dispose__DELETE(THIS);
ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='Memory has released', strArg := '');
------
PROPERTY Height : REAL
Get:Height := THIS^.nProductHeight;
Set:THIS^.nProductHeight := Height;
------
PROPERTY ID : INT
Get:ID := THIS^.nProductId;
Set:THIS^.nProductId := ID;
------
PROPERTY Name : STRING
Get:Name := THIS^.sProductName;
Set:THIS^.sProductName := Name;
------
PROPERTY Width : REAL
Get:Width := THIS^.nProductWidth;
Set:THIS^.nProductWidth := Width;

浅拷贝(Shallow Copy)
定义:创建一个新对象,然后将原对象的字段值直接复制到新对象中。
对于值类型成员(如 int、float、struct 等):直接复制值本身,新对象和原对象的值类型成员相互独立。
对于引用类型成员(如类实例、数组等):只复制引用地址,新对象和原对象的引用类型成员指向同一个内存地址。

深拷贝(Deep Copy)
定义:创建一个新对象,然后递归复制原对象的所有成员,包括引用类型成员所指向的对象。
无论属性值是基本数据类型还是引用类型,都会创建一个完全独立的副本。

对于PLC而言若相同类型FB直接fb1:=fb2,内部成员无论是POINTERREFERENCE或者INTERFACE只是将fb2内的地址赋值给fb1,fb1里的引用类型变量地址指向的是fb2里的内存,也就是fb1和fb2引用类型使用的是同一块内存。(浅拷贝)

主程序运行,先对fbProductParameter属性赋值初始值,iProductParameterClone接收克隆对象实例,接着将接口iProductParameterClone转换成iProductParameter,日志记录克隆之后的属性值。最后再将iProductParameterClone接口转换成iProductParameterDispose释放创建的内存。

PROGRAM MAIN
VARbTest1 : BOOL;iProductParameter 		: I_ProductParameter;fbProductParameter 		: FB_ProductParameter;iProductParameterClone	: I_Cloneable;iProductParameterDispose: I_Disposable;	
END_VARIF bTest1 THENfbProductParameter.ID := 1;fbProductParameter.Name := 'glasses';fbProductParameter.Width:= 800;fbProductParameter.Height:= 1000;iProductParameterClone := 	fbProductParameter.Clone();// 将拷贝创建的新对象转换成I_ProductParameter接口类型IF __QUERYINTERFACE(iProductParameterClone,iProductParameter) THEN// 输出拷贝对象成员值ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='Name:%s', strArg := iProductParameter.Name);ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='Id:%s', strArg := TO_STRING(iProductParameter.ID));ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='Width:%s', strArg := TO_STRING(iProductParameter.Width));ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='Height:%s', strArg := TO_STRING(iProductParameter.Height));END_IF// iProductParameter.Dispose();释放内存// 将拷贝的新对象转换成I_Disposable接口类型(主要演示__QUERYINTERFACE用法)IF __QUERYINTERFACE(iProductParameterClone,iProductParameterDispose) THEN// 将对象内存释放iProductParameterDispose.Dispose();END_IFbTest1 := FALSE;
END_IF

日志输出结果:

MSG | 'PlcTask' (350): Name:glasses
MSG | 'PlcTask' (350): Id:1
MSG | 'PlcTask' (350): Width:800.0
MSG | 'PlcTask' (350): Height:1000.0
MSG | 'PlcTask' (350): Memory has released
http://www.wxhsa.cn/company.asp?id=5303

相关文章:

  • 【一步步开发AI运动APP】十二、自定义扩展新运动项目1
  • 【Linux】人事档案——用户及组管理 - 详解
  • 试试这个AI邪修方法,让你刷推特时间节省80%
  • [数据结构——lesson10.2堆排序以及TopK障碍]
  • 终端里跑图形应用「GitHub 热点速览」
  • trl ppo
  • PHP-FPM 深度调优指南 告别 502 错误,让你的 PHP 应用飞起来
  • RAG系统大脑调教指南:模型选择、提示设计与质量控保一本通
  • 智驾终局:VLA与WA的“强脑”之争
  • 微软2018年第四季度顶级漏洞赏金猎人榜单揭晓
  • 能源汽车智能线控底盘
  • Linux中的LED子专业的系统
  • DP 凸性优化:wqs 二分
  • 浦东再添一所一流高校,上海交通大学医学院浦东校区正式启用
  • nccl study
  • AI服务器公开招标大面积失败,中国联通“招”了个寂寞?
  • 【GitHub每日速递 250916】2053 个 n8n 工作流曝光!365 种集成 + 可视化管理,效率直接拉满
  • 每日一家公司职场内幕——龙旗科技(上海)
  • 0129_迭代器模式(Iterator)
  • HJ7 取近似值
  • 读人形机器人13艺术领域
  • 活动报名:Voice First!Demo Day@Voice Agent Camp,9.22,上海丨超音速计划 2025
  • Windows计算器:现代C++实现的多功能计算工具
  • 使用 PySide6/PyQt6 实现系统图标的展示与交互
  • 如何让Java的线程池顺序执行任务 ?
  • Git 提交排除文件夹方法总结
  • 如何在 Ubuntu24.04 TLS 上安装 Kubernetes 集群 - Antonie
  • Jmeter的插件开发
  • Educational Codeforces Round 182 (Rated for Div. 2)
  • java第二周课前提问