Page 1 of 1

Is there a guide for developing modules?

Posted: Sun Mar 30, 2014 2:52 am
by Zach
Hi,

I was wondering if anyone has written a guide about good practices for developing modular code. I've kind of been interested in modules since the feature was introduced, but I know very little about them and how they are intended to be used. I am assuming it is for developing self-contained "libraries" that you can use from within your program that would be re-usable across many projects and are meant to be entirely self contained?

I suppose I am most curious about developing modular code that can access / modify / share data with other parts of the programs, perhaps other modules, and what that might look like / require... Is there a way to do this without using a lot of globals, or some kind of array or other structured storage medium to read info in / out as a means of passing it around?

If anyone could give me a brief education about modules and developing modular code; good practices when doing so, etc.. That would be cool. But a guide/tutorial of some sort would be super if one exists. The help CHM is kind of terse in its description; and I learn easiest from examples of real word stuff, focused on a specific task or end-goal :oops: . It doesn't need to be anything fancy using pointers and stuff though.

Re: Is there a guide for developing modules?

Posted: Mon Mar 31, 2014 2:13 am
by idle
There are a number of recent examples in the tips and tricks section
to get an idea, but what your asking has little to do with modules as such.
You can pretty much turn any code into a module but it's not necessarily going to make it any more modular
and could even make it harder to use.

The goal of modular code is to make your code easier to maintain and easier for someone else to use
so you generally follow an object model and present some interface to the code where the functionality
is more or less independent of how you use it and it often comes at a cost
requiring more code and a slight hit in performance. (wrap the crap)

For instance lets say you want to create a collection of cars
you have a choice of implimentation to either use heap memory, a list, array or map to store the instances
of a car structure. Each method has it's pros and cons but importantly the implementation
doesn't effect how you present the code to the user.
All you do is combine the data and functions together by eliminating the globals adding them to a structure
and pass instances of the structure to your procedures, that's it in a nutshell.

You can use a flat object model like the PB does or Interfaces it's a personal choice
but from a maintenance, extensibility and modification point of view using Interfaces
is a clear winner since it not only clearly presents a user friendly interface it
also allows you to turn your code into a dll with no effort plus facilitates name space resolution
via the public interface if it's bunged into a module.

Even though Modules in PB more or less allows you to turn any code into a black box
without effort and avoid name conflicts plus isolate your code it doesn't make it any easier to use
that code. So modules should be seen as an aid not a magic solution.
5% of a modules behaviour just prefixes the modules members in the final assembly code
and the other 95% is compiler abstractions to appear as if the codes in a black box
which is all good but its just a compiler trick.

So given that rant.

Step 1)
Eliminate global's by adding them into a logical structure

Code: Select all

 
  ;global Make.s,Model.s,ChassisType.i ... 
   
  Structure car 
    Make.s 
    Model.s 
    ChassisType.i 
    ... 
  EndStructure 

  Global NewList glcar.Car()   ;use a list to hold instances 
   
  


Step 2)
provide a means to create an instance of the structure and return the instance

Code: Select all

   Procedure New_Car()      
      AddElement(glcar()) 
      ProcedureReturn glcar() 
    EndProcedure     

Step 3)
write your procedures where the first parameter is as an instance to the structure

Code: Select all

Procedure  SetModel(Car,Model.s)   ;Notice it looks like a regular PB object function      
   Protected *car.car   ;where the ID will be cast to a pointer to access a car    
   If car 
      *car = car    ;cast the car parameter to a pointer 
      *car\Model = model    ;set the model 
   EndIf    
EndProcedure 

Procedure.s GetModel(*car.car)  ;Though it's better to use a pointer directly as a parameter  
    If *car                                ;which also will make it compatible with an interface should you choose to add one 
       ProcedureReturn *car\Model
   EndIf  
EndProcedure    

  

step 4)
wrap it in a module

Code: Select all


DeclareModule mCar 
;A collection of cars 
Declare New_Car()    ;create a new instance of a car  
Declare SetModel(car,model.s)   ;set a cars model 
Declare SetMake(car,Make.s)     ;set a cars make 
Declare.s GetModel(car)              ;get a cars model    
Declare.s GetMake(car)                ;get a cars make 
Declare Find(make.s,model.s)     ;find a car from a make and model returns a car instance  
EndDeclareModule 

Module mCar 
;impliment the module 
;all members here are private unless they're delcared in the declaremodule block 

Enumeration 1
   #gear_manual 
   #gear_auto 
EndEnumeration 

Enumeration 1 
   #metric 
   #Imperial 
EndEnumeration  
   
Structure car    ;a car  
   Model.s 
   Make.s 
   ChassisType.i  
   Bhp.i
   TopSpeed.i 
   NumberOfGears.i
   GearBoxType.i 
   units.i 
EndStructure 

Global NewList glcar.Car()   

Procedure New_Car()   ;adds an element to the collection returns the element in the list      
   AddElement(glcar()) 
   ProcedureReturn glcar() 
EndProcedure     

Procedure  SetModel(Car,Model.s)   ;Notice it looks like a regular PB object function      
   Protected *car.car   ;where the ID will be cast to a pointer to access a car    
   If car 
      *car = car    ;cast the car parameter to a pointer 
      *car\Model = model    ;set the model 
   EndIf    
EndProcedure 

Procedure.s GetModel(*car.car)  ;Better to use a pointer directly as a parameter  
    If *car                                               ;which will then make it compatible with an interface 
       ProcedureReturn *car\Model
   EndIf  
EndProcedure    

Procedure SetMake(*car.car,make.s)  
   If *car 
      *car\Make = make
   EndIf 
EndProcedure 

Procedure.s GetMake(*car.car) 
   If *car 
      ProcedureReturn *car\Make
   EndIf 
EndProcedure  

Procedure Find(make.s,model.s) 
   ForEach glCar()    ;search for a car by make and model 
      If glcar()\make = make And glcar()\Model = model 
         ProcedureReturn glcar()    ;returns the element   
      EndIf 
   Next 
 EndProcedure   

EndModule 



use it

Code: Select all

 

Global car1,car2,car3,car4
   
 UseModule mCar 
 
 car1 = New_Car() 
 SetModel(car1,"308") 
 SetMake(car1,"Ferrari") 
  
 car2 = New_Car() 
 SetModel(car2,"911") 
 SetMake(car2,"Porche") 
  
 car3 = New_Car() 
 SetModel(car3,"500") 
 SetMake(car3,"fiat") 
  
 Debug GetMake(car1) + " " + GetModel(car1)  
 Debug GetMake(car2) + " " + GetModel(car2) 
 Debug GetMake(car3) + " " + GetModel(car3) 
 
 car4 = find("Porche","911") 
 If car4 
    Debug GetMake(car4) + " " + GetModel(car4) 
 EndIf 
 

Re: Is there a guide for developing modules?

Posted: Mon Mar 31, 2014 2:12 pm
by Demivec
Nice outline idle. :)
idle wrote:Step 3)
write your procedures where the first parameter is as an instance to the structure

Code: Select all

  Procedure  SetModel(Car,Model.s) 
    Protected *car.car   ;use a pointer to access a car    
    If car 
      *car = car    ;cast the car parameter to a pointer 
      *car\Model = model     
    EndIf    
  EndProcedure
A slight simplification on this idea:

Code: Select all

Procedure  SetModel(*car.car,Model.s) 
  If *car 
    *car\Model = model     
  EndIf    
EndProcedure 

Re: Is there a guide for developing modules?

Posted: Mon Mar 31, 2014 7:23 pm
by idle
yes it is better that way Demivec, I was trying to show that it's no different than
working with regular PB commands with objects. Hiding the fact that it's actually a pointer

Re: Is there a guide for developing modules?

Posted: Mon Mar 31, 2014 9:09 pm
by Andre
Hi friends,

nice idea and thanks for your effort! :D

I see here potential for a little 'modules' chapter in the 'beginners section' of the PB manual, if you agree and probably improve the description/examples a bit more....

I will see - but already now thanks again!

Re: Is there a guide for developing modules?

Posted: Mon Mar 31, 2014 11:27 pm
by Zach
Thank you, I will be sure and read this several times and try to learn from it 8)

Re: AW: Is there a guide for developing modules?

Posted: Tue Apr 01, 2014 8:39 am
by bembulak
Nice examples and great explanations. Thanks for sharing! :)

Re: Is there a guide for developing modules?

Posted: Tue Apr 01, 2014 7:40 pm
by davido
Thanks everyone for the examples - very interesting. :D

Re: Is there a guide for developing modules?

Posted: Tue Apr 01, 2014 9:10 pm
by idle
Andre wrote:Hi friends,

nice idea and thanks for your effort! :D

I see here potential for a little 'modules' chapter in the 'beginners section' of the PB manual, if you agree and probably improve the description/examples a bit more....

I will see - but already now thanks again!
Yes it could be used as a basis for a modules chapter, though it's not particularly illustrative of the overall capabilities of modules
and it would probably require a bit of debate to decide what's actually best practice. Much of it depends on what the goal is.

Take my BitModule as an example.
http://www.purebasic.fr/english/viewtop ... 40&t=57409

It provides a number of bit based data structures BitArray, BitVector, BloomFilter and BitTrie which are wrapped
in a module to hide the private functions and enumerations plus provide a logical grouping of the data structures
as a "module". The data structures all have a common base structure and a number of functions in common
which led to the choice of an object orientated implementation which also has the added benefit of exporting
the public functions from the modules namespace via their interfaces.
Whether it's a good example of best practice is debatable but an object design pattern meshes well with modules
and removes the need to dereference the namespace every time you want to access a member of the module
instead a user only has to deal with the interface and constructor and avoids the issues of usemodule
which may lead to some confusion

Re: Is there a guide for developing modules?

Posted: Mon Jan 05, 2015 3:07 pm
by Barbarossa
Sorry to dig up this thread but I recently discovered modules and started using them.

What is particulary handy is the UseModule command.
Because then you don't have to use the long names anymore.
Module::MyProcedure() becomes MyProcedure()

HOWEVER: when you do this the IDE doesn't recognize the procedures anymore so you don't have autocomplete and parameter help when you type. It seems that this defeats the purpose of having the UseModule command altogether. Or am I missing something here?

John

Re: Is there a guide for developing modules?

Posted: Mon Jan 05, 2015 3:16 pm
by Kwai chang caine
Nearly OOP :shock:
Thanks IDLE... 8)