dafhi wrote:This thread is meant to open discussion about the SOLID programming model.
Ok, so let's address some concepts first:
dafhi wrote:So I have my class MiniBits which is intended to have only one use. Questions arise though because I wonder if I'm making the class somehow too complicated.
I can't really tell you, because I don't have the slightest idea of what the class does =D
The 'S' of S.O.L.I.D. stands for 'Single Responsibility Principle', and is defined as:
A class should have only one reason to
change.
So, in this context, what is the
responsibility of the MiniBits class? What does it do? If it has more that a single responsibility -that is, I would need to change it for any other reason that's not its primary use-, it violates the SRP and thus, needs to be refactored. Consider this classic example:
Code: Select all
type RGBAColor as ulong
type Rectangle
public:
declare constructor( _
byval as integer, _
byval as integer )
declare property width() as integer
declare property height() as integer
declare function area() as single
declare sub render( _
byval as integer, _
byval as integer, _
byval as RGBAColor )
private:
declare constructor()
m_width as integer
m_height as integer
end type
constructor Rectangle()
end constructor
constructor Rectangle( _
byval aWidth as integer, _
byval aHeight as integer )
m_width = iif( aWidth < 1, 1, aWidth )
m_height = iif( aHeight < 1, 1, aHeight )
end constructor
function Rectangle.area() as single
return( m_width * m_height )
end function
property Rectangle.width() as integer
return( m_width )
end property
property Rectangle.height() as integer
return( m_height )
end property
sub Rectangle.render( _
byval x as integer, _
byval y as integer, _
byval aColor as RGBAColor )
line( x, y ) - ( x + m_width - 1, y + m_height - 1 ), aColor, b
end sub
It has two unrelated responsibilities: performing computational geometry (here it just calculates its area =D) and rendering itself. It would be advisable, then, to segregate responsibilities and split the class. This is a possible implementation:
Code: Select all
type RGBAColor as ulong
type Rectangle
public:
declare constructor( _
byval as integer, _
byval as integer )
declare property width() as integer
declare property height() as integer
declare function area() as single
private:
declare constructor()
m_width as integer
m_height as integer
end type
constructor Rectangle()
end constructor
constructor Rectangle( _
byval aWidth as integer, _
byval aHeight as integer )
m_width = iif( aWidth < 1, 1, aWidth )
m_height = iif( aHeight < 1, 1, aHeight )
end constructor
function Rectangle.area() as single
return( m_width * m_height )
end function
property Rectangle.width() as integer
return( m_width )
end property
property Rectangle.height() as integer
return( m_height )
end property
type RectangleRenderer extends Object '' <-- I extend Object here to avoid giving the class any member vars
public:
declare sub render( _
byref as Rectangle, _
byval as integer, _
byval as integer, _
byval as RGBAColor )
end type
sub RectangleRenderer.render( _
byref aRectangle as Rectangle, _
byval x as integer, _
byval y as integer, _
byval aColor as RGBAColor )
line( x, y ) - ( x + aRectangle.width - 1, y + aRectangle.height - 1 ), aColor, b
end sub
screenRes( 800, 600, 32 )
var renderer = RectangleRenderer()
renderer.render( _
Rectangle( 100, 100 ), 100, 100, rgba( 255, 0, 0, 255 ) )
sleep()
This keeps the classes
decoupled from each other. If a class has more than one responsibility, the responsibilities become coupled. Changes to one responsibility may complicate the class's ability to fulfill its other responsibilities. This kind of coupling leads to brittle designs that break in unexpected ways when changed (and contributed to give OOP its
bad reputation) =D
dafhi wrote:For the class to operate properly, the cBits property seems immutable.
The cBits property is
anything but immutable =D:
Code: Select all
...
declare property cBits as ubyte
declare property cBits( as ubyte)
...
An immutable property is one that
doesn't change over the lifetime of the object. By this token, the cBits property is fully mutable, since it has a getter and a setter defined in code. To make the property immutable, you need to
inject it to the class, and disable further modification (reading it is OK, though). I use constructor injection in this example code:
Code: Select all
type MiniBits
public:
declare constructor( _
byval as ubyte )
declare property cBits() as ubyte
private:
declare constructor()
m_cBits as ubyte
end type
constructor MiniBits()
end constructor
constructor MiniBits( _
byval theBits as ubyte )
m_cBits = theBits
end constructor
property MiniBits.cBits() as ubyte
return( m_cBits )
end property
So as you can see, the only way to change the cBits property would be to reinstantiate the class with another cBits parameter.
dafhi wrote:Here is something close to a most-basic design
Code: Select all
type seq_state as ulongint '' perhaps these could be extinguished
type seq_return as ulong
Type aliases serve two purposes: to clarify and abstract the type, and to forward declare them if you have a case of
cyclic dependencies. They're useful on their own, and normally don't need to be 'extinguished' =D
Another thing to consider:
Code: Select all
type MiniBits
...
'' meh. read-only
as seq_state u
...
End Type
Which is declared in the public scope, and thus is also very non-read-only =D