SOLID programming

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

SOLID programming

Post by dafhi »

This thread is meant to open discussion about the SOLID programming model.

For starters, I have 1 question

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. For the class to operate properly, the cBits property seems immutable.

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 MiniBits
 
    /'  n-bit integer class - 2018 Sep 15 - by dafhi
    '/
 
    declare constructor( as seq_state = 0, as ubyte = 0)
    declare constructor( as MiniBits)
   
    declare operator    let( as seq_state)
    declare operator    let( as MiniBits)
   
    declare operator    cast as seq_state
   
    declare property    cBits as ubyte
    declare property    cBits( as ubyte)
   
    '' meh.  read-only
    as seq_state        u
   
  private:
    as seq_state        b, _cbits
End Type

constructor MiniBits(i as seq_state, b as ubyte)
    cBits = b:  this = i
End Constructor

constructor MiniBits(i as MiniBits)
    constructor i.b
End Constructor

operator MiniBits.let(i as MiniBits)
    if _cBits = 0 then cBits = i.cBits '' 2018 Sep 13
    this = i.b
End Operator

operator MiniBits.let(i as seq_state)
    b = i and u
End Operator

operator MiniBits.cast as seq_state
    return b
End Operator

property MiniBits.cBits(i as ubyte)   '2018 Sep 14 - update 2
    _cbits = i:   u = ( culngint(1)shl (_cbits-1) ) * 2 - 1
End Property

property MiniBits.cBits as ubyte
    return _cbits
End Property
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: SOLID programming

Post by paul doe »

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
dafhi
Posts: 1640
Joined: Jun 04, 2005 9:51

Re: SOLID programming

Post by dafhi »

krr. half the time I don't know where to create threads.

So, to me, "A class should have only one reason to change" is highly confusing.

For example, okay, here's a reason, I choose to change it to whatever type of programming model I want :D There's my ONE reason for it to change.

:P

Now, the purpose of 'MiniBits' is to hold and retrieve a value, masking it off according to the cBits property. If cBits is '3', the .. uh, instantiated class represents a 3 bit literal.

And I guess I misrepresented 'immutable.' What I mean is, cBits is an integral part of the class. Just like the Casts and the Lets. Ima re-read what you said and try to refactor in my mind
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: SOLID programming

Post by paul doe »

dafhi wrote:Now, the purpose of 'MiniBits' is to hold and retrieve a value, masking it off according to the cBits property. If cBits is '3', the .. uh, instantiated class represents a 3 bit literal.
Excellent. Then, the class has only one responsibility. Whatever you add to the class (reason to change), has to be related to this responsibility only. See the rectangle example above (it's a little crappy, I know, but its 2:00 AM here and I'm going to bed =D)

I'll try to post something more meaningful tomorrow.
dafhi
Posts: 1640
Joined: Jun 04, 2005 9:51

Re: SOLID programming

Post by dafhi »

I'll finish today with a remark. I watched one of Mr. Martin's vids and he has a cadence that gathers attention much like a preacher. Actually the energy is identical. And I find ".. should have only one reason to change" horrendous.

The model however, is intriguing.
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: SOLID programming

Post by paul doe »

dafhi wrote:And I find ".. should have only one reason to change" horrendous.
Would you consider 'should serve only one purpose' more appealing ? =D
dafhi wrote:The model however, is intriguing.
Rather than a 'model', is more like 'principles'. For what? To help you achieve the touted benefits of the OOP approach: reusability, flexibility and scalability.
Last edited by paul doe on Sep 21, 2018 2:19, edited 2 times in total.
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: SOLID programming

Post by paul doe »

It's interesting also to look at the other side of the coin: Object Oriented Programming is exceptionally bad. I find the quote by Edsger Dijkstra particularly amusing =D
dafhi
Posts: 1640
Joined: Jun 04, 2005 9:51

Re: SOLID programming

Post by dafhi »

paul doe wrote:
dafhi wrote:And I find ".. should have only one reason to change" horrendous.
Would you prefer 'should serve only one purpose' more appealing ? =D
found something even better .. "Keep a class uncluttered with things you potentially don't need."

In that vein, I think i've got something

Code: Select all

type MiBits_BASE
  
    '' Represents 1 to 64-bit literal
  
    declare constructor( as bitrep_state = 0, as ubyte = 0 )
    declare operator  Let( as bitrep_state )
    declare operator  cast() as bitrep_state
   
  private:
    as bitrep_state   _val, _cbits
    as bitrep_state   _mask

End Type

operator MiBits_BASE.Let(i as bitrep_state)
    _val = i and _mask
End Operator

constructor MiBits_BASE(i as bitrep_state, b as ubyte)
    _cBits = b:  _mask = ( culngint(1)shl (_cbits-1) ) * 2 - 1
    this = i ''this calls Let
End Constructor

operator MiBits_BASE.cast as bitrep_state
    return _val
End Operator
dafhi
Posts: 1640
Joined: Jun 04, 2005 9:51

Re: SOLID programming

Post by dafhi »

paul doe wrote:It's interesting also to look at the other side of the coin: Object Oriented Programming is exceptionally bad. I find the quote by Edsger Dijkstra particularly amusing =D
".. which could only have originated in California." .. quite the sense of humor
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: SOLID programming

Post by paul doe »

dafhi wrote:found something even better .. "Keep a class uncluttered with things you potentially don't need."
Indeed =D

Looking good!
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: SOLID programming

Post by paul doe »

dafhi wrote:".. which could only have originated in California." .. quite the sense of humor
It's actually quite derogatory. Dijkstra was notorious for this trait. It's the typical 'if you don't do things like I do, then you must be a fool' attitude, and it sucks. Search for 'How do we tell truths that might hurt?', and you'll see what I mean =D
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: SOLID programming

Post by paul doe »

Some more good reads:

Is OOP a lie or is it just misused
S.O.L.I.D. is OOP for dummies. =D

The Reddit discussion contain some very good points, explained in a straightforward way. Yegor's blog is also very good, but of course, one can't agree with everything he says (I find him to be some of an OOP fanboy =D). I think that it's a good article nonetheless, and the acronym is explained raw and simple.

But to understand what these 'principles' are, we should start by addressing the four concepts upon the paradigm is based. More to follow.
Last edited by paul doe on Sep 18, 2018 11:42, edited 1 time in total.
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: SOLID programming

Post by dodicat »

If a freebasic user (not necessarily one of the regular oop experts), looks up class in the help file he/she gets

We would have put something useful here (honest) except this feature isn't implemented in the compiler yet. But since it will get added in future, and there are several other document pages that need to link here, we thought it safe to include in anyway.

Maybe I have the wrong .chm, or I am being too trivial here.

But the fact remains.
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: SOLID programming

Post by paul doe »

dodicat wrote:Maybe I have the wrong .chm, or I am being too trivial here.

But the fact remains.
No, that's a good point, indeed. But, we're discussing 'class' as a concept here, regardless of the implementation details. Classes are not exclusive to OOP: some functional languages also have the concept of class, though the implementation details are different.
caseih
Posts: 2157
Joined: Feb 26, 2007 5:32

Re: SOLID programming

Post by caseih »

paul doe wrote:It's interesting also to look at the other side of the coin: Object Oriented Programming is exceptionally bad. I find the quote by Edsger Dijkstra particularly amusing =D
The only problem I have with that blog post is that his criticisms of OOP come more from how OOP is used in a language like Java (ugg) than anything else, where classes have to be used even when none are needed or desired, as well as the necessity of an inheritance hierarchy in those languages, which isn't necessarily required for OOP (Java's interfaces try to bring this dynamism into a strict class-based OO language). A dynamic language like Python or Ruby does not require a class hierarchy to implement polymorphism.

There's a false equivalency with the notion that classes == object-oriented. In fact the granddaddy of pure OO, Smalltalk, does not use classes in the same way as many modern languages do. It would be interesting if the author (or Dijkstra) would spend significant time using Smalltalk and then made commentary on it.
Post Reply