SOLID programming

For other topics related to the FreeBASIC project or its community.
paul doe
Posts: 772
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: SOLID programming

Postby paul doe » Sep 18, 2018 15:43

caseih wrote: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.

Totally. In fact, I seriously doubt that Dijkstra wrote a single line of OOP code. The link was provided because it has a 'fanboyish' attitude towards functional programming (the traditional OOP sworn enemy). The point is, we have both extremes. The truth is that the most interesting things happen in the middle, that is, when paradigms start to mix and the distinctions become blurry.

Effectively, Smalltalk doesn't use the 'instancing' pattern we use in, say, FreeBasic, but used an approach more akin to prototyping. Alan Kay, which is one of the fathers of the OOP paradigm, reportedly stated that the original concept has nothing to do whatsoever with what we've actually got.

Even professionals don't really know what the hell OOP means:

https://standardofnorms.wordpress.com/2012/09/02/4-pillars-of-object-oriented-programming/
https://medium.com/@hamzzza.ahmed95/four-pillars-of-object-oriented-programming-oop-e8d7822aa219

These are industry professionals, and look at their 'insights' into the Four Pillars (quote from the first article):

"Abstraction is a process of exposing essential feature of an entity while hiding other irrelevant detail. Why would you want to use abstraction?

Abstraction reduces code complexity and at the same time it makes your aesthetically pleasant."

Aesthetically pleasant!? Hahahahaha! I had a really good laugh with that one =D
caseih
Posts: 1296
Joined: Feb 26, 2007 5:32

Re: SOLID programming

Postby caseih » Sep 18, 2018 19:32

paul doe wrote:Abstraction reduces code complexity and at the same time it makes your aesthetically pleasant."
Aesthetically pleasant!? Hahahahaha! I had a really good laugh with that one =D
But actually this one is true, especial for larger projects, or working with more complicated problem domains like 3D graphics. Which would you rather see in your (pseudo) code:

Code: Select all

dim as Vector3D v
v = {1,5,6}
v.normalize()
v.rotate(0,90,0)

or

Code: Select all

dim as double v(0 to 2)
v(0) = 1
v(1) = 5
v(2) = 6

dim as double tempmag = sqr(v(0)*v(0)+v(1)*v(1)+v(2)*v(2))
v(0) = v(0) / tempmag
v(1) = v(1) / tempmag
v(2) = v(2) / tempmag

' manual unpacked linear algebra expressions for rotation here
' lots of cosines and sines here.
Especially when you're doing a lot of these operations.

You can argue that a function can do the same job as the methods of the Vector3D type, which is true. But methods have the advantage that I don't need to overload a function every time I introduce a new type.

Anyway, abstraction really does simplify things and make debugging easier. It's far easier to debug a rotation formula once (typos usually), and then all subsequent uses of it are clearly marked and it's easy to see what's going on. OOP may or may not be the best way to achieve this, but surely you can't argue with a straight face that abstraction doesn't make things easier to read and follow, which by definition makes them less prone to error.
paul doe
Posts: 772
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: SOLID programming

Postby paul doe » Sep 18, 2018 22:29

caseih wrote:But actually this one is true, especial for larger projects, or working with more complicated problem domains like 3D graphics. Which would you rather see in your (pseudo) code:
...
Especially when you're doing a lot of these operations.

You can argue that a function can do the same job as the methods of the Vector3D type, which is true. But methods have the advantage that I don't need to overload a function every time I introduce a new type.

Indeed, but there's a problem: polymorphism is not the same thing as an abstraction (in the OO sense). Look at how the author of the first article defines 'polymorphism':

"Polymorphism: It is a feature, which lets us create functions with same name but different arguments, which will perform differently. That is function with same name, functioning in different way. Or, it also allows us to redefine a function to provide its new definition."

Which to me it sounds a lot like function overloading. Only the last part of that sentence (added almost as an afterthought) provides some very vague definition about what polymorphism is. The author of the second article got it right, though.

The question is not if it's useful or not. The question is: why do one would want to abstract code? Because it's easier to debug? Sure, sometimes (get it wrong and I assure you, you'll have several teeth-grinding debugging sessions ahead). Because 'it makes our code aesthetically pleasant'? Because it makes your code look l33t and trendy? That's just ridiculous.

Those kinds of 'definitions' were what lead to OOP to get such a bad reputation (as if the programming paradigm caused programmers to write sh*itty code), much like parameter passing by reference got a bad reputation in the 60's (it's not because it was intrinsically bad, it was just a FORTRAN implementation snafu).

So, why whould you want to abstract, then? Because it makes your code decoupled. If I hide a class implementation behind an abstract interface, and all the code that depends on that class rely on the interface, instead of on the implementation, I can safely change the class without affecting the rest of the code. This is the real meaning of an 'abstraction' in OOP, and the 'D' of the S.O.L.I.D. acronym (Dependency Inversion):

Rely on abstractions, never on concretions

Thus, polymorphism (polymorphic dispatch would be more like it) is what allows for the same code to be reused for different classes. The underlying code doesn't know, nor it has to care, which object actually provides the functionality that the call requires, it just calls the interface that the object implements. Polymorphic dispatch provides a means to achieve that abstraction (there are others, of course), and the much needed flexibility in languages that are statically compiled.
paul doe
Posts: 772
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: SOLID programming

Postby paul doe » Sep 18, 2018 23:26

Let's see how the example code given in the article above looks in FreeBasic:

Code: Select all

'' This is the abstract interface
type Search extends Object
  declare virtual destructor()
 
  declare abstract sub searchItems()
end type

destructor Search()
end destructor

'' Concrete class to do a generic search
type AllSearch extends Search
  declare destructor() override
 
  declare sub searchItems() override
end type

destructor AllSearch()
end destructor

sub AllSearch.searchItems()
  ? "Searching all items..."
end sub

'' Concrete class to do an image search
type ImageSearch extends Search
  declare destructor() override
 
  declare sub searchItems() override
end type

destructor ImageSearch()
end destructor

sub ImageSearch.searchItems()
  ? "Searching for images..."
end sub

'' Concrete class to search for videos
type VideoSearch extends Search
  declare destructor() override
 
  declare sub searchItems() override
end type

destructor VideoSearch()
end destructor

sub VideoSearch.searchItems()
  ? "Searching videos..."
end sub

'' Concrete class for searching news
type NewsSearch extends Search
  declare destructor() override
 
  declare sub searchItems() override
end type

destructor NewsSearch()
end destructor

sub NewsSearch.searchItems()
  ? "Searching news feeds..."
end sub

'' A polymorphic function to initiate a search
sub performSearch( byref aSearch as Search )
  aSearch.searchItems()
end sub

/'
  Test code
'/
performSearch( AllSearch() )
performSearch( ImageSearch() )
performSearch( VideoSearch() )
performSearch( NewsSearch() )

sleep()

So, this is what the Dependency Inversion Principle states: to decouple the code, you rely on abstractions (interfaces). Polymorphism allows us to achieve abstraction, so it's a tool to reach an end, not an end in and of itself. With procedural code, this would look like this:

Code: Select all

sub allSearch()
  ? "Searching all items..."
end sub

sub imageSearch()
  ? "Searching images..."
end sub

sub videoSearch()
  ? "Searching videos..."
end sub

sub newsSearch()
  ? "Searching news feeds..."
end sub

sub performSearch( byval aSearch as sub() )
  aSearch()
end sub

performSearch( @allSearch )
performSearch( @imageSearch )
performSearch( @videoSearch )
performSearch( @newsSearch )

sleep()

So, it's polymorphism a trait specific to OOP? If I can achieve polymorphism on procedural code, why bother with OOP? The two examples look identical (and I would even dare to say that the procedural approach is shorter and clearer) but, in fact, they aren't. More to follow.
dafhi
Posts: 1197
Joined: Jun 04, 2005 9:51

Re: SOLID programming

Postby dafhi » Sep 19, 2018 4:05

Dependency Inversion is the one I struggled with in the beginning. So far, I'd say the points I *like* best are Single Responsibility (which i am now somewhat familiar with) and Interface Segregation (which was a piece of cake)

I'm tempted to quit using the Private, but continue with properties. If people want to write to the 'variables with complicated names' who am I to stop them :D
Last edited by dafhi on Sep 19, 2018 4:16, edited 1 time in total.
caseih
Posts: 1296
Joined: Feb 26, 2007 5:32

Re: SOLID programming

Postby caseih » Sep 19, 2018 4:10

Indeed we may be in violent agreement on many things here.

Here's an interesting case for using classes in Python when normal functions (duck typing) can also achieve the same effect: http://lucumr.pocoo.org/2013/2/13/moar-classes/. It's an interesting and compelling argument. But like you say, it all depends on what you're doing and what you're trying to achieve. When building an API or an algorithm framework, using classes as building blocks is a good idea for the reasons stated in the article. For you own one-off project, not so much.
Last edited by caseih on Sep 19, 2018 4:12, edited 1 time in total.
caseih
Posts: 1296
Joined: Feb 26, 2007 5:32

Re: SOLID programming

Postby caseih » Sep 19, 2018 4:11

dafhi wrote:I'm tempted to quit using the Private, but continue with properties. If people want to write to the 'variables with complicated names,' who am I to stop them :D
Yes, Python has completely converted me to this point of view. There's really no reason to use Private in my opinion. But I'm not in the business of writing opaque building blocks either.
caseih
Posts: 1296
Joined: Feb 26, 2007 5:32

Re: SOLID programming

Postby caseih » Sep 19, 2018 4:12

deleted; accidentally quoted instead of clicked on edit. When will this forum let me delete my posts!?
paul doe
Posts: 772
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: SOLID programming

Postby paul doe » Sep 19, 2018 10:15

caseih wrote:deleted; accidentally quoted instead of clicked on edit. When will this forum let me delete my posts!?

It does, if it's the last one of the thread. Which now it isn't =D
paul doe
Posts: 772
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: SOLID programming

Postby paul doe » Sep 19, 2018 12:50

dafhi wrote:Dependency Inversion is the one I struggled with in the beginning. So far, I'd say the points I *like* best are Single Responsibility (which i am now somewhat familiar with) and Interface Segregation (which was a piece of cake)

Oh, so Interface Segregation was 'a piece of cake'? Wait until you have to address some cross-cutting concern, and then we'll see if you keep thinking that =D
dafhi wrote:I'm tempted to quit using the Private, but continue with properties. If people want to write to the 'variables with complicated names' who am I to stop them :D

caseih wrote:Yes, Python has completely converted me to this point of view. There's really no reason to use Private in my opinion. But I'm not in the business of writing opaque building blocks either.

The use of properties without encapsulation is an oxymoron. Why would you want to do that? You simply expose the variable through the public interface and be done with it. Sounds nice and practical in theory, but it has several side effects one needs to consider. Very carefully. We'll address some of them now, talking about another one of the Pillars: Encapsulation. What is it? Why would you want to 'encapsulate'? And what exactly are you supposed to be encapsulating? More to follow.
dafhi
Posts: 1197
Joined: Jun 04, 2005 9:51

Re: SOLID programming

Postby dafhi » Sep 19, 2018 20:08

dafhi wrote:Oh, so Interface Segregation was 'a piece of cake'?

conceptually easy from a "customer is always right" view.

paul doe wrote:The use of properties without encapsulation is an oxymoron.
i misunderstood encapsulation. looking at the oop defiition, i see it includes the idea of restricting access to internal states.

paul doe wrote:Why would you want to do that?

Code: Select all

type MiBits_BASE
 
    ' Represent a 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, _mask, _cbits

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

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

operator MiBits_BASE.cast as bitrep_state
    return _val
End Operator


''
type MiBits_OPS extends MiBits_BASE
    declare property  cBits() as ubyte
end type

property MiBits_OPS.cBits as ubyte
    return _cbits
End Property

illegal member access, found '_cbits' in 'return _cbits'
fxm
Posts: 8347
Joined: Apr 22, 2009 12:46
Location: Paris (suburb), FRANCE

Re: SOLID programming

Postby fxm » Sep 19, 2018 20:30

Replace 'private:' by 'protected:'.
dafhi
Posts: 1197
Joined: Jun 04, 2005 9:51

Re: SOLID programming

Postby dafhi » Sep 19, 2018 20:41

thank you sir. one day i'd like to write an oop tutorial. i think once i learn all the keywords i'll be able to write something good
paul doe
Posts: 772
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: SOLID programming

Postby paul doe » Sep 20, 2018 12:40

dafhi wrote:conceptually easy from a "customer is always right" view.

Ah. You may want to look at a related concept, since it will help you in designing good interfaces (and classes): Design by Contract.

dafhi wrote:i misunderstood encapsulation. looking at the oop defiition, i see it includes the idea of restricting access to internal states.

Indeed. Not only for 'protecting' the state. It has other implications, that I'll talk about in a minute.
paul doe
Posts: 772
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: SOLID programming

Postby paul doe » Sep 20, 2018 12:48

Let's talk about another Pillar, Encapsulation.

Encapsulation, also known as Information Hiding, is a concept that conveys the notion that hiding 'data' inside objects keeps them decoupled from the rest of the system. The object manipulates this 'data' within its own boundaries; that way, the system is robust in the face of change. We already saw that Dependency Inversion is instrumental in keeping objects decoupled, acting as a kind of intermediate layer for them to communicate with each other, but this won't do you any good if the abstractions that the objects represent leak.

Joel Spolsky talks about this concept in this marvelous article (old, but still valid today): The Law of Leaky Abstractions.

When the concept of encapsulation was first introduced, it seemed like a sound idea. And indeed it is, but the problem with it begun when its original meaning was lost. Leo Brodie, in his wonderful book "Thinking Forth", explains the original concept quite well, in the section 'Hiding from whom?':

"Because modern mainstream languages give a slightly dierent meaning to the phrase 'information-hiding', we should clarify. From what, or whom are we hiding information? The newest traditional languages such as Modula 2 (can you imagine how old this book is? LOL!) bend over backwards to ensure that modules hide internal routines and data structures from other modules. The goal is to achieve module independence (a minimum coupling). The fear seems to be that modules strive to attack each other like alien antibodies. Or else, that evil bands of marauding modules are out to clobber the precious family data structures."

"This is not what we're concerned about. The purpose of hiding information, as we mean it, is simply to minimize the effects of a possible design change by localizing things that might change within each component."


It is especially noteworthy that Forth is not an object-oriented language. So, how comes that languages that aren't even object-oriented got this better that the ones that tout 'reusability' as one of their strengths? The problem lies not in the OO languages themselves -most of them implement mechanisms to achieve the needed encapsulation-, but in how the entire concept of object orientation was understood by the general public.

If one were to ask several people to explain the concept of object-orientation, most likely you'll end with widely differing views. One of the most common is:

"An object is a representative of a real-world entity, that has unique attributes called 'properties' that you can manipulate, and you can tell it to do stuff using commands called 'methods'"

Perhaps, when you first heard of this 'object-oriented paradigm', this was the first definition you came across. Sounds reasonable, right? Perhaps, but this definition leads to the 'everything is an object' kind of mentality (that languages like Java enforce to you). But let's dissect the above statement, so we can see why this definition is fundamentally flawed.

First, objects need not be 'representatives of real-world entities'. Which real world entity a 'BankAccount' class represent? Or an 'AcyclicGraphNode' class? Or a 'LinkedListItem' class? None, they're abstract concepts that get wrapped (read:encapsulated) onto a class for convenience. They may represent real world entities: a 'House' class, an 'Animal' class, or a 'Player' class, but need not to. How would you represent a mathematical function using a class? You don't, that's a concept that does not need encapsulation of any kind. Thus, the 'everything is an object' mentality is not a very good conceptual model to begin with, if only because it's very limited in its scope and does not allow for middle grounds.

So, you may ask, what is an object to you, then? This is how I conceive objects:

"An object is a code entity that represents a concept of the underlying problem domain and exposes functionality to work towards solving that problem"

Return to “Community Discussion”

Who is online

Users browsing this forum: No registered users and 2 guests