LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Example of Dependency Inversion Principle using LabVIEW Interfaces (requesting code review)

I've been trying to wrap my head around some OOP principles in addition to interfaces lately. I am aware of the interfaces example that ships with LV2020.

 

I did some browsing on youtube and found a channel/video that specifically involved the dependency inversion principle (the 'D' in "SOLID") using interfaces. The only problem is that it is of course not a LabVIEW video, but a C# one. I decided that since all the compsci principles are essentially universal that I would watch it anyway and try to translate it as exactly as possible to LabVIEW. Attached is the project of my attempt at this.

 

I thought the video was very clear and one of the better explanations of this principle. That said, I'm not sure if I did everything correctly, or how it is "supposed" to be. It absolutely works but we all know that it's possible to make something work that isn't exactly best practices. So I'm hoping there will be some interested people that would either like to give this a shot themselves and/or review my code and let me know and provide feedback as to whether it is the correct way to use interfaces in relation to dependency inversion as explained in the video. Also happy to hear feedback based on any other aspect of my code. Much of it was automatically generated by LV class templates of course. I did not explicitly comment the code as I might normally because it is both relatively simple, I used text icons, and following along the video pretty much explains everything (so don't ding me too hard on that, I think you will find things to be straight forward). Honestly, I'm not even sure I could document much about how exactly everything works b/c that is essentially what I'm trying understand myself.

 

This is the video:

Design Patterns: Dependency Inversion Principle Explained Practically in C# (The D in SOLID)

by IAmTimCorey

 

 

EDIT: I went back and added some documentation after all. At least the VIs not created from class templates.

0 Kudos
Message 1 of 15
(3,070 Views)

There's nothing wrong with that code, if it's an example.

 

In real life, I wouldn't use an IPerson interface (or any interface) until I'd have to. It's just more work, more complication and confusion, more maintenance.

 

Interfaces are great, but in C++ and C# they serve a purpose that isn't really a problem in LabVIEW. They provide a barrier, preventing a change in the implementing class to cause a recompilation of all callers. In LabVIEW, compilation doesn't work like that. AFAIK, in C++ (and C#?) interfaces are pretty standard for every class in a non-trivial project.

 

Interfaces are still very useful. I reserve them for reusable code. If I provide a class, users are forced to inherit from that class. With an interface, users can use any (existing) class, including child classes, as long as it provides the methods. Exactly what you want for a reusable peace of code.

 

However, if IChore (for instance) is a reusable library, then the IPerson interface becomes useful...

0 Kudos
Message 2 of 15
(3,018 Views)

Also (but probably not what you want to talk about)...

 

I'd advice you to remove the error in\out and case from VIs that don't need it (like get\set accessors). I know, they came from the template... Doing this will simplify your code (and life) a lot. For instance, in Person.lvclass:Write FirstName, do you really want to not write the value if there's an error in? Would it really matter if you did write the value? Would it be worse or better? Remove the error handling, so you don't even have to think about that anymore.

 

Do use error in\out for methods, when the code could create an error or when sequentially of the method might matter (e.g. when there are side effects, that happen when there are references involved).

0 Kudos
Message 3 of 15
(3,015 Views)

wiebe@CARYA wrote:

There's nothing wrong with that code, if it's an example.

 

In real life, I wouldn't use an IPerson interface (or any interface) until I'd have to. It's just more work, more complication and confusion, more maintenance.

 

Interfaces are great, but in C++ and C# they serve a purpose that isn't really a problem in LabVIEW. They provide a barrier, preventing a change in the implementing class to cause a recompilation of all callers. In LabVIEW, compilation doesn't work like that. AFAIK, in C++ (and C#?) interfaces are pretty standard for every class in a non-trivial project.

 

Interfaces are still very useful. I reserve them for reusable code. If I provide a class, users are forced to inherit from that class. With an interface, users can use any (existing) class, including child classes, as long as it provides the methods. Exactly what you want for a reusable peace of code.

 

However, if IChore (for instance) is a reusable library, then the IPerson interface becomes useful...


I agree, but I think this sort of feeling of unnecessary added complexity is always a problem with these sorts of tutorials/exercises, right? If for whatever reason I needed to code this useless project "for real" I would never go through all the trouble of interfaces, or classes for that matter. On the other hand I would never embark on a tutorial that wanted me to spend days making a highly complex application that warranted such programming constructs either. So we play along and pretend and try to imagine things in the context of a more complex, possibly real life project of the past, accepting that it's an educational exercise.

 

Thanks for reviewing and commenting! 

0 Kudos
Message 4 of 15
(2,998 Views)

wiebe@CARYA wrote:

Also (but probably not what you want to talk about)...

 

I'd advice you to remove the error in\out and case from VIs that don't need it (like get\set accessors). I know, they came from the template... Doing this will simplify your code (and life) a lot. For instance, in Person.lvclass:Write FirstName, do you really want to not write the value if there's an error in? Would it really matter if you did write the value? Would it be worse or better? Remove the error handling, so you don't even have to think about that anymore.

 

Do use error in\out for methods, when the code could create an error or when sequentially of the method might matter (e.g. when there are side effects, that happen when there are references involved).


That actually makes me feel much better because I was questioning that also! It just seems unnecessary for those sorts of VIs.

 

In fact I'm not even sure LV put all those there. I think it definitely did for the interface VIs but not for the VIs for override. At one point I did something on disk and really borked the project and did a lot of deleting/redoing and trying to figure out what LV was yelling about so I don't really know if it's my fault that some have the case structure and some not. I had trouble making those get/set methods using the data member access template and working with the dynamic dispatched interface methods. I ended up getting things working by making them via the VI for override template. Maybe I was just doing something out of order because I didn't really like how that worked and was kind of annoyed that LV wouldn't recognize my data member access VIs as the override methods. 

 

I'm curious, what are your thoughts on things like Format to String when you have a constant wired to the format string terminal? I think most of the time I don't even bother wiring up the error for that sort of thing. I suppose there's probably some way to generate an error from that but I can't recall it ever happening in all my years of LV. Maybe I'd wire it if I was working on the lunar descent module with souls onboard but that project hasn't come my way. 

0 Kudos
Message 5 of 15
(2,995 Views)

@DoctorAutomatic wrote:

I agree, but I think this sort of feeling of unnecessary added complexity is always a problem with these sorts of tutorials/exercises, right?


Absolutely.

 

But in C\C++\C# there's more reason to use interfaces (the compile times). 

 


@DoctorAutomatic wrote:

I agree, but I think this sort of feeling of unnecessary added complexity is always a problem with these sorts of tutorials/exercises, right? If for whatever reason I needed to code this useless project "for real" I would never go through all the trouble of interfaces, or classes for that matter.


I'd still use classes, but no interfaces.

 

I'm so used to classes, it's harder for me to not use them.

0 Kudos
Message 6 of 15
(2,986 Views)

@DoctorAutomatic wrote:

wiebe@CARYA wrote:

Also (but probably not what you want to talk about)...

 

I'd advice you to remove the error in\out and case from VIs that don't need it (like get\set accessors). I know, they came from the template... Doing this will simplify your code (and life) a lot. For instance, in Person.lvclass:Write FirstName, do you really want to not write the value if there's an error in? Would it really matter if you did write the value? Would it be worse or better? Remove the error handling, so you don't even have to think about that anymore.

 

Do use error in\out for methods, when the code could create an error or when sequentially of the method might matter (e.g. when there are side effects, that happen when there are references involved).


That actually makes me feel much better because I was questioning that also! It just seems unnecessary for those sorts of VIs.

 

In fact I'm not even sure LV put all those there. I think it definitely did for the interface VIs but not for the VIs for override. At one point I did something on disk and really borked the project and did a lot of deleting/redoing and trying to figure out what LV was yelling about so I don't really know if it's my fault that some have the case structure and some not. I had trouble making those get/set methods using the data member access template and working with the dynamic dispatched interface methods. I ended up getting things working by making them via the VI for override template. Maybe I was just doing something out of order because I didn't really like how that worked and was kind of annoyed that LV wouldn't recognize my data member access VIs as the override methods. 


There are some recommendations from NI not to use error in\out when it's not needed. This is different from how it used to be, giving every VI an error in\out.

 

'Official' data accessors (as created by LabVIEW) however, need to have an error in\out. I never use them, I simply use a VI. The only advantage of an accessor VI is that you can use them as a property node, and a bit 'wilder', these properties also works if you put the class in a DVR. I don't want to use DVRs, so I don't really care.

 

If you make a VI from overwrite, you should get the parents connector pane, and a call parent method inside. No error case is added.

 

If you make a static or dynamic dispatch method, you'll always get the error in\out and case. Often, first thing I do is delete that (select case, QD, CTRL+R or select, delete, CTRL+B).

 


@DoctorAutomatic wrote:

I'm curious, what are your thoughts on things like Format to String when you have a constant wired to the format string terminal? I think most of the time I don't even bother wiring up the error for that sort of thing. I suppose there's probably some way to generate an error from that but I can't recall it ever happening in all my years of LV. Maybe I'd wire it if I was working on the lunar descent module with souls onboard but that project hasn't come my way. 


I wouldn't use error in\out from a Format Into String, except

1) when it looks better when I do.

2) when you need to pass the CLD exam.

 

Same goes for a lot of 'static' (e.g. not by reference wire) property nodes. If a value (Signaling) fails, something's seriously wrong.

 

These error wires should be helpful. If you use them all over the place, they might actually hurt overview and understanding.

 

The set accessor is typical. Let's say you need to set an object's name. But there's an error. So the name isn't set. Later on, you want to report the error, but now you can't use the module's name, because it wasn't set.

0 Kudos
Message 7 of 15
(2,981 Views)

For a more LabVIEW-centric presentation, you might want to check out Stephen Loftus-Mercer's presentation from NI Connect: NI Connect 2022 LabVIEW Interfaces Things Better Left Unstated 



There are only two ways to tell somebody thanks: Kudos and Marked Solutions
Unofficial Forum Rules and Guidelines
"Not that we are sufficient in ourselves to claim anything as coming from us, but our sufficiency is from God" - 2 Corinthians 3:5
0 Kudos
Message 8 of 15
(2,977 Views)

wiebe@CARYA wrote:

 

 

'Official' data accessors (as created by LabVIEW) however, need to have an error in\out.


 Not necessary, there is a check-box to include error handling terminals. Most of the time I have that unchecked.


wiebe@CARYA wrote:


I wouldn't use error in\out from a Format Into String, except

1) when it looks better when I do.

2) when you need to pass the CLD exam.


 I usually wire it for the simple reason to save some bits in memory:) It seems LabVIEW allocates memory for unwired error inputs but it can reuse it if error in is wired.

Lucian
CLA
0 Kudos
Message 9 of 15
(2,962 Views)

@LucianM wrote:

wiebe@CARYA wrote:

 

 

'Official' data accessors (as created by LabVIEW) however, need to have an error in\out.


 Not necessary, there is a check-box to include error handling terminals. Most of the time I have that unchecked.



If you want to use the accessor as a property node, it does require the Error In and Error Out.

 

 


@LucianM wrote:

wiebe@CARYA wrote:I wouldn't use error in\out from a Format Into String, except

1) when it looks better when I do.

2) when you need to pass the CLD exam.


 I usually wire it for the simple reason to save some bits in memory:) It seems LabVIEW allocates memory for unwired error inputs but it can reuse it if error in is wired.


Most nodes in LabVIEW have a "stomper" flag to keep unused output from allocating memory, which can often propagate up to the input.  Even then, you are likely saving extremely few bytes of memory at the cost of eliminating parallelization.  A good general rule of thumb I've been hearing a lot lately is "do not optimize until absolutely necessary".



There are only two ways to tell somebody thanks: Kudos and Marked Solutions
Unofficial Forum Rules and Guidelines
"Not that we are sufficient in ourselves to claim anything as coming from us, but our sufficiency is from God" - 2 Corinthians 3:5
0 Kudos
Message 10 of 15
(2,955 Views)