|
|
| 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 |
|
|
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: |
|
|
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: |
|
|
| 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: |
|
|
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: |
|
|
| 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 |
|
 |
|