Jump to content

Non-OOP Design Patterns


Recommended Posts

Just because you use a class, doesn't make your program an object oriented design any more than passing a value from one function to another makes it dataflow.

Sure, but my point was that your whole application doesn't have to be completed OO for you to be using OO. Two of the most powerful things about OO is encapsulation and instantiation, which you could be doing without really thinking about it. I like to give these examples (DAQmx, Config File VIs) to help people ease into OO.

Link to comment

I've talked to my local DSM, and to Nancy at NIWeek last year. Getting more people to take the advanced courses is the issue. Either that or Nancy just doesn't want to come to hot/humid or cold/rainy DC. :)

I guess I should ping the DSM again.

Oh, and the 2nd edition of "LabVIEW: Advanced Programming Techniques" is from 2006. Do you think they're working on a third edition??

We teach most of the LV course for the DC area. You asemble the students and I'll get you the teacher (well not me but our training coordinator)

Ben

Link to comment

Well, Shaun, I threw you a soft ball and you lobbed it out of the park. :P

It's called "rounders" over here and it's played exclusively by girls :D

Reading about Architecture Astronauts made me wonder if that's not part of my problem. I know I need to spend more time on upfront design, but at some point my attempts to keep my software generic and reusable have led me off into spending a lot of time getting more and more abstract in my design. And whenever I sit down to code it up, all that I can think is how overly complicated it all is.

Patterns aren't as important in Labview as they are in other languages. Modularity is far more of a consideration IMHO. With modularity comes re-usability and those patterns relevant to Labview are generally the glue that binds the modules.

However labview is hierarchical in nature and in terms of architecture I only use one pattern (now)-the diamond. But you won't find it in any books.

As I've said in the past, I don't sling the computer science lingo. So your point that I'm already using designs is a good one. I just don't generally say to myself, "I think I'll use a Producer/Consumer design pattern here." I say, "I ought to feed this loop that's doing a bunch of stuff on command with a queue in another loop." So thanks for all the links to the right names and algorithms. That's important to know.

I use the terms "doodah" and "oojamaflip" quite a lot ("we'll use the messaging doodah for that DVM oojamaflip" :) )

Don't get too hung-up on terminology. It's a graphical language after all. :yes:

Link to comment

Patterns aren't as important in Labview as they are in other languages.

I disagree. Patterns are largely independent of the language you're using. Whether or not you need them depends on what you're trying to accomplish. Saying they're not as important in Labview is like saying formal development methodologies aren't important in Labview. Sure, many developers don't need them because the project scopes are relatively small, but their importance increases with project complexity. I think it's more accurate to say patterns aren't as important to most Labview developers rather than they aren't as important to Labview as a language.

Modularity is far more of a consideration IMHO. With modularity comes re-usability...

Agreed, and generally speaking that's the goal of patterns--getting pieces working together without binding them together too tightly.

However labview is hierarchical in nature...

Why do you say that? I don't think Labview is more inherently hierarchical than other languages.

...and in terms of architecture I only use one pattern (now)-the diamond. But you won't find it in any books.

Maybe because "the diamond" isn't a design pattern? I believe you're describing your app's static dependency tree or perhaps the communication hierarchy, not a design pattern. Design patterns are smaller in scope--they address specific problems people frequently encounter while working with code. Have two components that need to communicate but use incompatible interfaces? Use an adapter. Need to capture the state of a component without exposing its internal details? Use a memento. Stuff like that.

"Diamond" might be considered an architectural pattern, but even then it feels too ambiguous. What aspect of the application is arranged in a diamond? I think it needs more descriptors.

(Incidentally, adapter code can be written with or without oop. I can't think of a good way to do mementos without objects... maybe encode the component's state as an unreadable bit stream?)

  • Like 1
Link to comment

I disagree. Patterns are largely independent of the language you're using. Whether or not you need them depends on what you're trying to accomplish. Saying they're not as important in Labview is like saying formal development methodologies aren't important in Labview. Sure, many developers don't need them because the project scopes are relatively small, but their importance increases with project complexity. I think it's more accurate to say patterns aren't as important to most Labview developers rather than they aren't as important to Labview as a language.

I have already explained this in my original post.

Why do you say that? I don't think Labview is more inherently hierarchical than other languages.

Top down design (and to a lesser extent bottom up) are the recommended design architectures. Why would that be?

Maybe because "the diamond" isn't a design pattern? I believe you're describing your app's static dependency tree or perhaps the communication hierarchy, not a design pattern. Design patterns are smaller in scope--they address specific problems people frequently encounter while working with code. Have two components that need to communicate but use incompatible interfaces? Use an adapter. Need to capture the state of a component without exposing its internal details? Use a memento. Stuff like that.

I believe I did say "architecture". Incompatible interfaces are generally an admission that you haven't fully thought about the design. If it's third party code, then you simply wrap it in an subvi which acts as the "adapter" and I use sub-vis to capture state without exposing internal details. They are "problems" specific to LVPOOP.

"Diamond" might be considered an architectural pattern, but even then it feels too ambiguous. What aspect of the application is arranged in a diamond? I think it needs more descriptors.

Indeed. I vaguely remember describing it in another thread a while ago whilst I was investigating it. Now it's a defacto consideration in my designs. It's just a way of designing the architecture to maximise modularity and minimise points of interconnectedness (is that a word? :P ).

(Incidentally, adapter code can be written with or without oop. I can't think of a good way to do mementos without objects... maybe encode the component's state as an unreadable bit stream?)

Indicators, shift registers, wires, sub-vis. They all externalise "state". There is (generally, not exclusively) no need for "special" components or design patterns to capture state in labview, unless (of course) you break dataflow. Even then, they are merely idioms.

Link to comment

Top down design (and to a lesser extent bottom up) are the recommended design architectures. Why would that be?

Top down and bottom up are not architectures. They are development approaches--they describe the order in which the code is designed and/or implemented. You can apply either approach to any architecture. (Though some architectures may more naturally align with one approach.)

I believe I did say "architecture".

Sure enough, I missed it. My apologies. (Old age sucks.)

Incompatible interfaces are generally an admission that you haven't fully thought about the design.

If you write two components designed to work together and their interfaces are not compatible, then yes, you probably didn't think about the design enough. But to state interface incompatibility is generally the developer's fault is incorrect. There can be lots of reasons interfaces are not completely compatible. Any sort of layered abstraction naturally causes incompatible interfaces between high level components and low level components. Reusable low level components also lead to incompatible interfaces. You can't create interfaces for two different widgets that are both 100% compatible and expose all the features unique to each widget. Ever have to replace an instrument in an existing system with one from a different manufacturer? Incompatible interfaces.

If it's third party code, then you simply wrap it in an subvi which acts as the "adapter"... They are "problems" specific to LVPOOP.

Wait... first you say you write an adapter sub vi, then you say adapters are only needed because of OOP? Design patterns are conceptual solutions to problems encountered when programming with loosely coupled components. It doesn't matter if the components are implemented using oop or not--if you're aiming for the level of decoupling typically desired in an OOP application you'll run into the problems. An adapter is just a chunk of code that translates from one interface to another. It doesn't have to be implemented as a class.

In the instrument replacement situation above, the first reaction by many developers would be to go through all the code replacing all the old instrument calls with new instrument calls. (Frequently accompanied by late nights and large amounts of caffine.) Knowing about adapters one can consider wrapping all the new instrument calls in adapter methods that look like old instrument calls, plug it in, and be done. It turns out the "plugging in" part is way, way easier if you're using oop, but adapters can be individual vis as well.

Indicators, shift registers, wires, sub-vis. They all externalise "state". There is (generally, not exclusively) no need for "special" components or design patterns to capture state in labview...

The point of a memento is to externalize a component's state while keeping the state data accessable only to the component. The external code can only handle the state as a black box entity. This prevents unwanted coupling and improves reusability. A component's state is represented by some sort of data. How the external code obtains the data (indicator, shift register, global, etc.) is irrelevant. How much the external code knows about the data is relevant.

If the state is a cluster, changing the component's state cluster breaks the external code. Too much coupling. If the state is exported as a flattened string or variant the external code can unflatten it. Not enough data protection. There's also the potential of having the external code send an incorrect flattened type to the component. No type safety. In the case of mementos classes provide a far better combination of data protection, type safety, and decoupling than what is easily available using traditional techniques.

unless (of course) you break dataflow.

Ugh. I hate that term. It's meaningless without clarification. What context are you using it in?

Link to comment

Top down and bottom ....

<snip>

Ugh. I hate that term. It's meaningless without clarification. What context are you using it in?

Perhaps start a new thread so as not to derail this one? (although I think we will just end up arguing about symatics).

Edited by ShaunR
Link to comment

If the OOP course was ever offered in the DC area, I would take it (ditto Advanced Architectures).

if you head over to Sydney (down under), I'll give you a quick OO training for free, every person I can convert is a life saved for me :-)

If you have a specific question, I might have time to give you a LV OO implementation that you can test and get started with.

I normally start with a very basic OO frame work and then add more functions (design patterns), when I need them.

A design can look like this(see below), and if you don't know OO or UML it might be confusing, but all the boxes below are classes. But you can think of them as small standalone applications.

So my top application only talkes/uses three smaller componets.

Good luck,

post-941-0-95313800-1320019091_thumb.png

Cheers,

Mike

Link to comment

If the state is exported as a flattened string or variant the external code can unflatten it. Not enough data protection. There's also the potential of having the external code send an incorrect flattened type to the component. No type safety. In the case of mementos classes provide a far better combination of data protection, type safety, and decoupling than what is easily available using traditional techniques.

As an aside: is this really true? LabVIEW objects can easily be flattened to a string, allowing the external code to manipulate the string, and then unflattened back to an object. So it's no safer than a variant or flattened data. And, hey, Shaun has an encryption package, so he could provide an encrypted, flattened string, which is more protected than a LabVIEW object.

Link to comment

As an aside: is this really true? LabVIEW objects can easily be flattened to a string, allowing the external code to manipulate the string, and then unflattened back to an object. So it's no safer than a variant or flattened data. And, hey, Shaun has an encryption package, so he could provide an encrypted, flattened string, which is more protected than a LabVIEW object.

It's kind of true thanks to the way objects are encoded. When designing the flattened data format of LV classes, I really didn't give any consideration to someone deliberately mucking about with the string. That could have meant a serious problem that all objects would have a backdoor way of setting their values. But -- as several determined hackers have griped at me -- I also tried to minimize the size of the flattened string. As a result, the format of the flattened object shifts to three different forms, default fields are not written into the data at all, and there are various size checks that all must be preserved. All of this means that in many cases, it is tricky to reach into the flattened string -- even using the XML format -- and make changes.

Honestly, I made a big mistake in making the Flatten To String and Flatten to XML primitives work on objects by default. The other major programming languages of the world all require classes to opt-in to such abilities. They are not serializable (meaning "can go to/from string") by default. The only ones that are serializable are the ones that programmers declare that way deliberately, and then they have code in the unserialize method to check for tampering, if the class has critical internal data relationships that must be maintained. We lack any such hooks in LabVIEW. It's one of the few areas of LV classes that I wish I could retroactively redesign.

Link to comment

When designing the flattened data format of LV classes, I really didn't give any consideration to someone deliberately mucking about with the string.

Wise in my opinion. After all, it's no different than doing the following in C++:


void foo(SomeObject obj)

{

    // Operates directly on the object data, regardless of access scope to the underlying data

    *((char *) &obj) |= 0xff;

}

[/CODE]

Honestly, I made a big mistake in making the Flatten To String and Flatten to XML primitives work on objects by default. The other major programming languages of the world all require classes to opt-in to such abilities. They are not serializable (meaning "can go to/from string") by default. The only ones that are serializable are the ones that programmers declare that way deliberately, and then they have code in the unserialize method to check for tampering, if the class has critical internal data relationships that must be maintained. We lack any such hooks in LabVIEW. It's one of the few areas of LV classes that I wish I could retroactively redesign.

Such functionality could be introduced (at the expense of backwards compatibility) by extending the core LabVIEW object to include dynamic dispatch methods that handle serialization, among other things. As it stands, LabVIEW Object as a class doesn't really serve any purpose to the programmer other than being the superclass of all objects, it has no public or protected interface.

Link to comment

As an aside: is this really true?

You're right, classes don't provide perfect data protection. They provide a better balance of type safety, data protection, and decoupling than the alternatives. For instance, I believe the format of flattened clusters is documented well enough to make recreating an unknown cluster typedef a trivial exercise. Once you know the type you can unflatten it, change the data, and send it back without the component knowing it has been changed. It might be possible for a determined hacker to reverse engineer an unknown flattened class but it will take much more effort.

And, hey, Shaun has an encryption package, so he could provide an encrypted, flattened string, which is more protected than a LabVIEW object.

Yeah, security through obscurity is not security, so any kind of resiliant data protection should include encryption of some sort. And while an encrypted flattened string does provide data protection and decoupling, it misses the mark on type safety.

It's one of the few areas of LV classes that I wish I could retroactively redesign.

There have been lots of discussions about the dangers of automatically flattening a class--enough that most advanced developers don't do it. Any chance of pulling that capability from future versions? I know there would be all sorts of complaints and broken code, but don't you think that's a better long term solution?

Such functionality could be introduced (at the expense of backwards compatibility) by extending the core LabVIEW object to include dynamic dispatch methods that handle serialization, among other things. As it stands, LabVIEW Object as a class doesn't really serve any purpose to the programmer other than being the superclass of all objects, it has no public or protected interface.

I'd like to see NI provide more of a base class library as well, but I don't think LVOOP is quite ready for it. It needs a solution to the strict single inheritance paradigm first. Add a way to aggregate behaviors across the inheritance tree (interfaces, mixins, traits, etc.,) then a base class library will have a better foundation to build on.

Link to comment

I'd like to see NI provide more of a base class library as well, but I don't think LVOOP is quite ready for it. It needs a solution to the strict single inheritance paradigm first. Add a way to aggregate behaviors across the inheritance tree (interfaces, mixins, traits, etc.,) then a base class library will have a better foundation to build on.

Agreed.

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.