Kurt Friday Posted September 16, 2009 Report Share Posted September 16, 2009 New topic generated from this thread I've been having a play with interfaces and I've got something fairly hacky that I thought I would throw up here. One of the problems that I was running into was the inability to call a dynamic dispatch vi using a CIN or a Run VI Invoke Node. What I was able to do is define a static dispatch vi for each class called Interface and in there I would have the methods that could be executed by the interface. I then define a set of vi's called interfaces that know what class they are operating on and hence can call the appropriate static dispatch interface method using VI Server. I'm a bit green on interfaces so there may be big holes in this. The demo implements interfaces on two classes that are unrelated, a Thermometer and a DMM, the interfaces handle Create, Read and Destroy. InterfaceDemo.zip I like the way you've solved the problem of calling similar methods from unrelated classes. That is an approach I had not considered. I think I like that you don't have to obtain the interface before using it. On the other hand I think the reliance on strings and the CBR node will make maintenance more difficult in the long run. What you have doesn't quite present the user with traditional Interfaces, though it wouldn't be hard to wrap your interface in a class to give it a more standardized feel. I agree, using CIN's aren't the best way to go, especially if your interface is broken. I was thinking about a class for the interface but I couldn't see what a class could contribute since the Interface vi's take on the methods of the class they are assigned, and the interface has no data and no dynamic dispatch (the methods that the interface calls can have dynamic dispatch), they are more or less acting as a wrapper. Perhaps a library would be more appropriate. I liked your example, so there are a few ideas kicking around, would be cool to put some collective effort into this. Quote Link to comment
Daklu Posted September 17, 2009 Report Share Posted September 17, 2009 One of the problems that I was running into was the inability to call a dynamic dispatch vi using a CIN or a Run VI Invoke Node. I've never tried this. How does it fail? Does it just not dispatch to the correct child class VI? I was thinking about a class for the interface but I couldn't see what a class could contribute Probably nothing in terms of functionality. In text languages using Interfaces is a lot like using classes and objects. Wrapping your Interface methods in a class would help give the user a consistent experience. I liked your example Yeah, except for that tiny little issue about it relying on a particular feature that cannot exist in a dataflow language. However, I did redesign it around by ref objects and last night I got it working correctly. I'll try to get it posted soon but it is more complicated than my previous design so I should probably put together some documentation for it. ...would be cool to put some collective effort into this. I've been poking around with Interfaces off-and-on for about a year now. I could write a book on what doesn't work! I'm glad I've spent the time doing it though... I've learned a lot of things that I wouldn't have encountered in job-related stuff. Quote Link to comment
Black Pearl Posted September 17, 2009 Report Share Posted September 17, 2009 Probably nothing in terms of functionality. In text languages using Interfaces is a lot like using classes and objects. Wrapping your Interface methods in a class would help give the user a consistent experience. My idea would be to use a facade design pattern to allow the user to cast a class to an interface. So all classes would inherite from the interfacable class, which have a method CastAsInterface (and maybe more like GetInterfaces and the like, some of them private...). The cast interface would then return the facade vi (just encapsulating the calls on the class that are part of the interface declaration), which holds the the object in its private data (this allows per value designs, I hope). I don't know if that really works, as I'm not yet into LVOOP. Felix Quote Link to comment
Daklu Posted September 17, 2009 Report Share Posted September 17, 2009 My idea would be to use a facade design pattern to allow the user to cast a class to an interface. I tried that. I mocked up a Baby class and a CellPhone class, both of which inherited from my Interfaceable class. Babies sleep and cell phones sleep, so I implemented an ISleepable class (which derived from my Interface class) to provide an alternate access point to their sleeping methods. Then I built ISleepableBaby and ISleepableCellPhone classes which implemented the interfaces for each class. Converting the main object (Baby & CellPhone) into their ISleepable counterparts is no problem. Converting them back into their main objects is a problem. Since the conversion method needs to be a member of the Interfaceable class it will an Interfaceable object, which then needs to be downcast by the interface user. I didn't like that--I'd rather put more responsibility on the interface developer in return for something that is easier for the user. The other issue is that method restricts users to only having access to a single interface at time. Since classes can implement many different interfaces I didn't want to force users to go through a conversion every time they need to switch interfaces. These are the main design goals I have been working towards with my Interface framework, roughly in priority order: Allow many-to-many relationships. A class can implement many different Interfaces, and an Interface can be implemented by many different classes. Maintain conceptual continuity with Interfaces in text-based languages. (To avoid "polymorphism" confusion.) Push the complexity as far upstream as possible. There are two kinds of users: Interface users and Interface developers. Neither should be burdened with building repetitive framework code. Converting existing classes into Interfaceable classes should be easy. Keep the framework lightweight and transparent. This is a redistributable code library, not a product. Minimize dependencies on other packages. Some of these I've been able to meet with my code; some still need to be worked on. I'll try to get my code posted later today. Quote Link to comment
Daklu Posted September 17, 2009 Report Share Posted September 17, 2009 Kurt, I noticed an unusual upcast to a non-default Labview Object. Why do you do that? Quote Link to comment
Aristos Queue Posted September 17, 2009 Report Share Posted September 17, 2009 Kurt, I noticed an unusual upcast to a non-default Labview Object. Why do you do that? He probably just copied the constant from somewhere... the effect won't be any different than if he used a regular LV Object constant -- the center input only cares about the type of the wire, not the value on the wire. Of course, depending upon the value of that constant, you might be creating a dependency on a class that is otherwise unused in the diagram. It'd be better to use a default-value constant. 1 Quote Link to comment
Kurt Friday Posted September 18, 2009 Author Report Share Posted September 18, 2009 Sorry to take my time getting back to you, I was out of the office all yesterday. I've never tried this. How does it fail? Does it just not dispatch to the correct child class VI? My original Idea was have a set of vi's called interfaces that have default object inputs, inside the interface vi I would determine the class it was acting on using Get LV Class Path.vi and then call the method in the class that has the same name as the interface. So if a DMM object was input to Interface.Read.vi then it would determine that it was acting on DMM and then call method DMM.lvclass:Read.vi. It was a simple idea to get something working but when you wire a CIN to a Dynamic Dispatch vi type then you get a block diagram error and the reason is "Call By Reference Node: Refers to a dynamic member VI. I looked at the LV help and it states "The Call By Reference Node does not support dynamic member VIs in this version of LabVIEW" So the other idea was to have it call a static member vi in the class called Interface that has the methods you want to include. Probably nothing in terms of functionality. In text languages using Interfaces is a lot like using classes and objects. Wrapping your Interface methods in a class would help give the user a consistent experience. The problem I can see with a class is that it then becomes typed and I wouldnt be able to wire in my DMM or Thermometer instance to the interface methods because they now belong to the Interface class. I like the idea of it belonging to a class for the reasons you mentioned. Yeah, except for that tiny little issue about it relying on a particular feature that cannot exist in a dataflow language. However, I did redesign it around by ref objects and last night I got it working correctly. I'll try to get it posted soon but it is more complicated than my previous design so I should probably put together some documentation for it. I've been poking around with Interfaces off-and-on for about a year now. I could write a book on what doesn't work! I'm glad I've spent the time doing it though... I've learned a lot of things that I wouldn't have encountered in job-related stuff. I'd love to see it when you have it all firing, sounds like you have the OOP bug. Kurt, I noticed an unusual upcast to a non-default Labview Object. Why do you do that? When I was building it I was just creating a constant from the default object terminal of the Interface.vi, I thought at the time why does it look non default but there were no issues or coercion, so I thought OK. But just trying it now I can't re-create the same constant, ie black cube, I get the expected white cube. He probably just copied the constant from somewhere... the effect won't be any different than if he used a regular LV Object constant -- the center input only cares about the type of the wire, not the value on the wire. Of course, depending upon the value of that constant, you might be creating a dependency on a class that is otherwise unused in the diagram. It'd be better to use a default-value constant. So it could have been due to creating a constant from a default LV Object that contained non default data? Quote Link to comment
Daklu Posted September 19, 2009 Report Share Posted September 19, 2009 (edited) I'd love to see it when you have it all firing, sounds like you have the OOP bug.Yeah, I drank the kool-aid a couple years ago. The biggest benefit I get out of it isn't dynamic dispatching, it's the encapsulation. My code is much cleaner when I'm using classes. I'm more aware of when I'm linking things that shouldn't be linked and I'll stop and do it "right." (Well, try to do it right anyway.) I've really been having a lot of fun exploring what I can and can't do with LOOP.I was going to post my code yesterday but I've been having problems with a package dependency. VIPM hasn't been showing it as available for installation. So it could have been due to creating a constant from a default LV Object that contained non default data? Almost. The fp control that you created the constant from has to have non-default data set as its default value. That object block contains a DMM object. If you ran the VI, selected Edit -> Make Current Values Default during your testing, and then created the constant from the output terminal that would have done it. I've just recently started using non-default class constants on purpose. I've found it an effective way to eliminate the requirement for a 'Create New' method in certain cases. ...you might be creating a dependency on a class that is otherwise unused in the diagram. That's an important gotcha I hadn't thought of. I'll have to keep my eyes open for that one. Thanks for the tip. Edited September 19, 2009 by Daklu Quote Link to comment
Daklu Posted September 23, 2009 Report Share Posted September 23, 2009 Here's an object diagram of my current Interface framework. Aside from some comments in the demo vi this is all the documentation I have so far. I am curious what the pain points are for others who pick up the framework and try to implement Interfaces in their own code. (Or for that matter, those who just try to understand it!) Some points to note: The Interface framework classes are highlighted in green. Everything else is extra baggage for dev work. The framework currently depends on the CollectionFramework package, highlighted in orange. Eventually I'll refactor the functionality I need into the InterfaceCollection class and remove that dependency. For now the CollectionFramework package is included in the zip file as an OpenG package. They will show up under your User palette after installation. The blue classes have been implemented for demonstration purposes. The red classes are pending implementation to fill out other demonstration scenarios. LV2009 required. Since the language of Interfaces is undefined in Labview, trying to describe things in text is difficult. I have terminology I use but sharing it right now would confuse the issue since it's not quite consistent with the diagram or the demo project. [Edit Aug 3, 2010 - Removed pre-release version. Get current version from the Code Repository.] 1 Quote Link to comment
K-node Posted September 23, 2009 Report Share Posted September 23, 2009 I nosed around this for a little bit and I like it. Nice work. I would prefer, as a developer using this framework, not to have to have the ImplementationCollection as part of my class. I would prefer this on the Interfaceable class. I realize that you would need to change the Interface class to store a more general (but common) base class as the Target ObjRef. You could use the LabVIEW Object or create a new common base class. How does the ImplementationCollection get populated? I am restricted by business rules on what I can install on my laptop, so the OpenG stuff was out of the question. I replaced the references to this using a string array for the keys and an Interfaceable object array for the items. But the list is empty when I finally try to get the named interface in the BabyDemo vi. Not sure what I broke. Perhaps you can help there. In the mean time I simply added the ISleepableBaby object to the ImplementationCollection of Baby in the _GetInterfaces method I had to override. I kinda like your _CreateDVR method. As I get more and more properties I might find it a pain and would prefer to resort to a Create method that is called once - but then from the User perspective it is one less thing to remember to call as the User instantiates these things. Is there a reason that the Interface Class holds a DVR to its target and not just an Interfaceable object as the target? I will probably have more thoughts, but overall, well done. Thanks for sharing this with us. Quote Link to comment
Kurt Friday Posted September 24, 2009 Author Report Share Posted September 24, 2009 Hey Dak, good stuff. At first it was a bit like staring into the sun, but now I see it, very cool I like how your framework is implementable, all you need to do is have your desired class inherit from the Interfacable class, and then build your Interface class and then make the desired class a friend of the Interface class. I noticed that in your example that Baby is a friend of ISleepableBaby which made sense but I didn't understand why ISleepableBaby:Wake.vi is a friend of Baby. I'm still a bit sketchy on friends however your framework points to a really good use case. I'm still working through your demo, its a substantial piece of work, I'm hoping to test drive it a bit on some of my demo classes. Quote Link to comment
Daklu Posted September 24, 2009 Report Share Posted September 24, 2009 I would prefer, as a developer using this framework, not to have to have the ImplementationCollection as part of my class. I would prefer this on the Interfaceable class. You mean the InterfaceCollection class? This was a design decision I made in an attempt to keep things as simple as possible for class users at the cost of a little more complexity for the class developer. I did a lot of experimenting with putting the InterfaceCollection in the Interfaceable class. There are two main reasons why I ended up rejecting it. The main reason is that if the InterfaceCollection is in the Interfaceable class, child classes would need to implement initialization code to populate the parent object with the correct Interfaces before the class would function correctly. I don't like building classes that require Init VIs. I find them counter-intuitive in Labview and until NI provides us with customizable constructors self-invoked run time initialization routines I'll do my best to avoid them. Also, Init vi requirements tend to propogate down through all descendent classes. If a parent class requires one, chances are the child classes will need them too. I wasn't too thrilled about putting an Init requirement at the base of what could potentially become a very large hierarchy. The second reason is Interfaces are not intended to be dynamically assigned at run-time. The set of Interfaces a class supports is determined and fixed during design. You cannot add support for new Interfaces to a class without modifying its source code. (This restriction maintains continuity with how Interfaces are implemented in other languages.) Since an Interfaceable class exposes a pre-determined set of Interfaces, it's much easier in the long run to store the InterfaceImplementation objects (ISleepableBaby, etc.) with the Interfaceable class they are designed to work with. How does the ImplementationCollection get populated? On a scratch block diagram I add the appropriate InterfaceImplementation objects to an InterfaceCollection using the Add method. Then I make sure I wire an indicator to the output terminal of the last Add method. Run the VI. Convert the indicator to a constant, right click on the front panel indicator, select 'Make current values default,' and drag it over to my Interfaceable object's private data cluster. Done. I might put together a little tool that automates the process but I'm not convinced it's necessary. I am restricted by business rules on what I can install on my laptop, so the OpenG stuff was out of the question. You're in luck. No OpenG stuff is in there. You do need VIPM to install the package. (That's not OpenG though... it's a reusable code management application.) I replaced the references to this using a string array for the keys and an Interfaceable object array for the items. But the list is empty when I finally try to get the named interface in the BabyDemo vi. Not sure what I broke. Perhaps you can help there. That's exactly what I was going to do when I mentioned refactoring out the CollectionFramework package. All you need to do is create an InterfaceCollection object with non-default values like I described above and replace the object in the Baby class. I kinda like your _CreateDVR method. As I get more and more properties I might find it a pain and would prefer to resort to a Create method that is called once - but then from the User perspective it is one less thing to remember to call as the User instantiates these things. _CreateDVR is a compromise. I'd prefer constructors self-invoked run time initialization routines, but alas, we don't have them and it's doubtful we ever will. Create methods do only need to be called once, but then you also need to create error handling in case the user tries to call a method without initializing the object. It's a trade off. The complexity doesn't really go away; it just gets pushed off on a different developer. There's certainly nothing in the framework that requires one or the other. Is there a reason that the Interface Class holds a DVR to its target and not just an Interfaceable object as the target? Yep. Erm... wait... maybe not. I know I struggled with circular dependencies for a long time. (Labview doesn't allow them.) The DVR might just be a remnant from all the different things I tried. Or it might be required to break the circularity. To be honest I don't remember right now--it's late here and my brain is a little fried. I'll have to re-examine the code to see if I can figure it out. I will probably have more thoughts, but overall, well done. Thanks for sharing this with us. Thanks for looking it over. The more eyes we can get on it the better it will be. I honestly believe Interfaces in Labview will open up a whole range of possibilities that have previously been difficult or impossible to achieve. I'm not trying to toot my own horn; I'm just hoping others see the potential and get as excited about it as I am. I'm open to all feedback, positive and negative. I have been planning on posting the code in the repository if enough people expressed interest. Kurt mentioned a collective effort towards developing a common framework. I'm all for that too. I'd rather work towards a concensus than impose my vision on others. Quote Link to comment
Daklu Posted September 24, 2009 Report Share Posted September 24, 2009 (edited) Hey Dak, good stuff. At first it was a bit like staring into the sun, but now I see it, very cool I like how your framework is implementable, all you need to do is have your desired class inherit from the Interfacable class, and then build your Interface class and then make the desired class a friend of the Interface class. I noticed that in your example that Baby is a friend of ISleepableBaby which made sense but I didn't understand why ISleepableBaby:Wake.vi is a friend of Baby. I'm still a bit sketchy on friends however your framework points to a really good use case. I'm still working through your demo, its a substantial piece of work, I'm hoping to test drive it a bit on some of my demo classes. Thanks. A couple quick comments--I'm half asleep so hopefully they'll be coherent. -The only reason ISleepableBaby:Wake is a friend of Baby is so the Interface can access the Baby:WakeUp method. Baby:WakeUp is implemented as a way to wake up the baby without pinching her and making her cry. By giving the method community scope the only way to wake up the baby without pinching her is via the ISleepable interface. This case is trivial--it's intended to show how a developer can require certain functions be called through an Interface. -Baby shouldn't be a friend of ISleepableBaby. That's a mistake. You only make other classes or methods your friends if you want to give them access to your community scoped methods. Convential wisdom is that Interfaces should not have private or community methods, so there's no need for them to make any other classes friends. Maybe in the long run there will be a valid reason for having private or community methods in Interfaces. I haven't seen one yet though. ------------ G'night, and I expect you to have solved the remaining problems by the time I wake up tomorrow. Edited September 24, 2009 by Daklu Quote Link to comment
Aristos Queue Posted September 24, 2009 Report Share Posted September 24, 2009 Maybe in the long run there will be a valid reason for having private or community methods in Interfaces. Private methods on interfaces can implement parts of the interface and are quite useful. Quote Link to comment
Daklu Posted September 25, 2009 Report Share Posted September 25, 2009 (edited) Private methods on interfaces can implement parts of the interface and are quite useful. I don't see it. Interfaces, such as ISleepable, have no knowledge of the classes that will eventually expose them. How would you design a private method to do work on an object without knowing what the object is capable of? If you're talking about the Interface Implementations then I fully agree with you. Edit: Hmm... I see I wasn't very clear about differentiating between Interfaces and Interface Implementations in my previous post. Edited September 25, 2009 by Daklu Quote Link to comment
Aristos Queue Posted September 25, 2009 Report Share Posted September 25, 2009 I don't see it. Interfaces, such as ISleepable, have no knowledge of the classes that will eventually expose them. How would you design a private method to do work on an object without knowing what the object is capable of? If you're talking about the Interface Implementations then I fully agree with you. There are interfaces that have methods that are combinations of several other methods on the interface. An interface might expose a DoThis and a DoThat method and a third method that takes a boolean parameter called DoOneOrTheOther. Just an example. In that case the interface still doesn't have any knowledge of the particular class, but it does provide an algorithm as part of itself that takes advantage of the rest of the interface.Which brings me to another thought I've been having about interfaces... the possibility that an interface could declare a private method and expose a public method that uses that private method as part of an algorithm. There are cases where I, as the person implementing the interface, need to write some function that the class must expose in order to plug into some algorithm, but the function itself is one that should never be called except by the algorithm. I know... I'm questioning all sorts of basic assumptions about "what constitutes an interface." But that's my job as a language designer. Just because it works like XYZ in C# or Java doesn't mean that it is how it should be implemented, either in languages in general or in LV specifically. This is a topic on which I'll be playing for a long while yet. 1 Quote Link to comment
Daklu Posted September 25, 2009 Report Share Posted September 25, 2009 There are interfaces that have methods that are combinations of several other methods on the interface. An interface might expose a DoThis and a DoThat method and a third method that takes a boolean parameter called DoOneOrTheOther. Just an example. In that case the interface still doesn't have any knowledge of the particular class, but it does provide an algorithm as part of itself that takes advantage of the rest of the interface. I suppose that could be valid, though I don't think I would design an Interface that makes calls to itself. When I start implementing vertical api methods the code that uses it tends to get more complicated. I have much more success when I keep all my methods for a given api flat and on the same level of complexity. If I need something more complex than what is provided I prefer to either wrap it in a subvi in the client code or build a higher level api on top of the first. Which brings me to another thought I've been having about interfaces... the possibility that an interface could declare a private method and expose a public method that uses that private method as part of an algorithm. There are cases where I, as the person implementing the interface, need to write some function that the class must expose in order to plug into some algorithm, but the function itself is one that should never be called except by the algorithm. This is rather vague and cryptic, but is this something that could be addressed with a relationship-based community scope? Something that allows you to specify exactly which VIs in one class can be called by which VIs in another class, rather than allowing all Friend VIs permission to call any community scoped class VI? I'm not saying that's a good solution... just trying to understand the scenario you're describing. I know... I'm questioning all sorts of basic assumptions about "what constitutes an interface." But that's my job as a language designer. Just because it works like XYZ in C# or Java doesn't mean that it is how it should be implemented, either in languages in general or in LV specifically. Agreed. Me and my little brain tried to stick as close to traditional Interfaces as possible. Not out of loyalty to the concept, but because it seems to be a well-proven design and I have no idea what the long-term consequences of breaking those rules would be. Still, I have asked myself many times what an Interface in Labview would look like and how would it behave? How does the principle "code to an interface, not an implementation" apply to Labview, where everything is an implementation? Quote Link to comment
Daklu Posted October 1, 2009 Report Share Posted October 1, 2009 Here's an updated version of the Interface Framework. Changelist: Refactored out the dependency on the Collection Framework. The Interface Framework is now self sufficient. Renamed "Interface" class to "IUnknown" to help avoid confusion. Renamed "InterfaceCollection" class to "InterfaceBag," since it no longer derives from the Collection Framework. IUnknown now contains an Interfaceable object rather than a DVR to an Interfaceable object. (Thanks kugr!) Removed friend relationship between Baby and ISleepableBaby. (Thanks SciWare!) Added several UML diagrams to the documentation. (Use Star UML to view the core document.) Still trying to figure out a better name for the Interfaceable class. I'm open to suggestions... A few other minor things, but I've forgotten what they were. A couple notes on terminology: In COM and .Net when an object supports a given Interface it is said that it implements the Interface. This makes sense in those languages; the code that executes when an Interface method is called is implemented directly in the class. That terminology doesn't make sense in this framework since each Interface is implemented in its own class rather than as part of the core code of the target class. I prefer to say an object exposes an Interface. Sometimes I'll also say an object has an Interface or supports an Interface, or an Interface is implemented for an object. I'll typically capitalize the word "Interface" when referring to the interface classes and objects described in this framework. I'll leave it lower case when using the word "interface" in the general meaning. "Interfaceable" with a capital 'I' refers to the proper noun, the Interfaceable class. When it is not capitalized, "interfaceable" is an adjective describing a class or object that derives from the Interfaceable class. These are the main diagrams I've included in the documentation. Hopefully they help explain how the framework interacts with the classes that are built on it. Other than vi context help I have not yet developed documentation on steps for class developers to take to implement classes based on the framework. In this framework any class that exposes an Interface must inherit directly or indirectly from the Interfaceable class. All Interfaces must inherit directly or indirectly from IUnknown. InterfaceBag is simply a helper object that holds all the Interfaces an interfaceable object supports. This diagram shows the relationship between the framework and the three classes (at minimum) that must be implemented when using the framework. Interfaces are designed to be reused so usually the Interface already exists, leaving the class developer with only two classes to implement. Classes that inherit from Interfaceable override the _GetInterfaces method, returning an InterfaceBag which contains all the concrete Interfaces the class exposes. How the InterfaceBag gets populated with concrete Interfaces is up to the class developer. Two common methods are by initializing the InterfaceBag in a Create method or by adding the concrete Interfaces to the InterfaceBag at design time and making those values the default. This shows the implementation of the Baby class in the attached example. It has one added complexity over the generic implementation; the Baby class has made the ISleepableBaby class a friend, which allows Baby to expose methods that are only accessable through the ISleepable Interface. This diagram shows the sequence of calls that take place when a class user calls the GetInterface method on an interfaceable object. Note that the class developer only needs to implement the _GetInterfaces method in their class. The rest of the work is handled by the framework. Here is a sequence of calls made when a class user calls a method exposed by an Interface. As expected, the class developer has slightly more work here in deciding exactly how the Interface method applies to this specific interfaceable class. ------------------------------------- What can you do with Interfaces? That's a little like asking what can you do with Play-doh. Undoubtedly there are lots of ways to use them I haven't though of. Here are a few ideas I've been bouncing around: - I really like Kurt's Active Object pattern. It might be worthwhile to create an IActiveObject interface. Still thinking about use cases on this... - I also like using the Observer pattern to separate program logic from the UI. Maybe an IObservable interface? (Though Event Refnums being strictly typed presents some difficulties.) - IPublisher? ISubscriber? - .Net documentation also provides some hints on potential uses. ISerializable? IPersistToFile? - Or getting back to what started me on this journey in the first place, IMeasureVoltage? IMeasureCurrent? [Edit Aug 3, 2010 - Removed pre-release version. Get current version from the Code Repository.] 2 Quote Link to comment
Daklu Posted October 3, 2009 Report Share Posted October 3, 2009 (edited) Minor update: v0.9.1 I've changed the implementation for Baby:_GetInterfaceBag.vi. Previously I had created an InterfaceBag object at design time that contained the concrete interfaces the class exposes, set it as the default value, and stored it in Baby's private data. This implementation works but it can be cumbersome during development when things are changing frequently. Changes to the concrete interface tend to break the InterfaceBag. The new implementation simply creates the InterfaceBag object at runtime every time _GetInterfaceBag.vi is called. Since the concrete interfaces are stateless while in the InterfaceBag there is no need to carry them around with the class data. This simplifies the dev work for the class designer and is easier to understand. Ultimately there are lots of ways _GetInterfaceBag.vi can be implemented. The only thing that matters to the framework is that it returns an InterfaceBag object that contains all the concrete interfaces the class exposes. Edited October 3, 2009 by Daklu Quote Link to comment
K-node Posted October 13, 2009 Report Share Posted October 13, 2009 Minor update: v0.9.1 I did not see the attachment, but the changes were straight forward to implement. Ultimately there are lots of ways _GetInterfaceBag.vi can be implemented. The only thing that matters to the framework is that it returns an InterfaceBag object that contains all the concrete interfaces the class exposes. I actualy removed creating the interface bag at the _GetInterfaces method returning just the list. It is up to Interfaceable.GetInterface to populate the bag or even just work off the obtained list since the _GetName can be called for each Interface. Summarizing your suggestions from your "sibling search" post ( http://lavag.org/topic/11186-ideas-for-an-issibling-algorithm/), I think you were suggesting that the GetInterface method accept an object derived from IUnknown instead of using the name since all IUnknown derived objects must implement the _GetName method. Is that what you suggested? I like it - saves typing (and typos)! And also you were saying to use the PreserveRuntimeClass function as opposed to the ToMoreSpecific class after obtaining an interface from GetInterface. Am I correct? I have to reread what AQ said about those two functions. So, now for the question... Let's say you have a SmartCellPhone class that derives off of CellPhone. Would you expect it to implement the ISleepable interface, or utilize the CellPhone class capability? This may be a more fundamental question on interfaces. I have implemented interfaces, but I have never derived a class that had an interface (or at least did anything about it if it had one). Thanks for sharing this framework. Quote Link to comment
Daklu Posted October 13, 2009 Report Share Posted October 13, 2009 (edited) I did not see the attachment, but the changes were straight forward to implement. No attachment this time... I'm running low on server space and haven't purchased a premium membership yet. I've been too busy to look at this for the past week and as far as I'm concerned that's ancient history. Hopefully I haven't forgotten everything.... I actualy removed creating the interface bag at the _GetInterfaces method returning just the list. It is up to Interfaceable.GetInterface to populate the bag or even just work off the obtained list since the _GetName can be called for each Interface. Good point. As a matter of fact the InterfaceBag might be completely superfluous at this point. I suppose it could be replaced by a private data array containing the concrete interfaces a class supports. I'll have to think about the tradeoffs for a bit... I think you were suggesting that the GetInterface method accept an object derived from IUnknown instead of using the name since all IUnknown derived objects must implement the _GetName method. Is that what you suggested? I like it - saves typing (and typos)! Yep, that's what I was saying. I'd prefer to do direct type comparisons rather than rely on arbitrary string values; there's always a chance two different Interfaces will return the same string value. And also you were saying to use the PreserveRuntimeClass function as opposed to the ToMoreSpecific class after obtaining an interface from GetInterface. Am I correct? I have to reread what AQ said about those two functions. Very close. The PreserveRuntimeClass prim would be inside Interfaceable:GetInterfaces, meaning the Interface is returned to the class user correctly typed and the user doesn't have to use either prim to downcast to a specific Interface. So, now for the question... Let's say you have a SmartCellPhone class that derives off of CellPhone. Would you expect it to implement the ISleepable interface, or utilize the CellPhone class capability? This may be a more fundamental question on interfaces. Depends on the classes being discussed. SmartCellPhone objects will return an ISleepable interface, so the class designer needs to decide if the ISleepableCellPhone methods are appropriate in the context of SmartCellPhone objects. If not, they can override them with new implementations. I have some questions regarding using interfaces that anyone is free to respond to: Assuming GetInterface takes an Interface object as an input instead of the Interface's name as an input, is it useful to have a GetNames method that returns a string array of Interface names that an object supports? The more I think about it the more I have a hard time coming up with a valid run time use case for it. Is a GetInterfaces method that returns an array of supported concrete interfaces useful at runtime? The only use case I can think of for this is to check an interfaceable object to see if it supports a given Interface, and I think a better implementation is to simply raise an error in GetInterface if the class does not support that Interface. Edited October 13, 2009 by Daklu Quote Link to comment
K-node Posted October 13, 2009 Report Share Posted October 13, 2009 The PreserveRuntimeClass prim would be inside Interfaceable:GetInterfaces, meaning the Interface is returned to the class user correctly typed and the user doesn't have to use either prim to downcast to a specific Interface. Ah, thanks. Now I see how it works. It seems I have some retrofitting to do in my project. I have some questions regarding using interfaces that anyone is free to respond to: Assuming GetInterface takes an Interface object as an input instead of the Interface's name as an input, is it useful to have a GetNames method that returns a string array of Interface names that an object supports? The more I think about it the more I have a hard time coming up with a valid run time use case for it. Is a GetInterfaces method that returns an array of supported concrete interfaces useful at runtime? The only use case I can think of for this is to check an interfaceable object to see if it supports a given Interface, and I think a better implementation is to simply raise an error in GetInterface if the class does not support that Interface. For #1, I have implemented this approach and GetNames is no longer used. I removed the InterfaceBag class too actually. In the GetInterface method, since you are asking for an interface (ISleepable) and not the implementation (ISleepableBaby) you cannot do a direct comparison, I had to use a PRT. If successful, then the implementation (ISleepableBaby wired from PRT's object out value) is returned. For #2, I do not see how I would get the implementation given the interface without obtaining a list of concrete implementations to compare against (or an equivalent call to the Interfaceable object). The only entity that knows what it implements is the particular Interfaceable object. Maybe I missed something. Quote Link to comment
Daklu Posted October 14, 2009 Report Share Posted October 14, 2009 Updated to v0.10.0 [Note - Public api has changed. Backwards compatibility is broken.] -Removed the InterfaceBag from the framework. Interfaceable classes now override Interfaceable:_GetInterfaces and simply return an array containing the concrete interfaces that have been implemented for that class. -Changed the concrete interface retrieval mechanism from a string-based 'name' comparison to using the Preserve Run-Time Class primitive. -Removed all name properties from IUnknown. -Updated BabyDemo to reflect new public api. -Created some new UML diagrams to reflect the new design. [Edit Aug 3, 2010 - Removed pre-release version. Get current version from the Code Repository.] The change to the public api involves the inputs to Interfaceable:GetInterface. Instead of using the name of the Interface you simply wire in an object of the Interface you want to retrieve. I'm curious how my changes compare to yours kugr. For #1, I have implemented this approach and GetNames is no longer used. I removed the InterfaceBag class too actually. In the GetInterface method, since you are asking for an interface (ISleepable) and not the implementation (ISleepableBaby) you cannot do a direct comparison, I had to use a PRT. If successful, then the implementation (ISleepableBaby wired from PRT's object out value) is returned. For #2, I do not see how I would get the implementation given the interface without obtaining a list of concrete implementations to compare against (or an equivalent call to the Interfaceable object). The only entity that knows what it implements is the particular Interfaceable object. Maybe I missed something. These are questions for the class users, not the class developer. When using a class that exposes various interfaces, is there value in obtaining a list of names of Interfaces the class supports or an array of all the concrete interface objects? My gut says no. What do you think? I think I saw a comment from you somewhere indicating you are using this framework or a variation of it in real code, which is more than I've done. If so, you have more real-world experience than anyone else in the whole world with Labview Interfaces. Since you're the expert... what do you think? I'm really liking the simplified framework but I'll add the names back in if anyone can convince me of their usefulness. As I was working on this tonight I got to thinking again about using interfaces with by-val classes. When I looked at that previously I was never quite happy with what I had to do to recover the original object from the Interface. The PRT trick helps with that, though I would prefer a RecoverObject method smart enough that I wouldn't have to wire an object into that method to tell LV what to cast it into. I wonder how far up the wire the PRT prim looks for a valid object type? Quote Link to comment
Daklu Posted October 19, 2009 Report Share Posted October 19, 2009 As I was working on this tonight I got to thinking again about using interfaces with by-val classes. When I looked at that previously I was never quite happy with what I had to do to recover the original object from the Interface. The PRT trick helps with that, though I would prefer a RecoverObject method smart enough that I wouldn't have to wire an object into that method to tell LV what to cast it into. I wonder how far up the wire the PRT prim looks for a valid object type? I made a few changes over the weekend to the Interface model. I've gone back to the idea of 'casting' an interfaceable object into an interface, operating on it, then 'casting' it back into the original object. Internally the functionality is pretty much the same but by using this model it becomes possible to create interfaces for by-val objects. It also makes the class user branch the wire before getting an interface from an object rather than hiding the branch inside Interfaceable:GetInterface. I believe this improves code readability. Code can be found in the code repository. Quote Link to comment
Stobber Posted July 29, 2010 Report Share Posted July 29, 2010 Hi Daklu - I've been lurking on the LAVA forums without an account for a year or so, just poking my head in to learn some specific tidbit when I couldn't find the answer quickly on the NI pages. I decided at some point that my extremely limited knowledge of OOP programming -- I was raised a BSEE and kind of meandered over toward software development as my career grew -- is only holding me back. So I went off and bought a copy of Head First Design Patterns. I read the first chapter, sat down in front of my laptop with LV2009 eagerly loading, and got ready to implement the Strategy pattern from the first chapter in the book! "Oh.....wait....where do I find the interfaces in here?" So to cut this story short, I found this thread, read through it all twice, downloaded the latest copy of the framework, got a teammate who really knows LVOOP to explain it to me, read through parts of the thread again, and now I think I mostly understand how your nifty framework functions. I intend to use it on the SimUDuck example from the book in just a minute, but I have one nagging question about your class structure in the Baby Demo VI. I understand why you put your interfaceable classes' private data (Baby and CellPhone) inside a DVR that gets carried by the classes: this makes them behave like reference-based classes, which is handy when the wire has to be split to allow both the Interface methods and the interfaceable class methods to act on that data. But -- and forgive me for using my inaugural post to challenge a revered poster -- but doesn't that fundamentally change the behavior of the class wire on the client diagram? Instead of copying its private data when it is branched, as NI R&D designed it to do, it now acts as a reference to that data so the data isn't copied. It seems like any client developer who uses these interfaceable classes will have to know that they don't behave like normal classes do on the diagram. ...I suppose the alternative would be to wrap the class wire in a DVR and pass that around between methods, but then you lose dynamic dispatching and the whole thing kind of falls apart. I don't know the answer here, just thought I'd ask the question to make sure my understanding is correct. This looks like a very cool workaround for interfaces, and I'd like to start using it in my projects if I can. Quote Link to comment
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.