Forum > General

Good explaination of how interfaces work.

<< < (2/5) > >>

MarkMLl:

--- Quote from: Mr.Madguy on October 02, 2022, 09:47:44 am ---Well, it's actually not my article, so it isn't my example. It's just copy-paste of my post from some other thread about interfaces. Yeah, reference counting is nice thing, but it's not mandatory. One can control object's life time by himself, if he desires to do it.

--- End quote ---

Don't worry: I'm definitely not blaming you. But like so many things these days the article is written by somebody who knows what he's explaining, not by somebody who knows how to explain it (if you get my drift).

I've just been taking a look at the CORBA article on Wp (and I've been struggling to get my head around the applicability of this stuff since CORBA, IDL etc. came out in the early '90s). Neither article starts off with a succinct explanation of what an external component might encapsulate, why one can benefit from using that external component, and the reasons that going through something like CORBA is a benefit (e.g. data reorganisation determined by what's in the IDL).

So while I appreciate that while it can be used as a sort of "super RPC" remotely, and that locally it's an alternative to class multiple inheritance, generally speaking I don't see what the fuss is about.

MarkMLl

Mr.Madguy:

--- Quote from: MarkMLl on October 02, 2022, 10:11:35 am ---Don't worry: I'm definitely not blaming you. But like so many things these days the article is written by somebody who knows what he's explaining, not by somebody who knows how to explain it (if you get my drift).

I've just been taking a look at the CORBA article on Wp (and I've been struggling to get my head around the applicability of this stuff since CORBA, IDL etc. came out in the early '90s). Neither article starts off with a succinct explanation of what an external component might encapsulate, why one can benefit from using that external component, and the reasons that going through something like CORBA is a benefit (e.g. data reorganisation determined by what's in the IDL).

So while I appreciate that while it can be used as a sort of "super RPC" remotely, and that locally it's an alternative to class multiple inheritance, generally speaking I don't see what the fuss is about.

MarkMLl

--- End quote ---
Everybody comes to interfaces sooner or later, as complexity of his tasks grows more and more. It's both good way to implement more complex relations between objects, than standard OOP allows, and to automatically manage objects' life time in order to avoid memory leaks and "use after free" errors without using safe languages, like Rust. And at the end separating declaration from implementation - is actually good practice. Because one can never know, if he will need to split his program into several modules or not.

P.S. And of course RPC. But I don't treat it as major reason, why interfaces appeared. It's just nice bonus.

MarkMLl:

--- Quote from: Mr.Madguy on October 02, 2022, 10:32:23 am ---Everybody comes to interfaces sooner or later, as complexity of his tasks grows more and more. It's both good way to implement more complex relations between objects, than standard OOP allows, and to automatically manage objects' life time in order to avoid memory leaks and "use after free" errors without using safe languages, like Rust. And at the end separating declaration from implementation - is actually good practice. Because one can never know, if he will need to split his program into several modules or not.

--- End quote ---

Well, I've been using Delphi etc. for substantially more than 20 years, and from my POV there are far more pressing omissions in the language etc. than this addresses.

I mean, if somebody could demonstrate that this provided a better way of getting at a remote database than ODBC, or a better way of interfacing with a remote user than X11 or Wayland, then I think there would be compelling reasons for promoting it.

MarkMLl

Mr.Madguy:

--- Quote from: MarkMLl on October 02, 2022, 10:43:26 am ---Well, I've been using Delphi etc. for substantially more than 20 years, and from my POV there are far more pressing omissions in the language etc. than this addresses.

I mean, if somebody could demonstrate that this provided a better way of getting at a remote database than ODBC, or a better way of interfacing with a remote user than X11 or Wayland, then I think there would be compelling reasons for promoting it.

MarkMLl

--- End quote ---
Delphi/FPC can be turned into pseudo-scripted language via: 1) Wrapping all simple types into objects to make them type-compatible (optional) 2) Using interfaces for everything in order to achieve auto object life time management 3) Making good data management library, i.e. implement lists, dictionaries, stacks, queues, etc. Simply because data management - is very common goal. Such as "For number-letter pairs make list of all letters, that has the same number". It's just way too tedious to always rewrite such code from scratch. It should be standard.

And again. Interfaces aren't about RPC only. It's just nice bonus. It's all about protocols. Nothing stops you from wrapping X or Wayland into interfaces. Question is - is it needed?

Warfley:
First of all, forgett about multi inheritance, this most cerntainly only complicates it (I don't know why people think that this is a good explaination, inheritance in itself is much more complex to expalin than interfaces).

To make it simple, at it's core, an interface is very simple, it is a promise that the class you are writing contains a certain set of functions (and properties). Then using this interface in your code is equivalent to saying: "I don't care what kind of type something is as long as it holds to the promise made in the interface".

For example:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---type  IPrintable = interface  function ToString: String;  end;Any class that uses this interface promises that there will be a "ToString" function. So for example, if you write a logger, which shall log whenever some class get's created, the logger does not care what class that is, as long as it can write it to the log file. Therefore the logger would have something like this:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---procedure TLogger.LogClassCreation(CreatedClass: IPrintable);begin  LogLine('New Class instance Created: ' + CreatedClass.ToString);end;As you can see the LogClassCreation does only need the ToString function and nothing else, therefore it only requires a promise that the class will have this method, and this promise is made by having the type of the parameter being the Interface that creates this promise.

In this respect Interfaces are the same across most languages, and it is also like in golang.

Now comes the complicated part. Pascal has two types of interfaces, CORBA and COM. CORBA interfaces are the "raw" interfaces, which are basically just the promise I described above. To use them simply add a {$Interfaces CORBA} to your unit and you have the simple, raw, go-like interfaces.

The other (default) form of interfaces are COM interfaces, which basically add reference counting to it. If a class implements a COM interface it must also implement the _AddRef, _Release and and QueryInterace function. There you would need to implement your own reference counter. But, to make it simple, FPC provides the TInterfacedObject Class, which already implements those functions, so if you inherit from it, you don't have to care about it.

The "problem" with COM interfaces is, as they are reference counted, but normal pascal classes are manually managed, you cannot combine those two paradigms as easiely. So if you have a COM interface, it's best to only touch it via the interface. So for example, while this code is completely fine with CORBA interfaces:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---program Project1; {$mode objfpc}{$H+}{$Interfaces CORBA} type  IPrintable = interface  function ToString: String;  end;   TTest = class(TInterfacedObject, IPrintable)  public    function ToString: ansistring; override;  end; procedure LogClassCreation(CreatedClass: IPrintable);begin  WriteLn('New Class instance Created: ' + CreatedClass.ToString);end;  function TTest.ToString: ansistring;begin  Result := 'TTest';end; var  t: TTest; begin  t := TTest.Create;  try    LogClassCreation(t);  finally    t.Free;  end;end. When interfaces change the [$Interfaces CORBA} to COM (or delete it as COM is the default), it will cause a double free, because when entering "LogClassCreation", the reference counter is increased to 1, and when leaving it dropps to 0 and is therefore freed, even though the main code still has the reference "t" but because this is not a COM interface reference, it does not count into the reference counter.
Therefore when you use something as a COM interface once, it's best to use it always as a COM interface.

So while COM and CORBA in principle serve the same purpose, to give promises about functionality, generally CORBA interfaces are much simpler and are more like your Go interfaces, while COM provide powerful reference counting, but on the other hand are not as easiely compatible with "normal" pascal code.
This also means that if you want to use many small interfaces (for encapsulation), COM is usually not that useful for that, because you must always be handling at least one COM interface, so if you only have a lot small ones, you need to do a lot of conversions (because remember, you always must touch the object as an interface), while with CORBA, you handle your object like a normal Pascal object for the most parts, and only use it as the interface when needed.

For me the rule of thumb is: I always use CORBA when I want to use interfaces as the classical "promise", and COM only if I need a reference counted pointer (smartpointer) for my class, and in those cases my COM interface is basically just a complete copy of the "public" parts of the class I am writing, and I just use this rather than the class itself.

An example where the latter might come in useful is for example the GMP Unit:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---var a, b: MPInteger;begin  a := 42;  b := 3;  a := a * b + b;end;Here MPInteger is a COM interface, and therefore, even though a and b are classes, because they are reference counted, there is no need of writing ".Free" anywhere and you can use operators. But you need to always use a and b as MPInteger and never as the underlying class, because otherwise you would run into memory management issues

Navigation

[0] Message Index

[#] Next page

[*] Previous page

Go to full version