Jump to content
Mike Torrettinni

What is meant as 'implementation detail' vs 'non-implementation detail'?

Recommended Posts

I saw a phrase 'implementation detail' and I'm not sure what is meant, and why is it better then the 'other' (non-implementation) option... in this question:

 

https://stackoverflow.com/questions/52015619/how-to-change-tvirtualstringtree-node-icon-when-that-node-is-expanded

 

the comments say that checking if Node is expanded is better checked as:

Sender.Expanded[Node]

and not

vsExpanded in Node.States

First one is supposed to be 'implementation details', which is 'preferred' over the other option. I don't understand the meaning and the purpose, the main class can be defined in both, implementation and/or interface section of unit.

 

I would appreciate if someone can make some sense of this, for non-expert Delphi developers.

 

Thanks!

Share this post


Link to post

When facing terminology that you don't recognise I cannot recommend the use of websearch highly enough. 

 

https://enterprisecraftsmanship.com/2016/07/27/what-is-an-implementation-detail/

 

https://stackoverflow.com/questions/1777719/what-is-the-definition-of-an-implementation-detail

 

And no, it's the second excerpt that relies on leaking implementation details, which is why it should not be used. 

Edited by David Heffernan
  • Like 2
  • Thanks 1

Share this post


Link to post

Thanks, I think I got it. So to prevent leaking implementation:

- all class fields should be accessed through property read/write or methods, so make all fields Private

- " What matters is how many steps the client code must take in order to make the class do something useful for the client. If it's more than 1, it's a smell, and might be a sign of an implementation detail leaking out. " - I probably need more experience with classes to have this down to single method/property call

 

And in the example above, the second example accesses States field directly, which is leaking implementation details.

 

Of course, I have 'vsSelected/vsExpanded in Node.States' everywhere in my project :)

 

Oh, I had no idea that implementation detail is a real technical term, so didn't even think about searching for it.

 

Thanks!

  • Like 1

Share this post


Link to post
1 hour ago, Mike Torrettinni said:

I saw a phrase 'implementation detail' and I'm not sure what is meant, and why is it better then the 'other' (non-implementation) option... in this question:

 

https://stackoverflow.com/questions/52015619/how-to-change-tvirtualstringtree-node-icon-when-that-node-is-expanded

 

the comments say that checking if Node is expanded is better checked as:


Sender.Expanded[Node]

and not


vsExpanded in Node.States

First one is supposed to be 'implementation details', which is 'preferred' over the other option. I don't understand the meaning and the purpose, the main class can be defined in both, implementation and/or interface section of unit.

 

I would appreciate if someone can make some sense of this, for non-expert Delphi developers.

 

Thanks!

On the contrary: it is not preferred not to rely on implementation details. Implementation details are things you know about how a piece of software currently works, but which are not documented (although sometimes they are) and/or which may change in later versions. Do not rely on such details. Rather rely on public interfaces and documented traits of the software.

 

The latter of your examples is an implementation detail, i.e. that internally, the virtual tree uses Node.States. It may one day have a different way to denote that a node is expanded.

  • Thanks 2

Share this post


Link to post
2 hours ago, Rudy Velthuis said:

The latter of your examples is an implementation detail, i.e. that internally, the virtual tree uses Node.States. It may one day have a different way to denote that a node is expanded. 

Thanks, yes that is my understanding now. I hope my conclusions, in a post above yours, are correct, or at least going in right direction.

Accessing implementation details, methods instead of class fields directly, also allows overriding/extending methods into my own Expanded method - while overriding/extending States field you can't.

Share this post


Link to post
On 3/2/2019 at 12:13 PM, Mike Torrettinni said:

Accessing implementation details, methods instead of class fields directly, also allows overriding/extending methods into my own Expanded method - while overriding/extending States field you can't.

your explanation is even implementation-dependent! 🙂

 

I think you're missing the point. It's not about the means used to access something.

 

If you have a variable "fabc : integer" and somewhere you want to display it as a text value, 

 

fabc.ToString

 

depends on you KNOWING that fabc IS declared as an Integer, right? That's what meant by "implementation details".

 

There's no method being used here!

 

The GOAL is a solution where you do NOT KNOW HOW IT'S IMPLEMENTED. 

 

Is it an integer? A string? A float? A date? A means of querying a database? Something that sends a request to a SOAP or REST API to get a result back?

 

The point is, when you need that value in whatever format, you get it in that format, without having to KNOW how it's actually implemented.

 

There are some terms, "getter / setter" or "accessor / mutator" that refer to methods used to get and set values without regard to how they're implemented.

 

In Design Pattern terms, I believe these are called Adapters. Meaning, you use an Adapter pattern to adapt one thing to another.

 

No matter HOW fabc is defined within the class, you can be sure that a method like

 

function GetABC : string;

 

will ALWAYS return the contents of fabc as a string, regardless of how fabc is actually declared. And regardless of whether fabc is private, protected, public, or published.

 

This also goes under the more generic topic of "encapsulation" regarding object-oriented programming.

 

  • Thanks 1

Share this post


Link to post
2 hours ago, David Schwartz said:

function GetABC : string;

 

will ALWAYS return the contents of fabc as a string, regardless of how fabc is actually declared. And regardless of whether fabc is private, protected, public, or published.

Yes, that's how I understand it should be. Thanks for clarification, it is new world for me, and every little step is like new world is opening... some parts are easier to understand than others.

Share this post


Link to post
2 hours ago, David Schwartz said:

No matter HOW fabc is defined within the class, you can be sure that a method like

 

function GetABC : string;

 

will ALWAYS return the contents of fabc as a string, regardless of how fabc is actually declared. And regardless of whether fabc is private, protected, public, or published.

That's not true, and not helpful. The function can return anyrhing it likes. No reason where there has to be a field anywhere whose value is returned. 

Share this post


Link to post

 

3 hours ago, David Heffernan said:
6 hours ago, David Schwartz said:

No matter HOW fabc is defined within the class, you can be sure that a method like

 

function GetABC : string;

 

will ALWAYS return the contents of fabc as a string, regardless of how fabc is actually declared. And regardless of whether fabc is private, protected, public, or published.

That's not true, and not helpful. The function can return anyrhing it likes. No reason where there has to be a field anywhere whose value is returned. 

@David Heffernan and @David Schwartz: are you both talking about the same thing, just different verbiage and details?

Share this post


Link to post
7 hours ago, Mike Torrettinni said:

 

@David Heffernan and @David Schwartz: are you both talking about the same thing, just different verbiage and details?

I think what DH is saying is that there's nothing that prevents the author of the GetABC function from returning fxyz or FloatToStr(sin(2*pi*r*r)) or just a random string. That's true.

 

Reminds me of a time way back when color monitors were $4,000 and I was advocating for the use of colors to help highlight errors in red, success in green, and stuff like that. A contrarian guy on the team argued, "How do you know that someone won't display errors in green and successful results in red?"

 

Indeed, I'm working on a project now where we're porting an app from Oracle to PostgreSQL, and there are functions that are not returning what we're expecting. It's a major headache to track this stuff down.

 

Assuming there are some reasonable conventions being followed (which you should always verify if at all possible), then a function purporting to return a string version of a certain variable can be counted on to do that, while a function that returns an integer version will do that instead -- regardless of how that variable is implemented.

 

So if it's defined as a string, then the version of GetABC : string just returns fabc while the integer version returns StrToInt(fabc) -- and you need to ensure that fabc is supposed to be an integer and not a float.

 

The point is, the function acts as an adapter to convert whatever the implementation type is into whatever type the function returns. And if you change the implementation type, then code that accesses it directly will either fail to compile or fail at run-time, while the code that uses the adapter function will continue to work (assuming you update the function properly).

 

The function hides the implementation. 

 

Properties are just sugar coating that make accessors & mutators easier to use.

 

Edited by David Schwartz

Share this post


Link to post
1 hour ago, David Schwartz said:

The function hides the implementation. 

Not all of it. It only hides the integer, but it does not hide how you implemented conversion from integer to string. Also, string type itself leaks its implementation details in Delphi all over the place. 

 

You are going extra mile to hide just about everything - without real reasons. Point is, no matter how far you go, you can never hide all implementation details. In one way or another they will leak.

 

It is perfectly acceptable to have integer function or property as part of well defined public API.  The whole point of hiding implementation details, and not using them is that by doing so, you can change implementation without changing the public interface and without need to change all the code that relies upon it. That is the purpose of having well defined public API.

 

Hiding inner collection implementation (so you can easily replace plain array with list or some other structure) or some other more complex data is one thing, but hiding basic type like integer is going over the top without actually accomplishing anything.

 

One of the problems when it comes to following good practices like testing, proper OOP design... is that quite often those practices are explained and taught by going into extremes, making them overly complicated and losing original intent - making better, more maintainable code, with less bugs. That blowing things out of proportion, not only makes it harder to understand basic principles behind those practices but it also makes many developers just to drop the whole thing and not use any. It is hard to blame them when they see no point.

  • Like 2

Share this post


Link to post
4 hours ago, David Schwartz said:

I think what DH is saying is that there's nothing that prevents the author of the GetABC function from returning fxyz or FloatToStr(sin(2*pi*r*r)) or just a random string. That's true.

 

Reminds me of a time way back when color monitors were $4,000 and I was advocating for the use of colors to help highlight errors in red, success in green, and stuff like that. A contrarian guy on the team argued, "How do you know that someone won't display errors in green and successful results in red?"

 

Indeed, I'm working on a project now where we're porting an app from Oracle to PostgreSQL, and there are functions that are not returning what we're expecting. It's a major headache to track this stuff down.

 

Assuming there are some reasonable conventions being followed (which you should always verify if at all possible), then a function purporting to return a string version of a certain variable can be counted on to do that, while a function that returns an integer version will do that instead -- regardless of how that variable is implemented.

 

So if it's defined as a string, then the version of GetABC : string just returns fabc while the integer version returns StrToInt(fabc) -- and you need to ensure that fabc is supposed to be an integer and not a float.

 

The point is, the function acts as an adapter to convert whatever the implementation type is into whatever type the function returns. And if you change the implementation type, then code that accesses it directly will either fail to compile or fail at run-time, while the code that uses the adapter function will continue to work (assuming you update the function properly).

 

The function hides the implementation. 

 

Properties are just sugar coating that make accessors & mutators easier to use.

 

That's the not true part of what I am saying.

 

There's also the not helpful part of what you stated. I found your comment to be entirely unhelpful.

Share this post


Link to post

Non-implementation detail = the interface declaration - i.e. the black box UI - the what. 
Implementation detail = how an interface has been implemented - i.e. the inside of black box - the how.

 

You want to use the first, and avoid relying on details from the second.

  • Like 2
  • Thanks 1

Share this post


Link to post
57 minutes ago, Attila Kovacs said:

@Dalija Prasnikar What what the blog post about the leaking strings again? I can't find it anymore.

I cannot find it either... I don't think it was a blog post... maybe some post on G+ or somewhere else...

Share this post


Link to post
1 minute ago, Bill Meyer said:

No.

 

It was not about leaking strings, rather Delphi strings leaking their implementation details like character size, encoding... Think about going from TP fixed size string implementation to pre-Unicode Delphi string implementation to Unicode Delphi string implementation.

 

I remember writing about it, but don't remember where... 

Share this post


Link to post

I am not sure that I am buying Joel's TCP analogy.  It doesn't guarantee arrival, but it does guarantee that you as a sender will know if it arrived or not.

Share this post


Link to post

This guy is asking about basic concepts of encapsulation and you guys are going off the rails trying to explain the historical evolution of the bit arrangements of strings from early 1990's through today in TurboPascal -> 16-bit Delphi -> 32-bit Delphi -> Unicode Delphi, and using that to imply that the entire concept of encapsulation is bogus and can never be trusted.... never mind the fact that this 2019 and newbies are probably only using the latest (Unicode) versions of Delphi (and certainly not some old version of TP or 16-bit Delphi, but maybe a pre-Unicode version of Delphi).

 

So when you teach OOP to beginners now, do you say it's only about inheritance and polymorphism ... before you proceed to shred those concepts as well?

 

Or do you tell people not to use properties or Interfaces because technically speaking they cannot be trusted to last for 30 years since they don't anticipate language and platform changes accurately?

Share this post


Link to post
3 hours ago, David Schwartz said:

This guy is asking about basic concepts of encapsulation and you guys are going off the rails trying to explain the historical evolution of the bit arrangements of strings from early 1990's through today in TurboPascal -> 16-bit Delphi -> 32-bit Delphi -> Unicode Delphi, and using that to imply that the entire concept of encapsulation is bogus and can never be trusted.... never mind the fact that this 2019 and newbies are probably only using the latest (Unicode) versions of Delphi (and certainly not some old version of TP or 16-bit Delphi, but maybe a pre-Unicode version of Delphi).

 

So when you teach OOP to beginners now, do you say it's only about inheritance and polymorphism ... before you proceed to shred those concepts as well?

 

Or do you tell people not to use properties or Interfaces because technically speaking they cannot be trusted to last for 30 years since they don't anticipate language and platform changes accurately?

Sorry, but you started going off rails saying that you should not use fabc.ToString for displaying integer value as text, thus blowing the whole encapsulation and hiding the implementation thing out of proportion.

 

Now, it is possible that you just used poor example and that you actually tried to say something completely different. 

 

I am not trying to shred concepts to pieces, I am just trying to show their usage must be balanced. Abusing the concepts to the point of absurdity is as bad as not using them at all. What better example to show how not everything can be absolutely hidden and protected than to show that strings itself are leaking their implementation details. 

  • Like 1

Share this post


Link to post

Well, I probably chose the worst possible example to use in Delphi, and TBH, I didn't really think about it. As long as you stick with the default 'string' type, you're pretty safe ... most of the time. That's what I do, mainly so I don't have to think about it.

 

For someone learning the basics of OOP, strings and integers are obvious types to use for examples. 

 

I'm certainly not going to defend the presence of an intolerable plethora of string types where all sorts of things have been added to the language and RTL to try to smooth their incompatibilities.

 

If you guys don't like my examples, why don't you offer up better examples rather than picking my examples apart? I personally don't care, but the guy who's trying to figure this stuff out is probably watching with his jaw on the desk as a bunch of experts are trying to one-up each other showing off their knowledge of arguably obscure language features instead of helping a newbie understand basic concepts.

 

Y'all are brilliant when it comes to uncovering the source of problems rooted in really obscure issues, a skill I really appreciate being able to access from time to time. But that's not what this thread is about. So I'm  going to shut up now and let somebody else jump in ... if there's anything left of this poor fellow's desire to learn some basic OOP principles. 🙂

Share this post


Link to post

If only making good examples would be so simple.... it is so much easier to trash other people's examples...

 

Joking aside... bad examples (or ambiguous examples) can make more damage than good. OOP is complex matter and not always straightforward. Ask 100 experts about it and you will get 100 different answers. 

 

My rule of thumb is learn about OOP, design patterns, testing and then apply common sense over it. If some rules collide, then apply KISS (Keep it simple, stupid)  and YAGNI (You aren't gonna need it) and DRY (Don't repeat yourself) before any other principles. If it seems too convoluted or you don't have clear understanding why... better don't use it than use it in a wrong way. That does not mean people should not strive to learning and understanding better practices.

 

In that context, primary goal of encapsulation is protecting implementation details that can change to avoid subsequent changes in consumer code. 

 

When you write your own classes you are the boss, you can decide what is implementation detail and what is not. Of course, you have to be careful about those decisions because you will have to live with them for a long time. When you consume other people's classes you must avoid using things that are not part of public API. Since it is not always possible to hide all implementation details, it is possible that not everything that is publicly accessible is really part of documented public API. Unless public API is clearly documented, sometimes it can be hard to know what is implementation detail, and what is not. Experience can help, but it is hard to give any good general tips on the matter - it all depends on the actual code. And this is where original question came from - what is implementation detail in some code and why is better to use some construct comparing to another. 

 

Another thing to keep in mind is that OOP depend on the language. What fits in one, does not fit well in another. In Java encapsulation means having geter and setter methods for everything accessible - because Java does not have properties. If you expose field directly any changes in access logic that has to go through geter or setter will require changes in consumer code - because Java expects parens () when you access method.

 

In Delphi you might as well start with directly exposed field... changing that into property will not change consumer code (unless you add read or write restrictions) and adding geter or setter methods will also have no direct impact on consumer code. Of course, most Delphi developers will use properties instead of directly exposing fields, but this is more of common practice than actual requirement for fields that will be publicly accessible - part of the public API.

 

I could probably go on for days... but I have other things to do... 🙂 

  • Thanks 1

Share this post


Link to post
On 3/2/2019 at 4:24 PM, Mike Torrettinni said:

I saw a phrase 'implementation detail' and I'm not sure what is meant, and why is it better then the 'other' (non-implementation) option... in this question:

 

https://stackoverflow.com/questions/52015619/how-to-change-tvirtualstringtree-node-icon-when-that-node-is-expanded

 

the comments say that checking if Node is expanded is better checked as:


Sender.Expanded[Node]

and not


vsExpanded in Node.States

First one is supposed to be 'implementation details', which is 'preferred' over the other option. I don't understand the meaning and the purpose, the main class can be defined in both, implementation and/or interface section of unit.

 

I would appreciate if someone can make some sense of this, for non-expert Delphi developers.

 

Thanks!

A nice example of where people have to rely on the implementation (and no, this is not a Good Thing[tm]) is this Stack Overflow question and the answer:

 

> Use a TreeSet, which by default uses compareTo(), unlike a HashSet, which uses equals() and hashCode()

 

You'll have to know that while BigDecimals with values 0.1 and 0.100

  • compareTo() as the same value (return value 0), but
  • equal() (which tests for equal internal representation) returns false.
Edited by Rudy Velthuis

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×