Dual Output with Sensor Feedback
In order to add sensor feedback to the CylinderDualOutput component, we could modify the existing interface to include sensor inputs for position feedback. This would allow us to verify whether the cylinder is fully extended or retracted based on the sensor states.
However, modifying the existing interface would violate the Open/Closed Principle (OCP) since we would be changing an existing interface. It could also violate the Interface Segregation Principle (ISP) if not all implementations of the interface require feedback functionality.
1 2 3 4 5 6 | |
Because making this change will require updating any existing implementations of the I_Cylinder interface to include the new properties, this violates the Open/Closed Principle (OCP), and also the Interface Segregation Principle (ISP). Therefore we could instead create a new interface I_CylinderWithFeedback that extends I_Cylinder to include the sensor properties, allowing existing implementations to remain unchanged.
1 2 3 4 | |
Another option would be to create a separate interface specifically for feedback, such as I_CylinderFeedback, and have components that require feedback to implement this additional interface. There are two paths to consider here, depending on the use case. We can create an I_Cylinder interface that extends all other interfaces, or we can create separate interfaces for different functionalities. By creating separate interfaces, we adhere more closely to the Interface Segregation Principle (ISP) since components can implement only the interfaces they need.
For this example, we will proceed with creating a new interface I_CylinderWithFeedback that extends I_Cylinder.
But keep in mind that creating a separate feedback interface is also a valid approach. As the complexity of a component grows, the separation of concerns through multiple interfaces can lead to better maintainability and flexibility. As with avoiding inheriting from concrete implementations, it is also beneficial to avoid creating large, monolithic interfaces that force components to implement methods or properties they do not need. Consider the idea of adding I_Reset or I_Home interfaces, where should they be defined in the hierarchy? I_CylinderWithFeedbackAndReset? I_CylinderWithFeedbackAndHome? Or should they be separate interfaces that can be implemented as needed? Maybe I_Cylinder can EXTEND I_Reset and I_Home, but then all cylinders would have to implement those methods even if they don't need them. It could also be that the specific Cylinder classes implements each interface as needed. Such as:
1 | |
These are design decisions that should be made based on the specific requirements and use cases of the components being developed. As mentioned in the Machine Code Progression document, planning and designing the architecture of components before the implementation phase is crucial for creating maintainable and scalable systems.
Step 2: Create the CylinderDualOutputWithFeedback Component
Create a function block CylinderDualOutputWithFeedback that implements the I_CylinderWithFeedback interface. This component will depend on two Digital Output components for controlling the cylinder and will also include sensor inputs for feedback.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | |
Step 3 : Using a Null Object for the Digital Output and Digital Input Dependencies
To adhere to the Dependency Inversion Principle (DIP) and avoid null reference issues, we can create Null Object implementations of the I_DigitalOutput and I_DigitalInput interfaces. These Null Objects will provide default behavior when no actual digital outputs or inputs are injected.
In the CylinderDualOutputWithFeedback function block declare the Null Object variables for the digital output and digital input dependencies:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
Then, in the FB_Init method, check if the ExtendOutput, RetractOutput, ExtendSensor, and RetractSensor inputs are provided. If not, assign the respective Null Objects to the references.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
Step 4: Using the CylinderDualOutputWithFeedback Component
To use the CylinderDualOutputWithFeedback component, create instances of it in your main program or another function block. Initialize it with actual digital output variables for extending and retracting the cylinder, as well as digital input variables for the sensors.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | |
Conclusion
By following these steps, we have successfully created a CylinderDualOutputWithFeedback component that adheres to the SOLID principles, particularly the Dependency Inversion Principle (DIP) and with some consideration for the Interface Segregation Principle (ISP). This component can now be used in a modular and maintainable way, with clear separation of concerns for controlling the cylinder and obtaining feedback from sensors.