Jump to content

Daklu

Members
  • Posts

    1,824
  • Joined

  • Last visited

  • Days Won

    83

Everything posted by Daklu

  1. There's a decent Wikipedia article about immutable objects here. --------------------- Here's John's WORM example rewritten (and expanded) using an immutable NumericRefnum object. In addition to being able to make an arbitrary number of objects from a single class, I think immutable objects are easier to understand and use because the initialization (or creation) method is clearly separated from the reading method. Of course, if you *want* the FG feature of the WORM then the immutable object pattern doesn't work. (Maybe some sort of immutable class pattern would work?) Personally I prefer to avoid reference-based data, so that's not an issue for me. ImmutableObject.zip
  2. Immutable objects have this capability too. Build a class with creator method(s) and getters but don't provide any setters. Once the object (not class) is created it is immutable and can only be read. This also has the handy feature of being able to create as many objects as you want from a single class rather than having to create a separate vi for each piece of data or deal with dynamically launched vis.
  3. People don't hate Prius'... they hate Prius drivers. Or at least the Prius driver stereotype. (Every time I see a Prius I think of the South Park episode.) [Edit - 10 minutes after I posted this that very South Park episode started on Comedy Central. Coincidence?]
  4. It's illegal in Washington state too, but it's never enforced. Hence, sometimes I see drivers who think they are contributing to highway safety by intentionally holding up traffic and driving the exact speed limit. I'd like to throw them on a motorcycle in the backup behind them and see just how "safe" they feel. More often people aren't paying attention to their driving and just go however fast they happen to be going. There's a state highway near my house with a posted speed limit of 50 mph. People drive it at 40-45 mph *all the time.* Makes me nuts.
  5. Creating a class for the LV equivalent of a #define statement seems like overkill to me, but in general immutable objects = bee's knees. I use them all the time for various data classes and anywhere I want write-once, read-many functionality.
  6. The thread's title implies two different, but related questions: Q1: How do you package constants for use in application code? I do the same thing others have already mentioned. I wrap it in a vi and name it something like CONST_ApplicationConfigFile. Occasionally I'll put related constants in the same sub vi. Here's an example of a constant in my current app. (Yes, I realize it is not a constant as far as the compiler is concerned. From my point of view as the app developer, it is a constant, and that's my primary concern in this instance. I use this same technique for "real" constants as well.) Aside from the benefit of being able to write code to generate a constant, there are a couple other reasons I prefer not using globals. First, renaming a global breaks all the references. A minor annoyance to be sure, but an annoyance nonetheless. Second, globals tend to increase coupling between components in the form of unnecessary and/or unwanted dependencies. This leads to the second question... Q2: How do you use a project wide constant in an application? The concept of "project wide" constants doesn't really fit into the way I build applications. The project is (hopefully) a series of well-defined components connected just enough to achieve the customer's goal. Creating an artifact (global, vi, etc.) with the intent of placing it wherever the data is needed undermines my goal of creating sustainable software. Usually my constants are privately scoped within a single component and I'll try to design my api so other components don't need to know what the constant is. If other components do need the constant, I prefer to send a copy of the value as a message, but I have made the constant public for simplicity on the part of the calling component. I think the more correct solution would be to refactor the constant into the calling component and pass a value copy to the called component. Also, I don't let the constant be the reason for a dependency between two components. The constant's packaging is mostly irrelevant to this question. You can create undesirable dependencies just as easily with a sub vi output as you can from a global or a FG output. From a purely functional standpoint I don't think there's much difference. On the other hand, I think globals and FGs convey a different sense of where they can be "appropriately" used than sub vis. Their intent is to make it easy to access data from different parts of an application. It would seem silly to many developers to read a global value just to pass it to another loop as message data. By wrapping the constant in a sub vi I (hopefully) break the expectation that it should be globally available.
  7. Nope... almost never in fact. I'm not constructing a parent object, I'm constructing a child object, so calling a parent constructor seems out of place to me and makes the code more confusing (IMO.) The child constructor may or may not (often not) have the same creation requirements on the parent class data fields. Unit testing is one example. Some of my class creators obtain a "private" queue for use internally. To test the class I might create a Test Spy child class that monitors the queue and records what messages are sent on it. The construction requirements for the parent and child are: Parent - Obtain a queue and don't expose it to the rest of the application. This prevents other application code from doing something it shouldn't, like flush the queue or enqueue a message to the front. Child - Obtain a queue and inject it into the parent class data field so I can monitor what messages each method sends. With protected accessors to the queue I have a lot more flexibility in what I want a child class to do than I do if I was forced to use the parent class' public constructor.
  8. I just realized I'm no longer receiving email notifications when new posts appear on threads I've contributed to. Did the new site reset everything?
  9. I should have said it doesn't make sense to me. I do think it is a stylistic difference more than a practical difference. Typically I accomplish that by simply creating protected accessors and let the child class developer be responsible for correct implementation. They can use the accessors in Child.Create to set up the parent data. (Lots of times I want more control over the parent data than I would get from a public creator method.) If there's a lot of code that would require duplication in each child class sometimes I'll create a protected Initialize method that wraps it up for convenience.
  10. That sounds reasonable. It seems odd the book just made the statement without any explanation, unless that methphor is commonly used when teaching eigenfrequencies?
  11. I agree with everything Jon said, except the part about it being appropriate to use singletons for immutable objects. True, immutable singletons don't have the issue of race conditions mutable singletons have, but it's still hard to determine the scope of where it is used (because of the absence of wires) and it encourages a messy dependency tree (because it's so easy to retrieve the data from anywhere.) Personally I don't use singletons for anything and I very rarely use reference data of any kind. When I do use reference data I try to limit it to a single block diagram so it is easy to verify correctness. In my opinion, there are a couple times when reference-based data is appropriate: -It is the best technical decision given the constraints of the project. (i.e. Memory limitations, etc.) -It is the best business decision and all stakeholders understand the cost/benefit tradeoff and long-term impact of the decision. If you dig into the *real* reason people use reference data, usually it amounts to "it's easier to implement" or "I don't know how to solve this problem without using reference data."
  12. I'll throw in my $.02 and agree with what the others have said. I build creator methods for all my classes similar to what JG does. It seems to have become a LVOOP idiom and fairly standard practice, though I will still use a class constant for type information, such as with the downcast function. A couple notes on my convention: - I call them "creators" as opposed to "constructors" to maintain differentiation. Objects are "constructed" at some point in time during execution, it's just not exposed to us as developers. I think of it as happening when the RTE executes a class constant on the block diagram (thought that isn't strictly correct in all situations.) - My naming convention is "Create MyClass." If a class has multiple constructors using different types of arguments I'll describe that in the method names. (i.e. "Create MyClass(XmlString)" or "Create MyClass(Path)".) - Generally, I don't put a class input terminal on my creators. The idea of a creator is that I'm generating a new object, not modifying an existing one, so a class input terminal doesn't make sense. (The one exception is with plug-ins where the application needs to create an unknown child object from disk.) Nope, there isn't. I struggled with that for a while before giving up and deciding there's only so much hand-holding I can do as a class designer. Nope. If a developer is going to create a child class, it's his responsibility to understand the parent well enough to create the child correctly. To expand a bit on what Jon said, G doesn't allow us to specify a type without also specifying data. There are several functions that require type information inputs but ignore the actual data. What do we do? We create a data constant and wire it up. What you're asking for (and what I've asked for in the past) is to be able to freely use the class constant as a type definition while preventing it from being used as a data source. Conceptually it is possible, but it changes some of the fundamental concepts of the language and is inconsistent with current usage. See here. I've done that before. Usually the additional cost in development time isn't worth the payoff. Once people get used to the idea of using creators instead of class constants the problem that addresses goes away.
  13. So I finally got a day off yesterday and for some odd reason started skimming through my copy of Standard Mathematical Tables and Formulae [Zwillinger]. I'll be the first to admit that I'm no mathematician. I usually don't have the patience (or interest) to sort through all the formalized symbolism so there is a lot of stuff in there I don't understand. But when I ran across this bit of explanation about eigenfrequencies the only thought I could muster was, "uh.... what?"
  14. I recently added this bit of code in my application. As part of my unit testing I was sending a value of "10" to the Pin Number terminal. When the test failed I probed the integer output and was surprised to find the function interpreting the string as binary instead of decimal! It was returning 2, not 10. Took me about 10 minutes to figure out what the real problem is.
  15. Better yet, categorize this dialog box and other newbie-friendly warnings as "tips" and give us the option to disable all of them at once.
  16. How about just annoying? There are (IMO) too many "are you sure" messages. For example, drop a case structure on a block diagram and put a random function in each of the cases. Right click on it and select "Remove Case Structure." We're lovingly rewarded with this warning: This happens with any multi-frame stacked structure (event, diagram disable, etc.) that has content in the other frames. I understand NI is looking out for inexperienced programmers, but we understand the behavior and consequences of removing a stacked structure. We don't need to be reminded every single time. I can safely say in the years I've been using Labview I have never, not one single time, changed my mind about removing a structure because of the dialog box.
  17. A vision/motion system with real-time video overlay.
  18. Lots of good ideas for the administrators to consider, assuming they're even interested. As Chris pointed out LAVA already has a decent slogan to attract the masses. (Though as far as I know Lava is the *only* independent Labview community.) No matter... I've never let little things like "we already have one" stop me from trying to find something I like better. If nothing else I'll adopt it as an unofficial slogan. This is my favorite so far. It's concise, easy to remember, ties into the theme, and captures the essence of Lava's influence on Labview.
  19. LOL. I was thinking something along the lines of, *Giving life to new ideas *Fostering evolutionary LV development practices *Providing heat for Labview's primordial soup I don't really like any of these--they're too sterile--but it's kind of where I was heading.
  20. I've never really like using "is a" as a measure of whether a class should inherit from another class. I think it's too loosely defined and easily leads to design problems that are hard to resolve. The Circle-Ellipse Problem is an example where it breaks down. That could be the right business decision and I'm not criticising it, but be aware it is probably not the correct architectural decision. At the very least you'll be losing some readability. More likely the problem you've run into indicates an error in your design. If this code is going to be supported and extended over the long-term you'll probably end up wishing you had fixed it. Personally if I had to make a quick fix I would have gone with Shaun's solution. Move all the code from Parent.Operate into a new method, Parent.Operate(Shared), and have Parent.Operate simply delegate to Parent.Operate(Shared). I can't say definitively it's a better solution, but instinctively it makes more sense to me.
  21. Maybe it's hubris talking, but it seems to me LAVA plays a fairly important role in advancing LV development practices. Should LAVA should have a catch phrase that reflects that? Perhaps something that ties in with the volcano metaphor? Underwater vents are sources of all sorts of new and different life forms. Could we come up with a concise phrase that captures the idea of creating new ideas and helping LV development evolve?
  22. Yeah, especially when users can't post to it. This is where you could potentially run into trouble. What happens if you want to create a message without sending it right away? One key to a good reuse api is to not make each unit (component, class, method, etc.) do too much. If the unit's description includes the word "and" it's a pretty good indicator for me that I need to rethink the design a bit. Sometimes I will create those kinds of convenience methods. In the snip below each of those SendRefresh... methods obtains data from the class and sends a message to the UI loop. However, these methods are part of the application code, not the reuse code. I wrote these specifically to improve readability. They are not intended to be reusable beyond this application. Dunno... haven't written it yet. I've considered expanding the list of native types supported. I think I have messages for arrays for all the current native types hanging around somewhere. I haven't had to use it very often and I've never received a request to add more stuff... so I didn't release it. I've thought about releasing a Command message add-on pack. It's trivial to implement but it might help people understand what they can do with the library. I've also thought about creating a more feature-rich Error message, but first I'd want to use it myself in a couple applications. (After I actually build it of course.) Possible? Yep. The EnqueueMessage method is polymorphic. Fair warning though... I'm not a super big fan of polymorphism in a reuse library, especially if the set of polymorphic methods is open-ended. You'll have to convince me that what you have in mind is a good idea. Let's talk. I've been chatting with Minh about a follow up discussion to my user group presentation last week. My guess is it will be late this week or early next week.
  23. I don't recommend modifying the LapDog classes directly--it can easily lead to maintenance issues down the road. Every time I release an update you'll have to go back and modify them all again. (Or choose not to update to later releases.) It also can create problems for future developers. ("I installed the LapDog Messaging package. Why isn't this working?") Alternative 1 - Easy cheezy: One route would be to create a separate vi that wraps up the downcast and getter for each message type. Tie them up in a project library and use that instead of the LapDog palette. When more message classes are created all you have to do is add new vis to the library. You could even package it up and release it as a LapDog Messaging add-on if you're feeling ambitious. When a LapDog Messaging update is released, upgrading to the new version would be pretty painless. Alternative 2 - More work: Another solution is to subclass each of the message types included with the library and add a 'Get StringFromMessageObject' method to each subclass that does the downcasting and and returns the data. Personally I think the first option is a little cleaner. It clearly differentiates between the message framework and your convenience vis.
  24. Sorry for the late reply Steve. I kept getting an error when I tried to reply to the LapDogApi thread, so I tried to use the email reply feature. Four days later the email bounced back to me. Here's my original response: "No, you're not overlooking anything. There are a couple reasons I do it that way, none of which are enough of a concern to prevent you from downcasting internally. 1. Clarity. Since most of the people who look at the code will not be familiar with what I'm doing, I try to make it as easy for them to read as possible. I think explicitly showing the downcast helps make the connection between OO messaging and string/variant messages. 2. Laziness. When there are multiple accessors to retrieve various bits of data from the message, I'd have to put the downcast in every accessor in case the next developer changed the order in which the accessors are called. 3. Convention. I always make the top left and top right conpane terminals use the class constant of the class the method is a member of. I just find it easier to read that way. I had to look at your second diagram for a bit before I realized your input terminal uses the Message class, not the GUI class. (Easily worked around by moving the Message class input to a different terminal if you were so inclined.) Seems to me the difference is 95% stylistic, with *maybe* 5% being readability. As long as you're consistent the readability issue goes away. I wouldn't worry about it too much. (Oh, and I like using messages bundled inside a user event to send data to the event loop too. It's way cleaner than creating all those user events manually.)
×
×
  • Create New...

Important Information

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