just a quick update .. your other example i think is perfect
I simply haven't devoted enough time looking at it.
i will continue to read all examples
SOLID programming
Re: SOLID programming
Great!!Can't imagine before FB can do this!!! direct access hardware as powerful as c,mucher cleaner code; can create classes(type) instance both on the stack & on the heap,interface...Can't imagine what feature FB is lack of,template?paul doe wrote: ↑Sep 27, 2018 12:30 Well, I folded the two examples into one. It's a bit long, but it's straightforward and has some comments to help understand what's going on.Use the arrow keys to move a shape and the mouse to move another. There's a shape that moves automatically, and two shapes that don't move at all.Code: Select all
#include once "fbgfx.bi" /' Some colors (mostly for convenience and readability ) '/ type RGBAColor as ulong namespace colors const red as RGBAColor = rgba( 255, 0, 0, 255 ) const green as RGBAColor = rgba( 0, 255, 0, 255 ) const blue as RGBAColor = rgba( 0, 0, 255, 255 ) const yellow as RGBAColor = rgba( 255, 255, 0, 255 ) const cyan as RGBAColor = rgba( 0, 255, 255, 255 ) const magenta as RGBAColor = rgba( 255, 0, 255, 255 ) end namespace /' Interface for all shapes Here, we'll put all the generic functionality that shapes will need to implement. '/ type IShape extends Object declare virtual destructor() x as single y as single protected: '' This makes the class behaves as an interface ie. inherit-only declare constructor() end type constructor IShape() end constructor destructor IShape() end destructor /' Interface for all renderable shapes '/ type IRenderableShape extends Object declare virtual destructor() declare abstract sub render() end type destructor IRenderableShape() end destructor /' Interface for all shapes that need updating '/ type IUpdatableShape extends Object declare virtual destructor() declare abstract sub update() end type destructor IUpdatableShape() end destructor /' Interface for all rectangular shapes '/ type IRectangleShape extends IShape declare virtual destructor() override declare abstract property width() as integer declare abstract property height() as integer end type destructor IRectangleShape() end destructor /' Interface for all circular shapes '/ type ICircleShape extends IShape declare virtual destructor() declare abstract property radius() as integer end type destructor ICircleShape() end destructor /' A concrete class that represents a rectangular shape '/ type Rectangle extends IRectangleShape public: declare constructor( _ byval as integer, _ byval as integer, _ byval as integer, _ byval as integer ) declare destructor() override declare property width() as integer override declare property height() as integer override private: declare constructor() m_width as integer m_height as integer end type constructor Rectangle() end constructor constructor Rectangle( _ byval newX as integer, _ byval newY as integer, _ byval aWidth as integer, _ byval aHeight as integer ) x = newX y = newY m_width = iif( aWidth < 1, 1, aWidth ) m_height = iif( aHeight < 1, 1, aHeight ) end constructor destructor Rectangle() end destructor property Rectangle.width() as integer return( m_width ) end property property Rectangle.height() as integer return( m_height ) end property /' A decorator that provides rendering functionality for the IRenderableShape interface This one renders rectangular shapes. '/ type RenderableRectangle extends IRenderableShape public: declare constructor( _ byref as IRectangleShape, _ byval as RGBAColor ) declare destructor() override declare sub render() override declare property decorated() as IRectangleShape ptr private: declare constructor() m_rectangle as IRectangleShape ptr m_color as RGBAColor end type constructor RenderableRectangle() end constructor constructor RenderableRectangle( _ byref aRectangle as IRectangleShape, _ byval aColor as RGBAColor ) m_rectangle = @aRectangle m_color = aColor end constructor destructor RenderableRectangle() end destructor property RenderableRectangle.decorated() as IRectangleShape ptr return( m_rectangle ) end property sub RenderableRectangle.render() line( m_rectangle->x, m_rectangle->y ) - _ ( m_rectangle->x + m_rectangle->width - 1, m_rectangle->y + m_rectangle->height - 1 ), _ m_Color, bf end sub /' Another decorator, for a rectangle with borders '/ type RenderableRectangleWithBorder extends IRenderableShape public: declare constructor( _ byval as RenderableRectangle ptr, _ byval as integer, _ byval as RGBAColor ) declare destructor() override declare sub render() override private: declare constructor() m_renderableRectangle as RenderableRectangle ptr m_borderSize as integer m_borderColor as RGBAColor end type constructor RenderableRectangleWithBorder() end constructor 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 destructor RenderableRectangleWithBorder() delete( m_renderableRectangle ) end destructor sub RenderableRectangleWithBorder.render() dim as integer halfSize = m_borderSize / 2 /' Here you call the decorated instance for doing the base rendering. This is not necessary, but a 'true' decorator always calls the method of the instance it decorates. '/ m_renderableRectangle->render() line( _ m_renderableRectangle->decorated->x - halfSize, _ m_renderableRectangle->decorated->y - halfSize ) - ( _ m_renderableRectangle->decorated->x + m_renderableRectangle->decorated->width - 1 + halfSize, _ m_renderableRectangle->decorated->y + m_renderableRectangle->decorated->height - 1 + halfSize ), _ m_borderColor, b line( _ m_renderableRectangle->decorated->x + halfSize, _ m_renderableRectangle->decorated->y + halfSize ) - ( _ m_renderableRectangle->decorated->x + m_renderableRectangle->decorated->width - 1 - halfSize, _ m_renderableRectangle->decorated->y + m_renderableRectangle->decorated->height - 1 - halfSize ), _ m_borderColor, b paint( _ m_renderableRectangle->decorated->x, _ m_renderableRectangle->decorated->y ), _ m_borderColor, m_borderColor end sub /' A concrete class that represents a circle '/ type Circle extends ICircleShape public: declare constructor( _ byval as integer, _ byval as integer, _ byval as integer ) declare destructor() override declare property radius() as integer override private: declare constructor() m_radius as integer end type constructor Circle() end constructor constructor Circle( _ byval newX as integer, _ byval newY as integer, _ byval aRadius as integer ) x = newX y = newY m_radius = aRadius end constructor destructor Circle() end destructor property Circle.radius() as integer return( m_radius ) end property /' This decorator adds rendering functionality for a circular shape. '/ type RenderableCircle extends IRenderableShape public: declare constructor( _ byref as ICircleShape, _ byval as RGBAColor ) declare destructor() override declare sub render() override declare property decorated() as ICircleShape ptr private: declare constructor() m_circle as ICircleShape ptr m_color as RGBAColor end type constructor RenderableCircle() end constructor constructor RenderableCircle( _ byref aCircle as ICircleShape, _ byval aColor as RGBAColor ) m_circle = @aCircle m_color = aColor end constructor destructor RenderableCircle() end destructor property RenderableCircle.decorated() as ICircleShape ptr return( m_circle ) end property sub RenderableCircle.render() circle( _ m_circle->x, _ m_circle->y ), m_circle->radius, m_color, , , , f end sub /' This decorator adds moving functionality to the shape; in this case, to move the shape with the arrow keys. '/ type MovableWithKeyboard extends IUpdatableShape public: declare constructor( _ byref as IShape ) declare destructor() override declare sub update() override private: declare constructor() m_shape as IShape ptr end type constructor MovableWithKeyboard() end constructor constructor MovableWithKeyboard( _ byref aShape as IShape ) m_shape = @aShape end constructor destructor MovableWithKeyboard() end destructor sub MovableWithKeyboard.update() if( multikey( fb.SC_UP ) ) then m_shape->y -= 1 end if if( multikey( fb.SC_DOWN ) ) then m_shape->y += 1 end if if( multikey( fb.SC_LEFT ) ) then m_shape->x -= 1 end if if( multikey( fb.SC_RIGHT ) ) then m_shape->x += 1 end if end sub /' This decorator adds moving functionality to the shape; in this case, to move the shape with the mouse. '/ type MovableWithMouse extends IUpdatableShape public: declare constructor( _ byref as IShape ) declare destructor() override declare sub update() override private: declare constructor() m_shape as IShape ptr end type constructor MovableWithMouse() end constructor constructor MovableWithMouse( _ byref aShape as IShape ) m_shape = @aShape end constructor destructor MovableWithMouse() end destructor sub MovableWithMouse.update() dim as integer mx, my getMouse( mx, my ) m_shape->x = mx m_shape->y = my end sub /' This decorator moves the shape in a random direction, keeping it within the given bounds. '/ type BoundedMovement extends IUpdatableShape public: declare constructor( _ byref as IShape, _ byval as integer, _ byval as integer, _ byval as integer, _ byval as integer ) declare destructor() override declare sub update() override private: declare constructor() m_shape as IShape ptr m_top as integer m_left as integer m_right as integer m_bottom as integer m_directionX as single m_directionY as single end type constructor BoundedMovement() end constructor constructor BoundedMovement( _ byref aShape as IShape, _ byval topLimit as integer, _ byval leftLimit as integer, _ byval rightLimit as integer, _ byval bottomLimit as integer ) m_shape = @aShape m_top = topLimit m_left = leftLimit m_right = rightLimit m_bottom = bottomLimit m_directionX = ( rnd() * 2.0 ) - 1.0 m_directionY = ( rnd() * 2.0 ) - 1.0 end constructor destructor BoundedMovement() end destructor sub BoundedMovement.update() m_shape->x += m_directionX m_shape->y += m_directionY if( m_shape->x < m_left ) then m_shape->x = m_left m_directionX = -m_directionX end if if( m_shape->x > m_right ) then m_shape->x = m_right m_directionX = -m_directionX end if if( m_shape->y < m_top ) then m_shape->y = m_top m_directionY = -m_directionY end if if( m_shape->y > m_bottom ) then m_shape->y = m_bottom m_directionY = -m_directionY end if end sub /' And these represent other layers of the application. They need not be functions, they can also be classes, each with its own processing model. '/ sub renderShapes( theRenderableShapes() as IRenderableShape ptr ) '' Render shapes for i as integer = 0 to ubound( theRenderableShapes ) theRenderableShapes( i )->render() next end sub sub updateShapes( theUpdatableShapes() as IUpdatableShape ptr ) '' Update shapes for i as integer = 0 to ubound( theUpdatableShapes ) theUpdatableShapes( i )->update() next end sub /' Main code '/ screenRes( 800, 600, 32 ) randomize() /' These objects will be the objects we want to process. By themselves, they don't do anything, they just 'represent' a shape. I put them into separate lists for clarity (to avoid downcasting), but you can also put them into a single list. Of course, 'list' here means just an array. '/ dim as Rectangle rectangles( ... ) = { _ Rectangle( 300, 200, 100, 100 ), _ Rectangle( 100, 120, 50, 50 ), _ Rectangle( 500, 500, 80, 50 ) } dim as Circle circles( ... ) = { _ Circle( 200, 50, 30 ), _ Circle( 500, 400, 50 ), _ Circle( 50, 500, 100 ) } /' These two arrays hold decorated instances of objects, that are passed to the appropriate processing functions. '/ dim as IRenderableShape ptr renderableShapes( ... ) = { _ new RenderableRectangle( rectangles( 0 ), colors.blue ), _ new RenderableRectangleWithBorder( _ new RenderableRectangle( rectangles( 1 ), colors.red ), _ 5, colors.yellow ), _ new RenderableRectangleWithBorder( _ new RenderableRectangle( rectangles( 2 ), colors.cyan ), _ 2, colors.magenta ), _ new RenderableCircle( circles( 0 ), colors.green ), _ new RenderableCircle( circles( 1 ), colors.yellow ) } dim as IUpdatableShape ptr updatableShapes( ... ) = { _ new MovableWithKeyboard( rectangles( 0 ) ), _ new MovableWithMouse( circles( 0 ) ), _ new BoundedMovement( _ rectangles( 1 ), _ 0, 0, 799, 599 ) } /' Main loop Nothing fancy, just render and update shapes. However, do note that not all shapes get rendered, and not all shapes get updates; only the ones that matter (the ones we specified in the two arrays above) are passed to the functions that consume them. '/ do updateShapes( updatableShapes() ) screenLock() cls() renderShapes( renderableShapes() ) screenUnlock() sleep( 1, 1 ) loop until( multikey( fb.SC_ESCAPE ) ) /' Dispose of the explicitly created decorators '/ for i as integer = 0 to ubound( renderableShapes ) delete( renderableShapes( i ) ) next for i as integer = 0 to ubound( updatableShapes ) delete( updatableShapes( i ) ) next
Well, that should wrap some basic concepts about encapsulation and interfaces. Do UML Class Diagrams help anybody to better grasp the concepts? If it does, I'll post the class diagram for this snippet also.
Code: Select all
type RenderableRectangleWithBorder extends IRenderableShape
public:
declare constructor( _
byval as RenderableRectangle ptr, _
BTW,as the manual said:
Code: Select all
In the -lang fb dialect, numeric parameters are passed Byval by default. Strings and UDTs are passed Byref by default.
Re: SOLID programming
Effectively. FreeBasic can't yet define anything like a 'proper' interface (like those seen in C#/Java), nor can it define generic classes (templates).
It's a pointer so it should be byval (if you omit the specification, it's passed byval by default).byval---is this a typo,should be byref instead? or it is byval?I ran both cases and found the program produce exactly the same result.Code: Select all
type RenderableRectangleWithBorder extends IRenderableShape public: declare constructor( _ byval as RenderableRectangle ptr, _
BTW,as the manual said:so can byval and byref be omitted?Having them seems a bit verbose.What's the best practice should I follow?Code: Select all
In the -lang fb dialect, numeric parameters are passed Byval by default. Strings and UDTs are passed Byref by default.
It can be omitted if you want. I've changed my own style to omit it (that's old code), so it's more clear which parameters get passed byref (so it's easier to pinpoint which parameters are meant to return values from a function).
If we were talking about 'best practices', I'd say that your style should reflect your intent; that is, if another coder reads your code, he should be reasonably sure what your intent was when you wrote the code, provided he's experienced enough (comments should also be in place to clarify if needed; never underestimate the importance of good comments).