Forum Index
HomeZBasic Home   Forum RulesForum Rules   Forum FAQForum FAQ   MemberlistMemberlist   UsergroupsUsergroups   RSS FeedRSS Feed
Site SearchSite Search   LinksLinks   DownloadDownload   Digests and SubscriptionsDigests and Subscriptions
ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in   RegisterRegister
Object Oriented design of interfaces

 
Post new topic   Reply to topic    Forum Index -> ZBasic Language
Author Message
kranenborg



Joined: 27 Jul 2009
Posts: 37
Location: Uppsala, Sweden

Posted: 16 February 2010, 14:28 PM    Post subject: Object Oriented design of interfaces Reply with quote

Hello,

The following problem is presented in a somewhat abstract way but applies to the issue of using interfaces in an application program. I like the clarity of OO programming but I am a lttle bit lost in getting to the final solution.

Say we have a simple application that reads and stores sensor data in a circular buffer. There are two types of similar sensors (giving values between 0.0 and 1.0) with mainly the same characteristics and requirements (sensor readings only valid if their read value is larger than 0.3, otherwise a new reading needs to be done), they differ only in the way they can be communicated with (SPI and I2C). Thus it seems appropriate to define an abstract class in which a common interface is described and/or defined, as well as two final subclasses which implement the SPI and I2C variants (identical for this discussion):

Code:

Abstract Class SensorInterface_Def
   ' ----------------------------------------
   Public:
   '   Function SensorReading As Single
   '   End Function
   ' ........................................
   Protected:
      Dim SensorMargin as Single = 0.3
   ' ........................................
   Private:
   ' ----------------------------------------
End Class


Final Class SensorInterface_I2C Extends SensorInterface_Def
   ' ----------------------------------------
   Public Function SensorReading() As Single
      Do
         SensorReading = Rnd()
      Loop While (SensorReading < SensorMargin)
   End Function
   ' ----------------------------------------
End Class


Final Class SensorInterface_SPI Extends SensorInterface_Def
   ' ----------------------------------------
   Public Function SensorReading() As Single
      Do
         SensorReading = Rnd()
      Loop While (SensorReading < SensorMargin)
   End Function
   ' ----------------------------------------
End Class


Using one of the two final classes as a mixin for the main class that reads and stores the data in the buffer we get at a simple example application, in this case when including the SPI variant (it is the SensorReading method that is provided by the particular mixin):

Code:


Class SensorData Includes SensorInterface_SPI
   ' ----------------------------------------   
   Public:
      Function PerformReading() As Single
         SensorData(DataIndex) = SensorReading
         PerformReading = SensorData(DataIndex)
         DataIndex = DataIndex + 1
      End Function
   ' ........................................      
   Protected:
   ' ........................................
   Private:
      Dim SensorData (0 to 255) As Single
      Dim DataIndex As Byte
   ' ----------------------------------------   
End Class


Dim MySensorData As SensorData

Sub Main()
   Do
      Console.Write("Sensor reading: " & CStr(MySensorData.PerformReading))
      Delay(1.0)
   Loop
End Sub



This works well if you know at compile time which interface is to be used since then the class after the Include statement is the mixin used.

However the issue is what to do if you want to decide at run-time which interface is to be used (in particular when all of the classes should be part of a general-purpose library where the decision of which interface is to be used is taken only in the user program). You may actually want to use both interfaces in the same program: you can create objects for both Final Classes. Somehow you would like to pass these objects (i.e. interfaces) to the PerformReading method in the SensorData class, but I do not see how to do it. Any suggestions here?

/Jurjen


Last edited by kranenborg on 18 February 2010, 23:39 PM; edited 2 times in total
Back to top
kranenborg



Joined: 27 Jul 2009
Posts: 37
Location: Uppsala, Sweden

Posted: 18 February 2010, 22:57 PM    Post subject: Reply with quote

I managed to find a proper solution myself, by defining abstract methods as well and referring to their type in other classes, avoiding any reference to implementation subclasses. The interface implementation is now imported as a parameter at run-time while the parameter type is the base-class type known at compile time. No mixin is used anymore (although the solution I have here was motivated by the wikipedia definition of a mixin, in particular the part on interface definition/implementation).

The solution below allows a specific sensor interface implementation to be used in the main user program at run-time, while the application class (SensorData) that uses the interface only needs to use its base (abstract) class method definition at compile time, thus completely abstracting from the specific sensor interface implementation. This promotes large class libraries to be built (optionally including all possible interface implementations as well, like in this example) where the decision which interface to use is determined by the user application program at run-time.

Code:


Option Objects
Option TargetCPU ZX328l


Abstract Class SensorInterface_Def
   ' Defines a formal sensor interface spec by defining the method interfaces
   ' of the methods that need to be implemented by all of its subclasses.
   ' The methods themselves are defined here as abstract methods such that
   ' other classes can refer to this base class regardless of the method
   ' implementation in its subclasses.
   ' ----------------------------------------
   Public:
      Abstract Function SensorReading() As Single
      End Function
   ' ........................................
   Protected:
      Dim SensorMargin as Single = 0.3
   ' ........................................
   Private:
   ' ----------------------------------------
End Class


Final Class SensorInterface_I2C Extends SensorInterface_Def
   ' I2C-specific implementation (subclass) of SensorInterface_Def
   ' providing a specific implementation of method SensorReading
   ' ----------------------------------------
   Public Function SensorReading() As Single
      Do
         SensorReading = Rnd()
      Loop While (SensorReading < SensorMargin)
   End Function
   ' ----------------------------------------
End Class


Final Class SensorInterface_SPI Extends SensorInterface_Def
   ' SPI-specific implementation (subclass) of SensorInterface_Def
   ' providing a specific implementation of method SensorReading
   ' ----------------------------------------
   Public Function SensorReading() As Single
      Do
         SensorReading = 3.141
      Loop While (SensorReading < SensorMargin)
   End Function
   ' ----------------------------------------
End Class


Class SensorData
   ' Application class. Has a method that itself imports one of the interface
   ' implementations as an argument at run-time while only requiring the argument's base (abstract) class type
   ' in its definition at compile time.
   ' Thus this application (library) class can be defined using the interface spec only, independent
   ' of the interface implementation.
   ' ----------------------------------------   
   Public:
      Function PerformReading(ByRef intf As SensorInterface_Def) As Single
         SensorData(DataIndex) = intf.SensorReading
         PerformReading = SensorData(DataIndex)
         DataIndex = DataIndex + 1
      End Function
   ' ........................................      
   Protected:
   ' ........................................
   Private:
      Dim SensorData (0 to 255) As Single
      Dim DataIndex As Byte
   ' ----------------------------------------   
End Class

' ============================================
' Main user program

Dim MySensorData As SensorData
Dim MyActualSPIsensor As SensorInterface_SPI   ' Specific sensor implementation

Sub Main()
   Do
      ' Use a specific sensor interface (SPI in this case) as an argument
      Console.WriteLine("Sensor reading: " & CStr(MySensorData.PerformReading(MyActualSPIsensor)))
      Delay(1.0)
   Loop
End Sub




Regards,
Jurjen
Back to top
dkinzer
Site Admin


Joined: 03 Sep 2005
Posts: 2499
Location: Portland, OR

Posted: 20 February 2010, 0:29 AM    Post subject: Reply with quote

kranenborg wrote:
I managed to find a proper solution myself [...]
Well done.

After considering it for a while I thought about using a member that is a reference to the desired interface object, the address of which could be set at object creation time or later if desired. However, some of the internal type checking in the current release of the compiler is too restrictive to allow this strategy to be implemented. The necessary changes in the compiler are fairly simple so this may be an option in a near-future release.
Back to top
kranenborg



Joined: 27 Jul 2009
Posts: 37
Location: Uppsala, Sweden

Posted: 21 February 2010, 18:41 PM    Post subject: Reply with quote

I have noted that the compiler still generates code for class SensorInterface_I2C although no object for this class is instantiated in the user program nor is this particular class used by any of the other classes: is this missed somehow by the compiler?

One could actually consider different code generation scenarios for the above program, depending on what the meaning of the program is. If the program is to be regarded as a finished program then no code for class SensorInterface_I2C should be generated. However if one considers all classes of the program as a library, then code should be generated for all classes including the unused ones (as has happened here). Maybe some compiler directives would be useful that indicate the begin and end of the code block that should be compiled, irrespective of instantiation of parts of it?

/Jurjen
Back to top
dkinzer
Site Admin


Joined: 03 Sep 2005
Posts: 2499
Location: Portland, OR

Posted: 21 February 2010, 21:12 PM    Post subject: Reply with quote

kranenborg wrote:
[I]s this missed somehow by the compiler?
Yes. It is a side effect of the way that the virtual function tables are built internally. I believe that we can modify the algorithm to avoid including unused classes.
Back to top
Display posts from previous:   
Post new topic   Reply to topic    Forum Index -> ZBasic Language Time synchro. with the server - Timezone/DST with your computer
Page 1 of 1

 


All content Copyright © 2005-2012 Elba Corp. All Rights Reserved.
Opinions expressed in posts are those of the author and not necessarily those of Elba Corp.
Powered by phpBB © 2001, 2005 phpBB Group