PLC Structured Text Design Patterns
PLC结构化文本设计模式——适配器模式(Adapter Pattern)
介绍
适配器模式(Adapter Pattern)充当两个不兼容接口之间的桥梁,属于结构型设计模式。它通过一个中间件(适配器)将一个类的接口转换成客户期望的另一个接口,使原本不能一起工作的类能够协同工作。适配器模式是一种软件设计模式,旨在解决不同接口之间的兼容性问题。适配器通过继承或依赖现有对象,并实现所需的目标接口。——Java 适配器模式|菜鸟教程
使用场景
- 需要使用现有类,但其接口不符合系统需求。
- 希望创建一个可复用的类,与多个不相关的类(包括未来可能引入的类)一起工作,这些类可能没有统一的接口。
- 通过接口转换,将一个类集成到另一个类系中。
生活中经典的例子,手机适配器或电脑适配器,连接手机或电脑端的接口为USB,连接充电插座端是两孔/三孔,充电就通过中间的适配器。
优缺点
-
优点
- 促进了类之间的协同工作,即使它们没有直接的关联。
- 提高了类的复用性。
- 增加了类的透明度。
- 提供了良好的灵活性。
-
缺点
- 过度使用适配器可能导致系统结构混乱,难以理解和维护。
- 由于只能继承一个类,因此只能适配一个类,且目标类必须是抽象的。
Java 适配器模式|菜鸟教程
伪代码
以socket通讯和串口通讯为例,设计适配器用以适配客户端接口I_Communication
。
INTERFACE I_Interface EXTENDS __SYSTEM.IQueryInterface
串口通讯接口,接口的属性成员和方法成员与客户端接口I_Communication
不一致,因此需要进行适配。
INTERFACE I_SerialCom EXTENDS I_InterfacePROPERTY BaudRate : INT
Get()
------
PROPERTY DataBits : INT
Get()
------
PROPERTY IsConnected : BOOL
Get()
------
PROPERTY Parity : INT
Get()
------
PROPERTY PortName : STRING
Get()
------
PROPERTY StopBits : INT
Get()
------
METHOD Close : BOOL
VAR_INPUT
END_VAR
------
METHOD Open : BOOL
VAR_INPUT
END_VAR
------
METHOD SendData : STRING
VAR_INPUTdata : STRING;
END_VAR
Socket通讯接口,接口的属性成员和方法成员与客户端接口I_Communication
不一致,因此需要进行适配。
INTERFACE I_Socket EXTENDS I_InterfaceMETHOD Connect : BOOL
VAR_INPUT
END_VAR
------
METHOD Disconnect : BOOL
VAR_INPUT
END_VAR
------
METHOD SendMessage : STRING
VAR_INPUTmessage : STRING;
END_VAR
------
PROPERTY IpAddress : STRING
Get()
------
PROPERTY IsConnected : BOOL
Get()
------
PROPERTY Port : INT
Get()
客户端需要系统通用接口,直接工作在业务中。前面的I_SerialCom
和I_Socket
接口需要适配I_Communication
接口。
INTERFACE I_Communication EXTENDS I_InterfaceMETHOD Connect : BOOL
VAR_INPUT
END_VAR
------
METHOD Disconnect : BOOL
VAR_INPUT
END_VAR
------
METHOD Send : STRING
VAR_INPUTsContent : STRING;
END_VAR
------
PROPERTY IsConnected : BOOL
Get()
FB_SerialCom
串口通讯,实现I_SerialCom
接口。
FUNCTION_BLOCK FB_SerialCom IMPLEMENTS I_SerialCom
VAR_IsConnected : BOOL := FALSE;_BaudRate : INT;_DataBits : INT;_Parity : INT;_PortName : STRING;_StopBits : INT;
END_VAR
------
PROPERTY BaudRate : INT
Get:BaudRate := THIS^._BaudRate;
------
PROPERTY DataBits : INT
Get:DataBits := THIS^._DataBits;
------
PROPERTY IsConnected : BOOL
Get:IsConnected := THIS^._IsConnected;
------
PROPERTY Parity : INT
Get:Parity := THIS^._Parity;
------
PROPERTY PortName : STRING
Get:PortName := THIS^._PortName;
------
PROPERTY StopBits : INT
Get:StopBits := THIS^._StopBits;
------
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)portName : STRING; // 端口号baudRate : INT; // 波特率parity : INT; // 校验位dataBits : INT; // 数据位stopBits : INT; // 停止位
END_VARTHIS^._PortName := portName;
THIS^._BaudRate := baudRate;
THIS^._Parity := parity;
THIS^._DataBits := dataBits;
THIS^._StopBits := stopBits;
------
METHOD Open : BOOL
VAR_INPUT
END_VARADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='Serial port com port name:%s', strArg := THIS^._PortName);
ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='Serial port com baud rate:%s', strArg := TO_STRING(THIS^._BaudRate));
ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='Serial port com parity:%s', strArg := TO_STRING(THIS^._Parity));
ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='Serial port com data bits:%s', strArg := TO_STRING(THIS^._DataBits));
ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='Serial port com stop bits:%s', strArg := TO_STRING(THIS^._StopBits));// 打开串口逻辑
// ************
// 打开串口逻辑
THIS^._IsConnected := TRUE;ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='Serial port com state:%s', strArg := 'Opened');
------
METHOD SendData : STRING
VAR_INPUTdata : STRING;
END_VAR// 串口发送数据逻辑
// ******************
// 串口发送数据逻辑ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='Serial port com send data:%s', strArg := data);
------
METHOD Close : BOOL// 关闭串口逻辑
// ***********
// 关闭串口逻辑
THIS^._IsConnected := FALSE;ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='Serial port com state:%s', strArg := 'Closed');
FB_Socket
通讯实体类,实现I_Socket
接口
FUNCTION_BLOCK FB_Socket IMPLEMENTS I_Socket
VAR_isConnect : BOOL;_ipAddress : STRING;_port : INT;
END_VAR
------
PROPERTY IpAddress : STRING
Get:IpAddress := THIS^._ipAddress;
------
PROPERTY Port : INT
Get:Port := THIS^._port;
------
PROPERTY IsConnected : BOOL
Get:IsConnected := THIS^._isConnect;
------
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)ipAddress : STRING; // IP地址port : INT; // 端口号
END_VARTHIS^._ipAddress := ipAddress;
THIS^._port := port;
------
METHOD Connect : BOOL
VAR_INPUT
END_VARADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='Socket ip address:%s', strArg := THIS^._ipAddress);ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='Socket port:%s', strArg := TO_STRING(THIS^._port));// socket连接逻辑
// **************
// socket连接逻辑THIS^._isConnect := TRUE;ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='Socket state:%s', strArg := 'Connected');
------
METHOD SendMessage : STRING
VAR_INPUTmessage : STRING;
END_VAR// socket发送数据逻辑
// ******************
// socket发送数据逻辑
ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='Socket send message:%s', strArg := message);
------
METHOD Disconnect : BOOL// socket断开连接逻辑
// **************
// socket断开连接逻辑
THIS^._isConnect := FALSE;ADSLOGSTR(msgCtrlMask :=ADSLOG_MSGTYPE_LOG, msgFmtStr :='Socket state:%s', strArg := 'DisConnected');
FUNCTION_BLOCK FB_SerialComAdapter IMPLEMENTS I_Communication
VAR_iSerialCom : I_SerialCom;
END_VAR
------
PROPERTY IsConnected : BOOL
Get:IsConnected := THIS^._iSerialCom.IsConnected;
------
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)iSerialCom : I_SerialCom; // 串口通讯
END_VARTHIS^._iSerialCom := iSerialCom;
------
METHOD Connect : BOOLTHIS^._iSerialCom.Open();
------
METHOD Send : STRING
VAR_INPUTsContent : STRING;
END_VARTHIS^._iSerialCom.SendData(sContent);
------
METHOD Disconnect : BOOLTHIS^._iSerialCom.Close();
Socket通讯适配器FB_SocketAdapter
,实现I_Communication
客户端接口
FUNCTION_BLOCK FB_SocketAdapter IMPLEMENTS I_Communication
VAR_iSocket : I_Socket;
END_VAR
------
PROPERTY IsConnected : BOOL
Get:IsConnected := THIS^._iSocket.IsConnected;
------
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)iSocket : I_Socket; // Socket通讯
END_VARTHIS^._iSocket := iSocket;
------
METHOD Connect : BOOLTHIS^._iSocket.Connect();
------
METHOD Send : STRING
VAR_INPUTsContent : STRING;
END_VARTHIS^._iSocket.SendMessage(sContent);
------
METHOD Disconnect : BOOLTHIS^._iSocket.Disconnect();
客户端主程序MAIN
运行测试,声明fbSerialCom
和fbSocket
两个与客户端不同的接口通讯实例,并初始化连接参数。实例化FB_SerialComAdapter
和FB_SocketAdapter
初始化分别传入fbSerialCom
和fbSocket
实例。客户端根据实际需求使用I_Communication
接口接收适配器实例,执行Connect
、Send
和Disconnect
方法测试接口。
PROGRAM MAIN
VARfbSerialCom : FB_SerialCom('COM1',9600,0,8,1);fbSocket : FB_Socket('192.168.0.1',801);fbSerialComAdapter : FB_SerialComAdapter(fbSerialCom);fbSocketAdapter : FB_SocketAdapter(fbSocket);iCommunication : I_Communication;bTest : BOOL;
END_VARIF bTest THENiCommunication := fbSerialComAdapter;iCommunication.Connect();iCommunication.Send('this is serial com data');iCommunication.Disconnect();iCommunication := fbSocketAdapter;iCommunication.Connect();iCommunication.Send('this is socket message');iCommunication.Disconnect();bTest := FALSE;
END_IF
日志输出结果:
MSG | 'PlcTask' (350): Serial port com port name:COM1
MSG | 'PlcTask' (350): Serial port com baud rate:9600
MSG | 'PlcTask' (350): Serial port com parity:0
MSG | 'PlcTask' (350): Serial port com data bits:8
MSG | 'PlcTask' (350): Serial port com stop bits:1
MSG | 'PlcTask' (350): Serial port com send data:this is serial com data
MSG | 'PlcTask' (350): Serial port com state:ClosedMSG | 'PlcTask' (350): Socket ip address:192.168.0.1
MSG | 'PlcTask' (350): Socket port:801
MSG | 'PlcTask' (350): Socket state:Connected
MSG | 'PlcTask' (350): Socket send message:this is socket message
MSG | 'PlcTask' (350): Socket state:DisConnected
日志输出结果显示,客户端接口I_Communication
通过适配器FB_SerialComAdapter
和FB_SocketAdapter
均可以正常工作,接口测试成功。