B4J Question Best practice on oo design

Will Castillo

Member
Licensed User
Hello folks,

I "discovered" R4X last Saturday and I've been having so much fun with it. I worked on Basic for more years that I can remember, learning it in mid 80s using GW-Basic, then using Turbo Basic since ~1988 as a hobby. In early 90s I met Visual Basic 1 but it sucked very badly, went back to Turbo Basic. Years later, in 1994, I started working professionally in VB3 for about four years and then jumped to VB6 until 2005 (although managed projects on it till mid 2011.) I've not been related to any basic-based project since then.

All that said, just to mention that R4X brings so much nostalgia to me. Thanks to the developer who made it possible!

Ok.. my question...

=======

I'm trying to create a design similar to the following, but I'm not quite sure what are the best practices, or the best approach, in B4J. Any help will be appreciated!

Let's say I've a main class called Manager.

I also have three different classes called EngineA, EngineB and EngineC. All these classes share the same methods and properties. Let's say "name" and "doSomething()"

I've love to have an "engine" property in the Manager class that can hold any of the different engine classes so I can call manager.engine.doSomething() or manager.engine.name

I know inheritance is not possible and I've found duck typing is the recommended approach.

What I've done so far, is having a run(cmd, Args() as object) method in manager class which makes a CallSubX(cmd [, Args()]) on the appropriate engine, but it looks like so much work.

I'm not sure if this is the best approach possible in B4J. Any hint here?

Thanks in advance,
Will
 

Will Castillo

Member
Licensed User
:facepalm:

That's what happens when you: A) Write posts at 2am, B) You get old, C) You're too lazy to spell check yourself, or D) All of the above.

Thanks, i'll take a look at that thread and come back if needed.
 
Upvote 0

Will Castillo

Member
Licensed User
Ok, coming back to this (at 10:30 am this time)

I'm not smart enough to understand the proposed solutions. Every one of them seems to miss one or more of my needs.

But I came up with what seemed to be my best approach so far so I thought I should leave it here for future (dis)reference.

My different "Engine" classes are regular class modules.

My "Manager" class is now a Code Module (which, note to self, is the B4X way to create singletons, which seems to be a favorite design pattern for B4X -another note to self)

It has the following basic structure:

B4X:
Sub Process_Globals
    Private mCallBack As Object
    Private mEventName As String
     
    Private mEngine As Map
End Sub

' I know an Initialize is not needed for Code Module, but I leave it here for consistency
Public Sub Initialize(CallBack As Object, EventName As String)
    mCallBack = CallBack
    mEventName = EventName
 
    Engine.Initialize
    Engine.Put("manager", Me)
 
    CallSub(mCallBack, mEventName & "_Initialized")
End Sub

Public Sub Engine As Map
    Return mEngine
End Sub

Public Sub startEngine(EngineType As Int)
    Select EngineType
        Case Utils.ENGINE_A
            Dim EngineA As EngineAClass
            EngineA.Initialize(Me, "Engine")
         
            Engine.Put("type", Utils.ENGINE_A)
            Engine.Put("core", EngineA)
        Case Utils.ENGINE_B
            Dim EngineB As EngineBClass
            EngineB.Initialize(Me, "Engine")
         
            Engine.Put("type", Utils.ENGINE_B)
            Engine.Put("core", EngineB)
        ' [...]   more engines...
    End Select
     
    CallSub(mCallBack, mEventName & "_EngineStarted")
End Sub

Private Sub run(cmd As String, Args() As Object) As Object
    Dim NumberOfParameters As Int
 
    If (Args = Null) Then
        NumberOfParameters = 0
    Else
        NumberOfParameters = Min(Args.Length, 2)
    End If
 
    cmd = "Do_" & cmd
 
    Select NumberOfParameters
        Case 0
            Return CallSub(Engine.Get("core"), cmd)
        Case 1
            Return CallSub2(Engine.Get("core"), cmd, Args(0))
        Case 2
            Return CallSub3(Engine.Get("core"), cmd, Args(0), Args(1))
    End Select
 
    Return Me  ' I'd love to throw an exception here... but I can't find a way to do it. HELP!
End Sub

Any comment? Suggestion? Obvious-yet-missed flaw on this approach?
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
Is there only one engine for a single manager?
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
For the callback, I would use CallSubDelayed instead of CallSub.
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
Just use CallSubDelayed for the call back, not for calling the methods of your engine objects/classes
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
My "Manager" class is now a Code Module (which, note to self, is the B4X way to create singletons, which seems to be a favorite design pattern for B4X -another note to self)
Is it? A code module is just more code that runs on the main thread. The way I view a code module is that it provides another namespace () and allows me to keep the main thread nice and neat. It can also be used for code sharing. Unlike a class, it has no mandatory constructor - you don't need to call initialize on it before being able to use it, unlike a class module.
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
None of the B4X languages have the language keyword that lets you Throw an exception. You will have to use the return values as a means to report an error.
Edit: I deleted some dumb stuff. Need to take a break...
 
Upvote 0

Claudio Oliveira

Active Member
Licensed User
Longtime User
Woa! It does! Thank you very much! I really needed this.
Nice! :)
How do I know when a library can be used on different B4X apps? Trial and error?
Well... I must confess that I really don't know how one can be sure a library works in B4A and B4X. In these cases I just try it. Shame on me!!! :D
Sometimes it works. Sometimes not.
Exception handling and throwing is crucial in app development, so I've been using ExceptionEx with both B4A and B4J for some time now, with no problems whatsoever.
 
Upvote 0

udg

Expert
Licensed User
Longtime User
How do I know when a library can be used on different B4X apps?
As a general rule the author should precede the lib's name with a [B4X] or [B4A B4J] or similar indication. Expect newer libs to be mostly cross-compiler among the B4X group.
 
Upvote 0
Top