SOLID programming

General discussion for topics related to the FreeBASIC project or its community.
dafhi
Posts: 1641
Joined: Jun 04, 2005 9:51

Re: SOLID programming

Post by dafhi »

progress report

Code: Select all

/'
'/
type IimageVars extends object
   declare virtual destructor
   
   declare abstract property  pixels() as any ptr
   as integer                 w, h, bypp, pitch
    
    protected:
   declare constructor
End Type

constructor IimageVars
end constructor

destructor IimageVars
end destructor

/'
'/
type IImage extends IimageVars
   declare property  pixels() as any ptr
    
   as any ptr        m_pixels
end type

property IImage.pixels() as any ptr
   return m_pixels
end property

/'
'/
type IScreen extends IimageVars
   declare abstract property  page( as integer )
   declare abstract property  pages( as integer )
   declare abstract property  flags( as integer )
   declare abstract property  rate( as integer )
   
   declare property           pixels() as any ptr
   
   as integer                 m_flags, m_pages, m_rate
   as string                  m_driver_name
   as integer                 m_page
   as any ptr                 m_pixels(any)
end type

property IScreen.pixels() as any ptr
   return m_pixels(m_page)
end property
[edit] i realize i've just violated the first principle
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: SOLID programming

Post by dodicat »

We have inheritance, polymorphism, encapsulation and demotion.

Code: Select all

 



type Sailor extends object
    declare abstract sub show
end type

type Admiral extends sailor
    declare  sub show 
end type

type Captain extends sailor
  declare  sub show
end type

type FirstMate extends sailor
   declare  sub show
end type

type SecondMate extends sailor
 declare  sub show
end type

type ThirdMate extends sailor
 declare  sub show
end type

type Bosun extends sailor
 declare  sub show
end type

type AbleSeaman extends sailor
 declare  sub show
end type

type DeckBoy extends sailor
 declare  sub show
end type

type DeckHand extends sailor
 declare  sub show
end type


sub Admiral.show
    print "Admiral"
     *cast(sailor ptr,new Captain).show
end sub
sub Captain.show
    print "Captain"
     *cast(sailor ptr,new FirstMate).show
end sub
sub FirstMate.show
    print "First Mate"
    *cast(sailor ptr,new SecondMate).show
end sub
sub SecondMate.show
    print "Second Mate"
     *cast(sailor ptr,new ThirdMate).show
end sub
sub ThirdMate.show
    print "Third Mate"
     *cast(sailor ptr,new Bosun).show
end sub
sub Bosun.show
    print "Bosun"
      *cast(sailor ptr,new Ableseaman).show
end sub
sub AbleSeaman.show
    print "Able Seaman"
      *cast(sailor ptr,new Deckhand).show
end sub
sub DeckHand.show
    print "Deck hand"
      *cast(sailor ptr,new DeckBoy).show
end sub
sub DeckBoy.show
    print "Deck boy"
    end sub

'=============================

dim as sailor ptr b=new Admiral
b->show
sleep
delete b
  
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: SOLID programming

Post by fxm »

And as a bonus you also have memory leaks!
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: SOLID programming

Post by dodicat »

I did the loop test before posting.
do
locate 1
b->show
loop

No leaks detected after a 5 minute run.(Win 10 resource monitor)
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: SOLID programming

Post by fxm »

I see the column "memory" increment continuously during the loop.
(one Integer by "New" call)
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: SOLID programming

Post by dodicat »

OK, I put a large array as a field in sailor.
Leaks, yes, especially worrying for a sailor.
demotion is out.
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: SOLID programming

Post by fxm »

In addition, it's better with right syntax (see #811 *(ptr).field should give an error)

( *cast( base-type ptr, new derived_type ) ).show
or
cast( base-type ptr, new derived_type )->show
creates a dynamic object (to be suppressed by Delete)

( *cast(base-type ptr, @derived_type ) ).show
or
cast( base-type ptr, @derived_type )->show
creates a temporary object (automatically suppressed after the line execution: at each 'end sub' in this case)
Last edited by fxm on Oct 01, 2018 15:45, edited 2 times in total.
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: SOLID programming

Post by dodicat »

This works also (version 1.15.0)

Code: Select all


type Sailor extends object
    as double a(10000)'leak tester
    declare abstract sub show
   
end type

type Admiral extends sailor
    declare  sub show 
end type

type Captain extends sailor
  declare  sub show
end type

type FirstMate extends sailor
   declare  sub show
end type

type SecondMate extends sailor
 declare  sub show
end type

type ThirdMate extends sailor
 declare  sub show
end type

type Bosun extends sailor
 declare  sub show
end type

type AbleSeaman extends sailor
 declare  sub show
end type

type DeckBoy extends sailor
 declare  sub show
end type

type DeckHand extends sailor
 declare  sub show
end type


sub Admiral.show
    print "Admiral"
     *cast(sailor ptr,@Captain).show
     
end sub
sub Captain.show
    print "Captain"
     *cast(sailor ptr,@FirstMate).show
     
end sub
sub FirstMate.show
    print "First Mate"
    *cast(sailor ptr,@SecondMate).show
     
end sub
sub SecondMate.show
    print "Second Mate"
     *cast(sailor ptr,@ThirdMate).show
     
end sub
sub ThirdMate.show
    print "Third Mate"
     *cast(sailor ptr,@Bosun).show
    
end sub
sub Bosun.show
    print "Bosun"
     delete @this
      *cast(sailor ptr,@Ableseaman).show
end sub
sub AbleSeaman.show
    print "Able Seaman"
      *cast(sailor ptr,@Deckhand).show
      
end sub
sub DeckHand.show
    print "Deck hand"
      *cast(sailor ptr,@DeckBoy).show
     
end sub
sub DeckBoy.show
    print "Deck boy"
     
    end sub

'=============================

dim as sailor ptr b=new Admiral
'do
 
    locate 1
b->show

'loop
sleep
delete b



 
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: SOLID programming

Post by dodicat »

OK fxm, I see you had similar.
Demotion is on again.
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: SOLID programming

Post by dodicat »

I thought for a few moments that I had a recursion free quicksort.
But it is still on the stack.

Code: Select all


type Sorter extends object
    declare abstract sub sort(() As double, As Integer, As integer)
end type

type phase1 extends Sorter
    declare  sub sort(() As double, As Integer, As integer)
end type

type phase2 extends Sorter
    declare sub sort(() As double, As Integer, As integer)
end type

sub phase1.sort(array() As double,begin As Integer,Finish As integer)
     Dim As Integer i=begin,j=finish 
     Dim As double x =array(((I+J)\2))
    While  I <= J
        While array(I) < X
            I+=1
        Wend
        While array(J) > X
            J-=1
        Wend
        If I<=J Then
            Swap array(I),array(J)
            I+=1
            J-=1
        End If
    Wend
     If J > begin  then  *cast(Sorter ptr,@phase2).sort(array(),begin,J)
     If I < Finish Then  *cast(Sorter ptr,@phase2).sort(array(),I,finish)
end sub

sub phase2.sort(array() As double,begin As Integer,Finish As integer)
     Dim As Integer i=begin,j=finish 
     Dim As double x =array(((I+J)\2))
    While  I <= J
        While array(I) < X
            I+=1
        Wend
        While array(J) > X
            J-=1
        Wend
        If I<=J Then
            Swap array(I),array(J)
            I+=1
            J-=1
        End If
    Wend
     If J > begin  then  *cast(Sorter ptr,@phase1).sort(array(),begin,J)
     If I < Finish Then  *cast(Sorter ptr,@phase1).sort(array(),I,finish)
end sub

'======== create an array ============
dim as long limit=1000000
redim  as double a(limit)
for n as long=0 to limit
    a(n)=rnd*100-rnd*100
    next
'=====================================

dim as Sorter ptr x=@phase1
x->sort(a(),lbound(a),ubound(a))

for n as long=lbound(a) to lbound(a)+50
    print a(n)
next
print "..."
print "..."
print "..."
for n as long=ubound(a)-50 to ubound(a)
    print a(n)
next
'====================

function notsorted(a() as double) as long
    for n as long=lbound(a) to ubound(a)-1
        if a(n+1)<a(n) then return 1
    next
    return 0
end function

print iif(notsorted(a()),"Error","OK")
print "done"
sleep

   
dafhi
Posts: 1641
Joined: Jun 04, 2005 9:51

Re: SOLID programming

Post by dafhi »

on the topic of memory leaks, in paul's example i *might* be seeing a sketchy scenario

Code: Select all

dim as IRenderableShape ptr renderableShapes( ... ) = { _
   .. , _
  new RenderableRectangleWithBorder( _
    new RenderableRectangle( rectangles( 1 ), colors.red ), _
    5, colors.yellow ), _
and in the cleanup code, i'm thinking there needs to be a recursive delete
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: SOLID programming

Post by paul doe »

dafhi wrote:on the topic of memory leaks, in paul's example i *might* be seeing a sketchy scenario

Code: Select all

dim as IRenderableShape ptr renderableShapes( ... ) = { _
   .. , _
  new RenderableRectangleWithBorder( _
    new RenderableRectangle( rectangles( 1 ), colors.red ), _
    5, colors.yellow ), _
and in the cleanup code, i'm thinking there needs to be a recursive delete
No. Why would you want to do that?

Code: Select all

/'
  ...
'/
destructor RenderableRectangleWithBorder()
  delete( m_renderableRectangle )
end destructor
/'
  ...
'/
It's important to note that RenderableRectangle aggregates a Rectangle instance (expressed in code as a reference to an IRectangleShape interface), and RenderableRectangleWithBorder composes a RenderableRectangle instance. It doesn't show in the UML diagram I posted, because as I stated before, I folded a lot of code, examples and explanations into a single snippet.

Look at how both decorators get constructed:

Code: Select all

/'
  Aggregates (it accepts an interface by reference, which implies that
  the object is already constructed elsewhere, and this class doesn't
  own the reference)
'/
constructor RenderableRectangle( _
  byref aRectangle as IRectangleShape, _
  byval aColor as RGBAColor )
  
  m_rectangle = @aRectangle
  m_color = aColor
end constructor
/'
  ...
'/

/'
  Composes (in this case, the dependency is injected directly, 
  hence the new()ed instance of RenderableRectangle in the
  client code; this implies that the class does, in fact, own the
  reference and thus has to dispose of it upon destruction)
'/
constructor RenderableRectangleWithBorder( _
  byval aRenderableRectangle as RenderableRectangle ptr, _
  byval aBorderSize as integer, _
  byval aBorderColor as RGBAColor )
  
  m_renderableRectangle = aRenderableRectangle
  m_borderSize = iif( aBorderSize < 1, 1, aBorderSize )
  m_borderColor = aBorderColor
end constructor
/'
  ...
'/

fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: SOLID programming

Post by fxm »

The general rule is that there must be as many calls to Delete() as there are calls to New().

The nested calls to New():
new RenderableRectangle( rectangles( 1 ), colors.red )
and
new RenderableRectangle( rectangles( 2 ), colors.cyan )
are well terminated by calls to Delete() which are activated by the destructor of RenderableRectangleWithBorder (called when Deleting the two RenderableRectangleWithBorder objects in the loop on renderableShapes() array).
dafhi
Posts: 1641
Joined: Jun 04, 2005 9:51

Re: SOLID programming

Post by dafhi »

I'd forgotten about the destructors. I have adhd or something but i'll get through this.

I'm trying to refactor so that I'll be able to continue simply:

Code: Select all

sprite.blit @buf
my starting point, following your example (paul) probably is something like this

Code: Select all

type IimageVars extends object
   declare virtual destructor
   
   declare abstract property  pixels() as any ptr
   as integer                 w, h, bypp, pitch
    
    private:
   declare constructor
End Type
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: SOLID programming

Post by fxm »

fxm wrote:The general rule is that there must be as many calls to Delete() as there are calls to New().

The nested calls to New():
new RenderableRectangle( rectangles( 1 ), colors.red )
and
new RenderableRectangle( rectangles( 2 ), colors.cyan )
are well terminated by calls to Delete() which are activated by the destructor of RenderableRectangleWithBorder (called when Deleting the two RenderableRectangleWithBorder objects in the loop on renderableShapes() array).
It is true that I generally prefer to have the New/Delete calls at the same level of code.
For example in this case, the New/Delete call respectively in the constructor/destructor of RenderableRectangle, but this requires passing through a temporary object of RenderableRectangle if we want to keep the same type of syntax for the user.
Example of modification for this:

Code: Select all

'.....
constructor RenderableRectangleWithBorder( _
  byval aRenderableRectangle as RenderableRectangle ptr, _
  byval aBorderSize as integer, _
  byval aBorderColor as RGBAColor )
 
  m_renderableRectangle = new RenderableRectangle( *aRenderableRectangle )  '' default copy-constructor is sufficient
  '                                                                         ''   in this case
  m_borderSize = iif( aBorderSize < 1, 1, aBorderSize )
  m_borderColor = aBorderColor
end constructor
'.....
dim as IRenderableShape ptr renderableShapes( ... ) = { _
  new RenderableRectangle( rectangles( 0 ), colors.blue ), _
  new RenderableRectangleWithBorder( _
    @RenderableRectangle( rectangles( 1 ), colors.red ), _
    5, colors.yellow ), _
  new RenderableRectangleWithBorder( _
    @RenderableRectangle( rectangles( 2 ), colors.cyan ), _
    2, colors.magenta ), _
  new RenderableCircle( circles( 0 ), colors.green ), _
  new RenderableCircle( circles( 1 ), colors.yellow ) }
'.....
Post Reply