Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Divide a file to multiple files

11 views
Skip to first unread message

Daniel Szasz

unread,
Jul 8, 2001, 5:44:17 AM7/8/01
to
Hello

I have a file with more than 20 classes and objects. Most of them have
properties from the type of other objects like that :

type
TB= class
TC= clase
TA = object
b : TB;
c : TC;
end;
TB = object
a : TB;
c : TC;
end;
TC = object
b : TB;
a : TA;
end;
.....

the file of course is growing more and more and I'm looking for a way to
break it in small files where each class have it's own file...


thanks

dan


Raoul De Kezel

unread,
Jul 8, 2001, 5:13:48 AM7/8/01
to
In article <3b481d24_1@dnews>, dan...@hotmail.com says...

> the file of course is growing more and more and I'm looking for a way to
> break it in small files where each class have it's own file...

Forget it.

I think I tried all known ways to break inter-unit type cycles,
and they were all inconvenient or unsafe.

Please, Borland, add forward declarations of class types declared
in other units. Pleeeeeeze.

--- Raoul

Philip Brown

unread,
Jul 8, 2001, 6:27:19 AM7/8/01
to
I use include files (one for each class interface and another for the class
implementation) to get around this. It's not ideal, but it's not unsafe
either. The main issue is that Code Insight doesn't work well inside the
include files.

One way around this is to use interfaces in one unit to declare all
interfaces you need, and then implement separate units for each
interface-wrapper class. But that's not ideal either.

----
Philip Brown
Informatica Systems Ltd

"Raoul De Kezel" <raould...@hotmail.com> wrote in message
news:MPG.15b242bae...@forums.inprise.com...

John Elrick

unread,
Jul 8, 2001, 11:56:10 AM7/8/01
to
"Daniel Szasz" <dan...@hotmail.com> wrote in message
news:3b481d24_1@dnews...

> Hello
>
> I have a file with more than 20 classes and objects. Most of them have
> properties from the type of other objects like that :
SNIP

> the file of course is growing more and more and I'm looking for a way to
> break it in small files where each class have it's own file...

Curious, Daniel. What specifically are the problems you are experiencing
that are introduced by the very large unit?

I've been working on some plugins to assist in managing units and would
appreciate your specific observations.


John Elrick


Raoul De Kezel

unread,
Jul 8, 2001, 12:33:47 PM7/8/01
to
In article <3b488345_2@dnews>, jel...@adelphia.net says...

> Curious, Daniel. What specifically are the problems you are experiencing
> that are introduced by the very large unit?

1. Every class has access to the implementation of all other classes
with which they have a (perhaps indirect) relationship. Therefore, the
distinction between implementation, public and protected interfaces tends
to be blurred. Mistakes (or laziness) is discovered late in the development
process because most clients of these classes are themselves in the gigantic
unit.

2. Members of the development team continuously compete to checkout that core
unit. Therefore we sometimes check it in back as soon as possible to make it
available for others. This breaks our policy that only finished, unit tested
code is checked in the shared code repository.

3. Somewhat hard to navigate and print out.

I consider 1 & 2 the most important problems.

> I've been working on some plugins to assist in managing units and would
> appreciate your specific observations.

Could you tell us more about your work ? Thanx.

--- Raoul

Dennis Landi

unread,
Jul 8, 2001, 1:18:19 PM7/8/01
to
Why are these twenty classes so tightly coupled?
Is it necessary? Why is there this proliferation of tightly linked classes?
Consider a different design.

Why don't you start with putting each new class in its own unit and start
with that "design" constraint and see where that takes you... Play
around... experiment...

-dennis

"Daniel Szasz" <dan...@hotmail.com> wrote in message
news:3b481d24_1@dnews...

Dennis Landi

unread,
Jul 8, 2001, 1:19:40 PM7/8/01
to
uh, um, this just looks like bad code management to me. I might have given
you guy the benefit of the doubt if you were talking about one humungous
class. If you are talking about stuffing a single unit with many large
classes and then complaining about the size of the unit... well... don't
do that.

Put each class in its own unit. If the class becomes so big that you unit
is becoming unmanageable, consider breaking your class into smaller (perhaps
inherited) classes.


"Raoul De Kezel" <raould...@hotmail.com> wrote in message

news:MPG.15b2a9da7...@forums.inprise.com...

Raoul De Kezel

unread,
Jul 8, 2001, 2:05:40 PM7/8/01
to

> Why are these twenty classes so tightly coupled?

You see, classes are sometimes used to model physical or
conceptual objects out there in The Real World (tm). And these
objects have the nasty habit to enjoy many relationships between
them.

We want to model these relationships as accurately as possible, even
at the price of strong coupling between classes. For deviations of the
model from the underlying reality are often paid a high price sooner
or later.

This is in sharp contrast with the more common use of classes as
a software structuring tool. In this later case, of course, we all
avoid dependencies as much as possible in order to increases flexibility
and reuse.

> Put each class in its own unit.

We WANT to do that, but CANNOT.

How do you implement 1-N or M-N relationships between classes declared
in different units ?

--- Raoul


Dennis Landi

unread,
Jul 8, 2001, 2:55:05 PM7/8/01
to
"Raoul De Kezel" <raould...@hotmail.com> wrote in message
news:MPG.15b2bf5f4...@forums.inprise.com...

> > Put each class in its own unit.
>
> We WANT to do that, but CANNOT.
>
> How do you implement 1-N or M-N relationships between classes declared
> in different units ?
>
By creating a "Link" class. Have a look at Borland's tDataLink and how it
plays an intermediary role between tDataset and tDatasource. Yep, that's
right these two crucial classes are not tightly coupled.

I recall that Ray Konopka fully explained tDataLink in the first and second
editions of his book "Developing Custom Delphi Components" (or something
like that), circa 1996 and 1997.

So that's a Real World answer.

From Academia, you may want to consider GoF's Proxy pattern. Way before I
discovered GoF I created link classes with the "proxy" appelation to
specifically implement decoupling. My inspiration at the time was
tDatalink...

-dennis


Raoul De Kezel

unread,
Jul 8, 2001, 3:50:56 PM7/8/01
to
In article <3b48aeb0_2@dnews>, dland...@yahoo.com says...

> > How do you implement 1-N or M-N relationships between classes declared
> > in different units ?
> >
> By creating a "Link" class. Have a look at Borland's tDataLink and how it
> plays an intermediary role between tDataset and tDatasource. Yep, that's
> right these two crucial classes are not tightly coupled.

I'm afraid I still dont see how to put TDataLink, TDataSource, TDataSet each
in its own unit. Any idea ?

type

TDataLink = class;
TDataSource = class;
TDataSet = class;

TDataLink = class(TPersistent)
public
property DataSet: TDataSet read GetDataSet;
property DataSource: TDataSource read FDataSource write SetDataSource;
end;

TDataSet = class(TComponent, IProviderSupport)
public
function IsLinkedTo(DataSource: TDataSource): Boolean;
end;

TDataSource = class(TComponent)
public
function IsLinkedTo(DataSet: TDataSet): Boolean;
end;

More generally, assume class types TA in unit A, TB in unit B and
link class TAB in unit AB. Type declaration TAB needs both types
TA and TB. Therefore unit AB uses units A and B in interface section.
Therefore, unit A cannot use unit AB in interface section. Therefore
type TA cannot use type TAB. Notably, no method of TA can use type
TAB. Therefore, it is impossible to ask to an object of type TA any
question about a TAB. Symetry extends the conclusion to TB. I have
to ask such questions.

--- Raoul

Dennis Landi

unread,
Jul 8, 2001, 3:52:27 PM7/8/01
to
Making me code, eh?

Let me see if I can whip something up for you tonight. I will send you a
sample project if I can "crack" the problem!

-dennis

"Raoul De Kezel" <raould...@hotmail.com> wrote in message

news:MPG.15b2d8072...@forums.inprise.com...

Rémon Sinnema

unread,
Jul 8, 2001, 4:04:03 PM7/8/01
to
"Raoul De Kezel" <raould...@hotmail.com> schreef in bericht
news:MPG.15b2a9da7...@forums.inprise.com...

> 2. Members of the development team continuously compete to checkout that
core
> unit. Therefore we sometimes check it in back as soon as possible to make
it
> available for others.

You may want to check out a version control system that does not require
exclusive locking of files, like cvs (www.cvshome.org). I've used it for an
extended period of time now, and found the number of merging conflicts to be
very small. I wouldn't want to go back to locking anymore.


> 3. Somewhat hard to navigate and print out.

Navigational problems may be lessened by using a source documentation tool
like Time2Help (www.time2help.com) and integrating the help in the Delphi
help system.

I hardly ever print out source code. Therefore, I'm curious why you would
want to do that?

Raoul De Kezel

unread,
Jul 8, 2001, 5:18:28 PM7/8/01
to
In article <3b48bda3_2@dnews>, rssi...@yahoo.com says...
> I found the number of merging conflicts to be

> very small. I wouldn't want to go back to locking anymore.

We use SourceSafe, which also allows multiple checkouts, and we do that
sometimes. I certainly agree that merging conflicts are uncommon and most
are easily resolved. However, such checkins still require careful attention,
sometimes from both authors of changes, at a time when the details are no
longer in memory. A bit too error-prone for my taste, especially given that
our goal is error-free shared code.

> Navigational problems may be lessened by using a source documentation
> tool like Time2Help (www.time2help.com)

Thanks a lot. I will have a look.

> I hardly ever print out source code. Therefore, I'm curious why you would
> want to do that?

Well, probably it's just me. When proof-reading my new code or doing
peer review, I just find more bugs on paper than on screen. Perhaps
because the screen is a great tool for browsing, while paper is great
to study completely through in a calm, isolated place.

I also tend to print and read completely an old, stable class before
making a change, to remind me of its overall design philosophy and see
how to cleanly integrate the modification. Not always, I'm afraid, while
I should :-(


--- Raoul

Peter Below (TeamB)

unread,
Jul 8, 2001, 5:41:02 PM7/8/01
to
In article <3b48bda3_2@dnews>, Rémon Sinnema wrote:
> I hardly ever print out source code. Therefore, I'm curious why you would
> want to do that?
>

I always do code reviews on paper. For some reason i find way more errors
this way.

Peter Below (TeamB) 10011...@compuserve.com)
No e-mail responses, please, unless explicitly requested!
Note: I'm unable to visit the newsgroups every day at the moment,
so be patient if you don't get a reply immediately.

Dennis Landi

unread,
Jul 8, 2001, 5:31:02 PM7/8/01
to
> > I hardly ever print out source code. Therefore, I'm curious why you
would
> > want to do that?
>
> Well, probably it's just me. When proof-reading my new code or doing
> peer review, I just find more bugs on paper than on screen. Perhaps
> because the screen is a great tool for browsing, while paper is great
> to study completely through in a calm, isolated place.

I like to do that too... <g>


Dennis Landi

unread,
Jul 8, 2001, 6:18:25 PM7/8/01
to
Raoul,
I sent a zip file to you about 25 minutes ago. Let me know if you got it.
-dennis


Daniel Szasz

unread,
Jul 9, 2001, 4:45:51 AM7/9/01
to
First I want to thanks to all of the answers.

Dennis ..I will be glad to get the example too .......
thanks in advance

Daniel


"Dennis Landi" <dland...@yahoo.com> wrote in message
news:3b48de58_1@dnews...

Jannie Nel

unread,
Jul 9, 2001, 4:37:28 AM7/9/01
to
>Dennis ..I will be glad to get the example too .......
>thanks in advance
>


So would we. Can you perhaps post it to borland.public.attachments ? That
would be great.

Thanks.


Raoul De Kezel

unread,
Jul 9, 2001, 4:46:01 AM7/9/01
to
In article <3b48de58_1@dnews>, dland...@yahoo.com says...

> Raoul,
> I sent a zip file to you about 25 minutes ago. Let me know if you got it.

Thanks, Dennis, I got it.

You derive the concrete link class from an abstract link ancestor.
The objects in relation expose the abstract link only, in order to
break the circular dependency.

Been there, tried that, trashed it. Client code will be full of downcasts.
Inconvenient and unsafe, as I said in my answer to Daniel :-)

Anyway, thanks. Pleeeze tell me if you ever find another solution to
this problem.

--- Raoul


Daniel Szasz

unread,
Jul 9, 2001, 5:55:15 AM7/9/01
to
Raoul..

Please can u send me the example ?

Thanks in advance

Daniel


"Raoul De Kezel" <raould...@hotmail.com> wrote in message

news:MPG.15b38db5a...@forums.inprise.com...

Rudy Velthuis (TeamB)

unread,
Jul 9, 2001, 6:58:49 AM7/9/01
to
In article <VA.0000744...@antispam.compuserve.com>, Peter Below
(TeamB) says...

> In article <3b48bda3_2@dnews>, Rémon Sinnema wrote:
> > I hardly ever print out source code. Therefore, I'm curious why you would
> > want to do that?
> >
>
> I always do code reviews on paper. For some reason i find way more errors
> this way.

I do the same. One can oversee more on paper than on a screen, even if it
is a 21" flat screen. <g>
--
Rudy Velthuis (TeamB)

Dennis Landi

unread,
Jul 9, 2001, 7:29:50 AM7/9/01
to
I'll give you a explanation later on an extended "safer" version of this
later today.
As for convenience, you will have to judge that for yourself...

People please don't clog this newsgroup or my mail box for requests for a
copy.

I will post a link on my website. I will let you know, here, when I do
that...

-dennis

"Raoul De Kezel" <raould...@hotmail.com> wrote in message

news:MPG.15b38db5a...@forums.inprise.com...

Franz-Leo Chomse

unread,
Jul 9, 2001, 8:29:57 AM7/9/01
to

>I hardly ever print out source code. Therefore, I'm curious why you would
>want to do that?

its much more easier to check your source. In most cases you have to
follow the flow of execution through more than one class unit,
together with the M$ documentation (On screen), Delphi or third party
tool help files ....

Switcheing between several piles of paper is easier than jumping from
on screen to another.

Regards from Germany

Franz-Leo

Raoul De Kezel

unread,
Jul 9, 2001, 9:12:45 AM7/9/01
to
In article <3b4997da$1_1@dnews>, dland...@yahoo.com says...

> I'll give you a explanation later on an extended "safer" version of this
> later today.

Great, Dennis, thanx.

Actually, I know of a safe, conceptually clean solution (IMO). I never
dared to use it on a routine basis.

Unit A;

interface

type
TA = class
public
// whatever
end;

TAs = class
public
procedure Add(a : TA);
function Count : Integer;
property Item[i : Integer] : TA;
end;

implementation
uses AB; // likely needed

end.

Unit B;

interface

type
TB = class
public
// whatever
end;

TBs = class
public
procedure Add(b : TB);
function Count : Integer;
property Item[i : Integer] : TB;
end;

implementation
uses AB;

end.

Unit AB;

interface

uses A,B;

procedure AddRelation(a : TA; b : TB);
procedure RemoveRelation(a : TA; b : TB);
function IsARelatedToB(a : TA; b : TB) : Boolean;
function GetAsRelatedToB(b : TB) : TAs;
function GetBsRelatedToA(a : TA) : TBs;

implementation

// somewhat difficult and/or slow implementation.
// The relation is likely implemented with a singleton.

--- Raoul

John Elrick

unread,
Jul 9, 2001, 9:30:29 AM7/9/01
to
"Raoul De Kezel" <raould...@hotmail.com> wrote in message
news:MPG.15b2a9da7...@forums.inprise.com...

> In article <3b488345_2@dnews>, jel...@adelphia.net says...
> > Curious, Daniel. What specifically are the problems you are
experiencing
> > that are introduced by the very large unit?
>
> 1. Every class has access to the implementation of all other classes
> with which they have a (perhaps indirect) relationship. Therefore, the
> distinction between implementation, public and protected interfaces tends
> to be blurred. Mistakes (or laziness) is discovered late in the
development
> process because most clients of these classes are themselves in the
gigantic
> unit.

Very real issue. One advantage to encapsulation is to prevent mistakes that
stem from taking shortcuts.

>
> 2. Members of the development team continuously compete to checkout that
core
> unit. Therefore we sometimes check it in back as soon as possible to make
it
> available for others. This breaks our policy that only finished, unit
tested
> code is checked in the shared code repository.

No ideas here.

>
> 3. Somewhat hard to navigate and print out.
>
> I consider 1 & 2 the most important problems.
>
> > I've been working on some plugins to assist in managing units and would
> > appreciate your specific observations.
>
> Could you tell us more about your work ? Thanx.

Number 3 is the one I've been playing with the most. If you are familiar
with GExpert's Procedure List function, I wrote a plugin that added some
significant functionality that I felt was missing (ability to filter on
AND'd keywords, filter the unit code itself in realtime, preview the source
of the selected method, view methods in the list that are not implemented -
abstract or interfaces, include/exclude class names from the search), it's a
CodeRush plugin and is posted to the third party page.

I've been examining the plug in to see how I can make it more useful and was
curious for your input.


John Elrick


Jan Nordén

unread,
Jul 9, 2001, 8:23:35 AM7/9/01
to
I'm afraid I don't have an answer, we would all like to see "real" forward
declarations i OP (After all a class reference is only a pointer, so the
interface
section really doesn't need to know more).

While we are at it I have another (related) wish, and that is that Borland
would allow the private parts of classes to use types defined
in the implementation section, or in units in the implemetnation sections
uses claue. This is logical since private parts can only be accessed in the
implementation section.

i.e

interface
type
TA = class

fA: TList
fB: TB;
end;

implementation
uses
Classes;
TB = class
....

"Daniel Szasz" <dan...@hotmail.com> wrote in message
news:3b481d24_1@dnews...

Dennis Landi

unread,
Jul 9, 2001, 9:30:10 PM7/9/01
to
I have to admit, I just don't get what you are trying to do here... Try
explaining in words.

(I am working on that extended version of my abstractLink class for you
now.)
-dennis


"Raoul De Kezel" <raould...@hotmail.com> wrote in message

news:MPG.15b3cc3b6...@forums.inprise.com...

Dennis Landi

unread,
Jul 10, 2001, 12:21:30 AM7/10/01
to
Here is an updated version of my tAbstractLink class, IOW, use of interfaces
to implement "unit abstraction" thus breaking that horrific "circular
referencing" constraint. Raoul, you will notice that now no extra
downcasting is required... You should be able to refine this example
considerably.

Instead of posting on my website here is a minimalist rendering of all four
units.

-dennis


*****Unit iProxyLink***************

unit ProxyLink;

interface

type

iProxyLink = interface
{class specific methods...}
end;

implementation

end.

**********************************

**********Unit ABLink*************

unit ABLink;

interface

uses ProxyLink, UnitB, UnitA;

type tABLink = class(tInterfacedObject,IProxyLink)
private
fA : tAObject;
fB : tBObject;

protected
function GetA: tAObject;
procedure SetA(A:tAObject);

function GetB: tBObject;
procedure SetB(B:tBObject);

public
property A : TAOBject read getA write setA;
property B : TBOBject read getB write setB;
end;

implementation

{ tABLink }

function tABLink.GetA: tAObject;
begin
end;

function tABLink.GetB: tBObject;
begin
end;

procedure tABLink.SetA(A: tAObject);
begin
end;

procedure tABLink.SetB(B: tBObject);
begin
end;

end.

**********************************


**********UnitA*******************

unit UnitA;

interface

uses ProxyLink;

type tAObject = class(tObject)
private
fLink : iProxyLink;
public
function GetLink: iProxyLink;
procedure SetLink(Link:iProxyLink);

end;

implementation

uses ABLink;
{ tAObject }

function tAObject.GetLink: iProxyLink;
begin
result := fLink;
end;

procedure tAObject.SetLink(Link: iProxyLink);
begin
fLink := Link ;
end;

end.

**********************************

**********UnitB*******************

unit UnitB;

interface

uses ProxyLink;

type tBObject = class(tObject)
private
fLink : iProxyLink;
public
function GetLink: iProxyLink;
procedure SetLink(Link:iProxyLink);

end;


implementation

uses ABLink;

{ tBObject }

function tBObject.GetLink: iProxyLink;
begin
result := fLink;
end;

procedure tBObject.SetLink(Link: iProxyLink);
begin
fLink := Link ;
end;

end.

**********************************


Raoul De Kezel

unread,
Jul 10, 2001, 5:24:16 AM7/10/01
to
In article <3b4a82e8_1@dnews>, dland...@yahoo.com says...

> Here is an updated version of my tAbstractLink class,

Thanx, Dennis, your work is appreciated.

I have trouble to see how, given a tAObject, client code gets
the linked tBObject. Otherwize said :

uses ProxyLink, ABLink, ALink, BLink;

function GetLinkedB(A : TAObject) : TBObject;
begin
Result := A.GetLink.// what do I write here ?
end;

As another example, I dont see how to reframe TDataSet/TDataSource
and still allow client code to ask whether a given DataSource
is linked to a given DataSet.

More generally, as I see it, most interesting questions - which are
I guess to be declared in iProxyLink - will refer to types TAObject
and/or TBObject, leading back to the infamous cycle.

Kind regards

--- Raoul

Raoul De Kezel

unread,
Jul 10, 2001, 6:35:39 AM7/10/01
to
In article <3b4a5cd0_1@dnews>, dland...@yahoo.com says...

> I have to admit, I just don't get what you are trying to do here... Try
> explaining in words.
>

Ok, I'll do my best.

Assume we have Persons and Books. Assume also that for
some reason an application wants to track who did read
which books, that is, the application tracks the MxN
relationship Readings(Person, Book)

The traditional OP way of representing persons and books
is to have a class TPerson (or perhaps TReader) and
a class TBook. So far so good.

The traditional way of representing the relation is
to break it in two halves. A property of the TPerson class
will answer the set of books a person instance did read
in its life. A property of the TBook class will answer
the set of present and past readers of a given book.

It is this later way which leads to the infamous dependency
cycle.

The cycle can be removed if we use a different - IMO cleaner -
representation.

In one unit, we have TPerson, which has no business to know
about TBooks (and TCars, and THouses, and who knows what else).

In another unit, we have TBook, which has no business to know
about TPersons (and TPrintingPress, and ...).

In a third unit, we have TReadings, whose business is to
represent the Readings relationship. That unit obviously has
to know about TPersons and TBooks. In practice, there is a
single object of type TReadings, itself a set of TReading
(your link class).

Utility functions *in this third unit* answer common questions,
such as the set of books a guy did read. The cycle is broken.

When this view is pushed to its extremist logical conclusion,
objects have no attribute. They just have an identity and are
in relationship with other objects.

This view is however so remote from OO that the whole idea is best
forgotten for anybody working in Object Pascal. Also, the work needed
to implement the relationship is important. That prevents the idea to
be used systematically. It has its niche, however.

I hope I have been clearer. For relational db people, TReading is
a tuple and TReadings is the body of the relation. I guess that for
logic programming guys, TReading is a fact and TReadings a set of
facts.

Kind regards,

--- Raoul

Rick Rogers (TeamB)

unread,
Jul 10, 2001, 8:48:27 AM7/10/01
to
On Sun, 8 Jul 2001 11:13:48 +0200, Raoul De Kezel
<raould...@hotmail.com> wrote:

> Please, Borland, add forward declarations of class types declared
> in other units. P

Already possible. Use interfaces.
--
Rick Rogers (TeamB); Fenestra Technologies, http://www.fenestra.com/
Use Borland servers; posts via others are not seen by TeamB.
For more info, see http://www.borland.com/newsgroups/genl_faqs.html

Dennis Landi

unread,
Jul 10, 2001, 10:07:19 AM7/10/01
to
Raoul,
This is my finally installment, otherwise this is becoming a part time job
<g>. I cruise this form so I *don't* have to write code, generally.

Here is a full example. I have further fleshed the use of interfaces to
make it clearer, I hope. I have put each interface in their own unit as
well, since this thread is about separating any and all classes into
discrete units. Below are units for two Objects: AObject, BObject and two
units for their respective interfaces: IALink, IBLink; one unit for the link
class: ABLink and a unit for its interface IProxyLink; and finally a snipped
unit which is the little demo form with a button.

Hope that helps.

-dennis

***************************************************
unit ProxyLink;

interface

type

iProxyLink = interface
{class specific methods...}
end;

implementation

end.
***************************************************
***************************************************
unit ABLink;

interface

uses ProxyLink, ALink, BLink;

type tABLink = class(tInterfacedObject,IProxyLink)
private

fA : iALink;
fB : iBLink;
protected
procedure SetA(A:iALink);
procedure SetB(B:iBLink);
public
constructor create(A:IALink;B:IBLink);
property A : iALink read fA write setA;
property B : iBLink read fB write setB;
end;

implementation

{ tABLink }

constructor tABLink.create(A:IALink;B:IBLink);
begin

fA := A;
fB := B;

end;

procedure tABLink.SetA(A: iALink);
begin
end;

procedure tABLink.SetB(B: iBLink);
begin
end;

end.

***************************************************
***************************************************
unit ALink;
interface
uses ProxyLink, BLink;
type
iALink = interface


function GetLink: iProxyLink;
procedure SetLink(Link:iProxyLink);

procedure DoSomething;
function ReturnSomething:boolean;
end;
implementation
end.
***************************************************
***************************************************
unit UnitA;
interface
uses ProxyLink, ALink;
type tAObject = class(tInterfacedObject,iALink)


private
fLink : iProxyLink;
public
function GetLink: iProxyLink;
procedure SetLink(Link:iProxyLink);

procedure DoSomething;
function ReturnSomething:boolean;


end;
implementation
uses ABLink;
{ tAObject }

procedure tAObject.DoSomething;
begin
end;


function tAObject.GetLink: iProxyLink;
begin
result := fLink;
end;

function tAObject.ReturnSomething: boolean;
begin
result := true;


end;
procedure tAObject.SetLink(Link: iProxyLink);
begin
fLink := Link ;
end;
end.

***************************************************
***************************************************
unit BLink;
interface
uses ProxyLink;
type
iBLink = interface


function GetLink: iProxyLink;
procedure SetLink(Link:iProxyLink);

procedure DoSomething;
function ReturnSomething:boolean;
end;
implementation
end.
***************************************************
***************************************************
unit UnitB;
interface
uses ProxyLink, BLink;
type tBObject = class(tInterfacedObject,iBLink)


private
fLink : iProxyLink;
public
function GetLink: iProxyLink;
procedure SetLink(Link:iProxyLink);

procedure DoSomething;
function ReturnSomething:boolean;
end;
implementation
{ tBObject }
procedure tBObject.DoSomething;
begin
end;


function tBObject.GetLink: iProxyLink;
begin
result := fLink;
end;

function tBObject.ReturnSomething: boolean;
begin
result := true;


end;
procedure tBObject.SetLink(Link: iProxyLink);
begin
fLink := Link ;
end;
end.

***************************************************
***************************************************
unit MainTest;

interface

...snip...

var
Form1: TForm1;

implementation

uses UnitA, UnitB, ABLink;

{$R *.DFM}

procedure TForm1.btnTestClick(Sender: TObject);
var
A : tAObject;
B : tBObject;
Link : tABLink;
IsMostExcellent:boolean;
begin
A := tAobject.create;
B := tBobject.create;
try
Link := tABLink.create(A,B);
A.SetLink(Link);
B.SetLink(Link);
Link.A.DoSomething;
IsMostExcellent:=Link.B.ReturnSomething;
Link.A := nil;
Link.B := nil;
A.SetLink(nil);
B.SetLink(nil);
finally {the following lines are just to get passed compiler,but...}
Link := nil; {a question is raised...}{note that the compiler doesn't
link these lines?????}
A := nil; {if a try I call "free", I get an invalid "Pointer
Operation" exception}
B := nil; {does it know they are already nil? why?}
{this of course must be addressed - memory leak...}
end;

if IsMostExcellent then
showmessage('Most Excellent');
end;

end.


Raoul De Kezel

unread,
Jul 10, 2001, 1:18:05 PM7/10/01
to
In article <hculktkudp2854eg2...@4ax.com>, ri...@fenestra.com says...

> > Please, Borland, add forward declarations of class types declared
> > in other units. P
>
> Already possible. Use interfaces.
>

You still need to put all related interfaces in the same
unit, dont you ?

Not that this is not a progress. Synchronizing interfaces
and the implemention classes is quickly bothering, however.
Do you know of some expert to do it automagically ?

--- Raoul

Raoul De Kezel

unread,
Jul 10, 2001, 2:06:15 PM7/10/01
to
In article <3b4b0e49_2@dnews>, dland...@yahoo.com says...

> This is my finally installment, otherwise this is becoming a part time job
> <g>.
>

Fine, Dennis, thanx for your time.

> A := nil; {if a try I call "free", I get an
> invalid "Pointer Operation" exception}

Well, you should not free a TInterfacedObject yourself.
It is automatically destroyed when the last interface
reference to it disappears. I think this happened as
a side effect of the line Link.A := nil;

Kind regards,

--- Raoul

Dennis Landi

unread,
Jul 10, 2001, 2:12:55 PM7/10/01
to
>
> Well, you should not free a TInterfacedObject yourself.
> It is automatically destroyed when the last interface
> reference to it disappears.

I never knew that about tInterfacedObject (or forgot it) although that is
exacty the behaviour that is being exhibited.
Thank You!
-dennis


Raoul De Kezel

unread,
Jul 10, 2001, 2:56:21 PM7/10/01
to
In article <MPG.15b562871...@forums.inprise.com>, raould...@hotmail.com says...

> I think this happened as
> a side effect of the line Link.A := nil;
>

Correction: It would have happened there if tABLink.SetA
had an implementation. As the code stands, A is
destroyed as a side effect of B.SetLink(nil);

Sorry

--- Raoul

Dennis Landi

unread,
Jul 10, 2001, 3:44:02 PM7/10/01
to
> Well, you should not free a TInterfacedObject yourself.
> It is automatically destroyed when the last interface
> reference to it disappears.
>
I am not sure if I ever knew this about TInterfacedObject although it makes
perfect sense.

-dennis

Rick Rogers (TeamB)

unread,
Jul 10, 2001, 5:33:27 PM7/10/01
to
On Tue, 10 Jul 2001 19:18:05 +0200, Raoul De Kezel
<raould...@hotmail.com> wrote:

> You still need to put all related interfaces in the same
> unit, dont you ?

Sure, but what's the harm in that? Declare one unit with all of your
related interfaces and even class factories, and that's the only one
unit you ever have to include in other units of your projects. Even
with all of this, the unit will still be small because it will contain
no implementation.

> Synchronizing interfaces and the implemention classes is quickly
> bothering

I haven't found it to be a bother in the slightest, and one of the
projects that we're working on has hundreds of small interfaces with
implementing classes.

> Do you know of some expert to do it automagically ?

Nope, but I suppose there is one somewhere.

John Elrick

unread,
Jul 10, 2001, 5:28:02 PM7/10/01
to
"Raoul De Kezel" <raould...@hotmail.com> wrote in message
news:MPG.15b557386...@forums.inprise.com...

Well, there is Compile. It throws the errors out when the interface doesn't
match.

Creating it automatically...no, but its a good idea. If I get a few minutes
I'll see what I can cook up with CodeRush.


John Elrick


Raoul De Kezel

unread,
Jul 10, 2001, 6:40:32 PM7/10/01
to
In article <51tmkt4hvlotunscf...@4ax.com>, ri...@fenestra.com says...
> one of the project we're working on has hundreds of small interfaces
> with implementing classes.
>

Very interesting, I'm looking after actual experience in the
massive/exclusive use of interfaces in a project.

You use lots of small interfaces, each with a handful of methods and
a very narrow purpose. Correct ?

Do you have (roughly) an implementing class per interface ?
Or fat classes which implement many interfaces ?
Or one class per problem domain interface (eg, IPerson) which
also supports an additional number of general-purpose interfaces
(eg IBrowsable, IExecutable, ...) ?

May I have an estimation of the number of interfaces a typical
class implements ?

Interfaces as an Object Pascal feature only, or are you
doing COM/CORBA ?

Are parts of the project *not* using interfaces ?

Do you use interface inheritance ?

May I know the purpose of the project (if not confidential) ?
Is it APS RollCall ?

The KLocs of the project (if not confidential) ?

Is it in use on sites (if not confidential) ?

Pardon so many questions. I'm very interested.
Many thanks.

--- Raoul


Rick Rogers (TeamB)

unread,
Jul 10, 2001, 7:41:12 PM7/10/01
to
On Wed, 11 Jul 2001 00:40:32 +0200, Raoul De Kezel
<raould...@hotmail.com> wrote:

> You use lots of small interfaces, each with a handful of
> methods and a very narrow purpose.

Correct.

> Do you have (roughly) an implementing class per
> interface ?

No way. Most of our classes implement more than one interface. The
average is probably somewhere between 3-4 interfaces per class, with
the maximum being somewhere around 6-7. I don't think very many if any
of our implementing classes only implement one interface.

> Or fat classes which implement many interfaces ?

I wouldn't call them "fat", I'd say they're exactly the right size ;).

> Or one class per problem domain interface (eg, IPerson) which
> also supports an additional number of general-purpose interface

That's more like it.

> May I have an estimation of the number of interfaces a
> typical class implements ?

See above.

> Interfaces as an Object Pascal feature only, or are you
> doing COM/CORBA ?

Object Pascal only, no COM/COBRA. We've found interfaces have given us
some real design elegance and flexibility not possible with classes.

For example, we have an ISerializable interface that many implementing
classes that need to be persisted to a stream (memory, disk,
clipboard, etc.) implement.

With classes, to do this sort of thing would either require all
classes to share a common ancestor, which isn't always possible, or
multiple inheritance, which isn't available in Object Pascal. Using
interfaces for this sort of thing is a very elegant design approach.

> Do you use interface inheritance ?

Sometimes. As you know, since interfaces have no implementation, there
is no implementation inheritance, so basically all you're inheriting
is the declaration. This is useful when you wish to polymorphically
invoke a method on an ancestor interface.

> May I know the purpose of the project (if not confidential) ?

I'm the CEO so I get to decide what is confidential. In this case, my
pride for what we've built plus my natural inclination to share in the
Delphi newsgroups means this isn't confidential. ;)

It is a suite of software components, applications, and NT services
that we have built for the United States Census Bureau to automate the
production of paper and electronic surveys based on the same source
content data.

> The KLocs of the project (if not confidential) ?

One of the applications that uses most of the components is 123,000
lines of code. It has been in development for about 1.5 years, and has
involved the dedicated efforts of a 3-4 person team during that time.
It is a sizeable project.

> Is it in use on sites (if not confidential) ?

Yes, it has been widely deployed at the Census Bureau, and has been
very successful. One of the NT services receives an XML file that
contains survey content (the question wording, answer fields, etc.)
and automatically generates a sophisticated either paper layout or
electronic layouts that represents that content using stylesheets,
formatting rules, etc.

The paper layout includes lots of advanced features such as
intelligent page breaks, "continuation" footers and headers that
indicate when a question has been broken across a page, etc. The
generated layout is represented in an XML-based layout language that
we have developed for this project (it is modeled after XSL), and
we've also developed a viewer for our layout language that works as
both a stand-alone application and as a web-browser plug-in. The
viewer also can include data entry controls for electronic surveys
which allows people to furnish responses to the survey.

The NT service receives this content XML file (which, incidentally
originates from an Oracle SQL database and is generated by a Java
Servlet) and generates the return layout XML file in approximately
3-10 seconds. The input files are around 100K and the output files are
around 300K. The output files can be as many as 5-6 highly composed
and formatting-rich full survey pages. This service is only installed
on one NT server, and is servicing a request about every 10 seconds
all day every day from a group of around 200 users.

Another application allows Census staff to visually design the parts
of the surveys whose layout cannot be automatically generated as
explained above.

> I'm very interested.

Thanks, I like talking about interfaces... I seldom get the chance
because they do not seem to be widely used in Delphi.

John Elrick

unread,
Jul 10, 2001, 8:23:36 PM7/10/01
to
"Rick Rogers (TeamB)" <ri...@fenestra.com> wrote in message
news:6n2nktg5gama44m1g...@4ax.com...

> On Wed, 11 Jul 2001 00:40:32 +0200, Raoul De Kezel
> <raould...@hotmail.com> wrote:
>

SNIP

> Thanks, I like talking about interfaces... I seldom get the chance
> because they do not seem to be widely used in Delphi.

Then I'll give you yet another chance, if you don't mind. <G> I've been
poking around the ToolsAPI unit, which is mostly interface declarations.
Any comments on ToolsAPI, pros and/or cons, from the standpoint of its
design?

Specifically, since this is a rather large (2500 line) unit of interface
declarations, I'm curious about your general opinions of the way this was
done. Based, of course, on your own experiences using interfaces in code.
Its an easy straw man since it's available to anyone with Pro and up.

Question number two: forms and interfaces. Liberally interpreting what you
have written, I see that the preference is toward using interfaces over
object instances. If so, how does this theory fit into, say for sake of
example, a modal form? Would you instance it and use Action := caFree
(which breaks reference counting nastily)? Or, if you would use it as an
interface, how would you do so?

TIA,


John Elrick


Raoul De Kezel

unread,
Jul 11, 2001, 4:47:45 AM7/11/01
to
In article <6n2nktg5gama44m1g...@4ax.com>, ri...@fenestra.com says...

> > Or fat classes which implement many interfaces ?
> I wouldn't call them "fat", I'd say they're exactly the right size ;).

Agreed. I didn't intend to be pejorative, used the wrong word :-(

> I like talking about interfaces... I seldom get the chance
> because they do not seem to be widely used in Delphi.

As I see it, they are stuck in a vicious circle.

They were added relatively late to the language, when many
projects were using straight class techniques. Now, massive
use of interfaces is a major design decision. One cannot
really retrofit it into an existing project. Therefore, there
is no public example of previous usage to help new projects.
Which then use again well-known, well-documented straight class
techniques.

We just see in existing projects some interfaces gradually added
here and there on top of a straight class design. The VCL itself
is an example of this process, I guess.

Another problem is that interfaces require a big mind-shift. I think
there is a consensus on what's a well-designed interface, but
didn't see it articuled in a book or sizable article (didn't look
for recently, however). That doesn't help people to jump in.

Finally, one really needs a medium to large-scale project to fully
understand their power. Therefore, some people are just not motivated
to use them, perhaps rightly so.

Sad, sad, sad... I like interfaces too.

Thanks for your answers, Rick. Much appreciated.

--- Raoul

Raoul De Kezel

unread,
Jul 11, 2001, 5:00:57 AM7/11/01
to
In article <3b4b73fe_2@dnews>, jel...@adelphia.net says...

> Synchronizing interfaces and the implemention classes is
> quickly bothering, however.
>
> Creating it automatically...no, but its a good idea. If I get a few minutes
> I'll see what I can cook up with CodeRush.
>

Thanks, John. I would almost kill for that.

--- Raoul

Rick Rogers (TeamB)

unread,
Jul 11, 2001, 5:06:22 PM7/11/01
to
On Wed, 11 Jul 2001 10:47:45 +0200, Raoul De Kezel
<raould...@hotmail.com> wrote:

>I didn't intend to be pejorative, used the wrong word

I was just teasing a little ;)

> As I see it, they are stuck in a vicious circle.

I agree. Add to what you've said the fact that having an object
reference and an interface reference to the same instance often causes
frequent hard-to-solve problems, and people really tend to shy away
from interfaces.

> see it articuled in a book or sizable article

I've been thinking about asking our CTO, Steve Schafer (former TeamB
member), to write some of his design ideas for using interfaces up for
an article or book. Steve really deeply understands how and where to
use interfaces (much better than I do).

> Finally, one really needs a medium to large-scale project to
> fully understand their power.

Agreed. There is a learning curve that only pays off over the long
haul.

Rick Rogers (TeamB)

unread,
Jul 11, 2001, 5:03:29 PM7/11/01
to
On Tue, 10 Jul 2001 20:23:36 -0400, "John Elrick"
<jel...@adelphia.net> wrote:

> Any comments on ToolsAPI, pros and/or cons, from the standpoint
> of its design?

I'm sadly ignorant of the ToolsAPI, the last time I really knew much
about it was in the D1-D2 timeframe, which was before interfaces
existed (they were added in D3, right?). I will peruse the ToolsAPI
over because you've tweaked my interest, and I know that interfaces
used extensively there, but I don't know how many salient comments
I'll have... ;)

> Liberally interpreting what you have written, I see that the
> preference is toward using interfaces over object instances

True to a point, but that's mainly because most of our work is
non-visual built-from-scratch coding. We do use the VCL components for
UI work, and when we do, we use them as standard object instances.
This approach does keep the "presentation" and "logic" quite separate,
our event-handler methods almost never do anything except call
interface reference methods of some sort or another.

We've found it to be much too painful to mix interfaces references and
object references... it always seems to cause strange untraceable AVs
and other problems (as you alluded). I'd like the VCL to be more
interface-compatible and designed, but they really can't break the
existing framework either.

John Elrick

unread,
Jul 11, 2001, 5:17:00 PM7/11/01
to
"Rick Rogers (TeamB)" <ri...@fenestra.com> wrote in message
news:idfpktol0fr0ftr8d...@4ax.com...

> On Tue, 10 Jul 2001 20:23:36 -0400, "John Elrick"
> <jel...@adelphia.net> wrote:
>
> > Any comments on ToolsAPI, pros and/or cons, from the standpoint
> > of its design?
SNIP

> used extensively there, but I don't know how many salient comments
> I'll have... ;)

And I'm looking forward to them<g>

>
> > Liberally interpreting what you have written, I see that the
> > preference is toward using interfaces over object instances
>
> True to a point, but that's mainly because most of our work is
> non-visual built-from-scratch coding. We do use the VCL components for
> UI work, and when we do, we use them as standard object instances.
> This approach does keep the "presentation" and "logic" quite separate,
> our event-handler methods almost never do anything except call
> interface reference methods of some sort or another.
>
> We've found it to be much too painful to mix interfaces references and
> object references... it always seems to cause strange untraceable AVs
> and other problems (as you alluded). I'd like the VCL to be more
> interface-compatible and designed, but they really can't break the
> existing framework either.

Yep. IMHO major mistake on Delphi's design team part was THEIR mixing of
meaning. Been nice if there was a base interface command that was not
reference counted and then a reference counted version. Of course,
hindsight is 20/20.


John Elrick


Rick Rogers (TeamB)

unread,
Jul 11, 2001, 6:16:29 PM7/11/01
to
On Wed, 11 Jul 2001 17:17:00 -0400, "John Elrick"
<jel...@adelphia.net> wrote:

> Been nice if there was a base interface command that
> was not reference counted

This is a common misconception. There is a base interface command that
is not reference counted -- it is called "interface". ;)

Just because the default ancestor (IUnknown) has methods that *could*
be *implemented* to support reference counting, there is absolutely
nothing whatsoever intrinsic to reference counting and the Object
Pascal interface type.

Any reference counting that is done is *always* done by specific
implementing classes, such as TInterfacedObject. It is quite easy to
descend from another class that doesn't implement reference counting
-- such as TObject -- and then implement the IUnknown reference
counting methods as do nothing methods.

Thus, you can pretty easily use Delphi interfaces without reference
counting.

I realize you probably already know these nuances, but I'm
pontificating because it is the source of so much confusion for many.

Terry Field

unread,
Jul 11, 2001, 6:18:18 PM7/11/01
to
"Rick Rogers (TeamB)" <ri...@fenestra.com> wrote in message
news:bpfpktk7q8n1dlgf8...@4ax.com...

> I've been thinking about asking our CTO, Steve Schafer (former TeamB
> member), to write some of his design ideas for using interfaces up for
> an article or book. Steve really deeply understands how and where to
> use interfaces (much better than I do).
>

Can I squeeze in a big word of encouragement for this. It would be of major
value to a lot of people looking in on this NG.


Terry Field.


John Elrick

unread,
Jul 11, 2001, 8:03:34 PM7/11/01
to
"Rick Rogers (TeamB)" <ri...@fenestra.com> wrote in message
news:cqjpktk2q28jessmg...@4ax.com...

> On Wed, 11 Jul 2001 17:17:00 -0400, "John Elrick"
> <jel...@adelphia.net> wrote:
>
> > Been nice if there was a base interface command that
> > was not reference counted
>
> This is a common misconception. There is a base interface command that
> is not reference counted -- it is called "interface". ;)

True, to a point<g> In Java (IIUC), there is an interface keyword which
simply specifies an interface. But if you instanciate into an interface in
Delphi and DON'T implement _AddRef, _Release, and _QueryInterface, well,
you'll know very quickly that this is not an acceptable approach<g>.

So, in a sense, "interface" does trigger a reference counting mechanism.
Whether you use it or not for your own classes is your own business.


John Elrick


Raoul De Kezel

unread,
Jul 12, 2001, 6:41:00 AM7/12/01
to
In article <bpfpktk7q8n1dlgf8...@4ax.com>, ri...@fenestra.com says...

> Add to what you've said the fact that having an object
> reference and an interface reference to the same instance often causes
> frequent hard-to-solve problems, and people really tend to shy away
> from interfaces.
>

Yes. When lifetime is handled by interface reference counting, the
client code should just not keep references to objects hanging around.
And that makes interfaces (used this way) mostly an all or nothing
proposition.

This is in sharp contrast with interfaces used to abstract some
fine grained behavior of an existing class design, where reference
counting is not used, lifetime is handled as usual, and references
to objects and interfaces may be quite freely mixed.

Many patterns waiting to be written here :-)

--- Raoul

Rick Rogers (TeamB)

unread,
Jul 12, 2001, 7:04:00 AM7/12/01
to
On Thu, 12 Jul 2001 12:41:00 +0200, Raoul De Kezel
<raould...@hotmail.com> wrote:

> mostly an all or nothing proposition.

> This is in sharp contrast with interfaces used to abstract
> some fine grained behavio

I agree in entirely. I'm also curious: how are you using interfaces in
your work?

Rick Rogers (TeamB)

unread,
Jul 12, 2001, 7:05:15 AM7/12/01
to
On Wed, 11 Jul 2001 20:03:34 -0400, "John Elrick"
<jel...@adelphia.net> wrote:

> So, in a sense, "interface"

Agreed. It is clear that interfaces were implemented primarily to
facilitate COM support, and I wish they had been a bit more neutral.

Brett McKenzie

unread,
Jul 12, 2001, 9:05:24 AM7/12/01
to
my 2 cents worth re: interfaces...

I think interfaces are great. If you don't have multiple inheritance, which I don't really need, you need interfaces to
implement a lot of OO designs.

I typically use interfaces when I want to put objects into a collection but the actual object type can vary. For
example, when using the observer pattern the observers implement an interface. Another similar example is when I have a
generic data processor. The processor has a collection of objects that perform different types of processing. Sometimes
theses objects are forms, sometimes dataModules, and sometimes objects derived directly from TObject. These objects
implement the same interface. They register themselves with one keeper object which stores the interface pointers in a
TList (or TStringList etc). i.e. the keeper only knows the interface, not the actual class type.

I never put a GUID on my interfaces and just implement the usual dummy version of QueryInterface, _AddRef and _Release.
Other than having to implement these 3 dummy methods interfaces work as expected with no reference counting.

I only use interfaces when I need to. I don't routinely create an interface on all classes I design.

I suspect that interfaces are under-utilised by developers not because they are a difficult concept, but because a lot
of people don't appreciate Object Oriented design techniques. (That's just a general statement, I'm definitely not
pointing at anyone!)

Brett

"Rick Rogers (TeamB)" <ri...@fenestra.com> wrote in message news:101rkt8qvrfptmbq1...@4ax.com...

Jerome Tremblay

unread,
Jul 12, 2001, 9:40:49 AM7/12/01
to
> I never put a GUID on my interfaces and just implement
> the usual dummy version of QueryInterface, _AddRef and _Release.
> Other than having to implement these 3 dummy methods interfaces
> work as expected with no reference counting.

How do you obtain the interface from the object then? And an interface from
another interface? I tried to use Interfaces in one of my project and it ended
up too slow. The profiler told me that the bottleneck was the 6 million calls
to QueryInterface, AddRef and Release. I'm sure there is a better way to pass
interfaces around than use AS everywhere, but I'm unsure how to proceed.


Jerome Tremblay
Popup! Solutions


Raoul De Kezel

unread,
Jul 12, 2001, 9:59:26 AM7/12/01
to
In article <101rkt8qvrfptmbq1...@4ax.com>, ri...@fenestra.com says...

> I'm also curious: how are you using interfaces in
> your work?
>

Rick, I'm at home right now, leaving for holidays tomorrow.

Please let me answer this question when I come back end of this
month. I want to gather some stats. Actual figures will be much
more interesting than my feelings.

For example, I have a strong suspicion that our mean method count
per interface is less than 2. That doesn't mean much by itself,
however, because the distribution is likely multimodal, with one
mode for each pattern of use. Let me sort this out, it's a very
interesting question :-)

Very pleased to meet you,

--- Raoul

Rick Rogers (TeamB)

unread,
Jul 12, 2001, 10:21:13 AM7/12/01
to
On Thu, 12 Jul 2001 15:59:26 +0200, Raoul De Kezel
<raould...@hotmail.com> wrote:

> leaving for holidays tomorrow.

Have a terrific holiday, I look forward to continuing the discussion
when you get back...

John Elrick

unread,
Jul 12, 2001, 10:55:29 AM7/12/01
to
"Brett McKenzie" <junk...@mck.com.au> wrote in message
news:3b4da096_1@dnews...

> my 2 cents worth re: interfaces...
SNIP

>
> I suspect that interfaces are under-utilised by developers not because
they are a difficult concept, but because a lot
> of people don't appreciate Object Oriented design techniques. (That's just
a general statement, I'm definitely not
> pointing at anyone!)

I'd suspect you guess wrong. It's not they are a difficult concept, nor is
it not appreciating the OO benefits - it's that you must be extra careful
when using them. As Rick Rogers pointed out, use them incorrectly and you
can get seemingly random access violations.

I tried using interfaces extensively in one section of a project and dropped
them after the merry av chase. The gains were simply not worth the benefit.
An abstract base class served the purpose very nicely. This was in Delphi
3, no TInterfaceList, so I was also balancing the issues of trying to add
them to a TList. I also typically use const parameters - a major no-no in
interfaces that are reference counted, at least in what I was doing. So I
had yet another exception to my rules to remember.

You can choose to not use reference counting, but now you must either
remember to reimplement the trio, or descend from a base class designed for
the purpose - once again, since you have to descend from a special base
class you are halfway to defeating some of the gains from interfaces.

Another reason can be performance. In repeated accessing, there is a
performance hit that can add up rapidly due to the calls to _AddRef and
_Release (even if not really implemented).

In short, Brett, the concept is fairly simple, the benefits are obvious but
the implementation can be a pain in the butt. I emphasize "can" because
they can also be used elegantly - but the upfront learning curve is (IMO)
one of the steepest in the Delphi world, primarily due to the "gotchas".
The average developer must have a project that will benefit greatly from
interfaces before they can usually afford to become an expert in them.

FWIW


John Elrick


Brett McKenzie

unread,
Jul 12, 2001, 3:54:27 PM7/12/01
to
As far as I know, assuming you are not implementing a COM object,
a Delphi interface only needs to have a GUID if you want to try and get
the interface from an object that doesn't know if it implements it or not.
This is a bit like downcasting in the C++ world (casting from a base class
pointer to a derived class pointer). I tend to avoid doing this, with the
the exception (no pun intended) of getting objects and interfaces out of a
list object. Thus I never explicitly call QueryInterface, _AddRef or
_Release.

For example:
type
TBeepObserver = class(TObject, IIntegerObserver) ...

procedure attach(const observer: IIntegerObserver);

var
myObject: TBeepObserver;
myInterface: IIntegerObserver;
begin
attach(myObject); // get IIntegerObserver intf
myInterface := myObject; // get IIntegerObserver intf


i.e. Delphi knows how to get the IIntegerObserver interface from a
TBeepObserver object without any help. No QueryInterface or "as"
operator required.

I typically don't "get" one interface from another interface using
QueryInterface. Its a design issue. (Of course you have to use
QueryInterface if you are using Microsoft's COM objects, but that's
different. I'm in the non-COM world). To me it's an issue of passing
object references around using the appropriate type. i.e. don't pass
someone an IBase if they need an IDerived. That doesn't stop me
from passing them something derived from IDerived of course.

If you have one object that implements two totally independent
interfaces, then I don't think you should be relying on QueryInterface
to get one interface from the other. Doesn't this mean that the two
interfaces are related? Why not provide a function or property to return
the required interface? The actual object that implements these two
interfaces can easily pass back the required interface without
QueryInterface. Does this make sense?

Attachments may not be welcome in this newsgroup (?) so I have
thrown together a simple observer demo and put it here. Its pretty
simple so may or may not help:
Source code: http://www.mck.com.au/junk/ObserverDemo.zip
Executable: http://www.mck.com.au/junk/observerEx.exe

Brett

"Jerome Tremblay" <jer...@loginov.com> wrote in message news:3b4da8e9_1@dnews...

Brett McKenzie

unread,
Jul 12, 2001, 4:19:17 PM7/12/01
to
Hi John,

I hear what you are saying. Though I know that if I ask the Delphi programmers
that I used to work with if they are using interfaces much, they will typically say
"what are interfaces?". Most of them don't even know what a design pattern is.
Oh well!

Yes I agree that having to put up with the unnecessary calls to _AddRef and
_Release is disappointing. Hey Borland, please give us a "clean" interface...?

I'm quite sure that if you follow some simple rules interfaces are relatively
straightforward to use, fingers crossed... :-) I come from a C++ background so
have certain ideas on design. I think that the people who have come from a
VB and/or COM background, and have had to put up with Microsoft's philosphy
of "we think this object is this type, lets try it and see if we get an error" have had
a poor introduction to design.

Brett

"John Elrick" <jel...@adelphia.net> wrote in message news:3b4dbaf3_2@dnews...

John Elrick

unread,
Jul 12, 2001, 4:39:42 PM7/12/01
to
"Brett McKenzie" <junk...@mck.com.au> wrote in message
news:3b4e0646_2@dnews...

> Hi John,
>
> I hear what you are saying. Though I know that if I ask the Delphi
programmers
> that I used to work with if they are using interfaces much, they will
typically say
> "what are interfaces?". Most of them don't even know what a design
pattern is.
> Oh well!
>
> Yes I agree that having to put up with the unnecessary calls to _AddRef
and
> _Release is disappointing. Hey Borland, please give us a "clean"
interface...?
>
> I'm quite sure that if you follow some simple rules interfaces are
relatively
> straightforward to use, fingers crossed... :-) I come from a C++
background so
> have certain ideas on design. I think that the people who have come from a
> VB and/or COM background, and have had to put up with Microsoft's
philosphy
> of "we think this object is this type, lets try it and see if we get an
error" have had
> a poor introduction to design.

LOL or worse yet...according to CodeCompletion, this object supports this
method. Let's see if it really does<g>


John Elrick


Rémon Sinnema

unread,
Jul 12, 2001, 3:52:13 PM7/12/01
to
----- Original Message -----
From: "Raoul De Kezel" <raould...@hotmail.com>
Newsgroups: borland.public.delphi.oodesign
Sent: Sunday, July 08, 2001 11:18 PM
Subject: Re: Divide a file to multiple files


> We use SourceSafe, which also allows multiple checkouts, and we do that
> sometimes. I certainly agree that merging conflicts are uncommon and most
> are easily resolved. However, such checkins still require careful
attention,

...as do all checkins...


> sometimes from both authors of changes, at a time when the details are no
longer in memory.

We're used to checking in small changes very often; usually at least once a
day (since we have an automated nightly build). So the details are still in
memory when we reconcile merging conflicts. BTW, checking often does *not*
conflict with your policy of only checking in unit-tested code!


> A bit too error-prone for my taste, especially given that our goal is
error-free shared code.

I can't recall me ever having encountered a single bug due to merging
conflicts. I recall plenty of others though <g/> But as ever, YMMV.

Rémon Sinnema

unread,
Jul 12, 2001, 4:21:48 PM7/12/01
to
"John Elrick" <jel...@adelphia.net> schreef in bericht
news:3b4dbaf3_2@dnews...

>> I also typically use const parameters - a major no-no in interfaces that
are reference counted, at least in what I was doing. <<

Would you care to elaborate on that, i.e. what were the problems you
encountered with const parameters?

Raoul De Kezel

unread,
Jul 12, 2001, 6:02:44 PM7/12/01
to
In article <3b4e1792_1@dnews>, rssi...@yahoo.com says...

> I can't recall me ever having encountered a single bug due to merging
> conflicts.

Honestly, I don't recall either. But is it because they are intrinsically
safe or because we have few multiple checkouts ?

I'll take your word for it and try to find a way to test a change of
our process in this area. Thanks much for your input. Really.

> BTW, checking often does *not* conflict with your policy of only
> checking in unit-tested code!

I have more of a problem here. To be precise, our shared code is
reputed deliverable on site. Certainly not that we want to be able
to deliver at any time, but this policy breaks progress in a number
of honest, measurable milestones to prevent hidden planning sideslipping.

Therefore, we checkin only when a new feature is complete. And many
features need one week to be developped, not one day.

> we have an automated nightly build

Doing C++ ?

--- Raoul

Bryan

unread,
Jul 12, 2001, 6:52:39 PM7/12/01
to
Interfaces are FANTASTIC for objects that are accessed by multiple threads.
Otherwise you have the horrendous problem on shutdown of when to free off
the object - another thread may still be using it.

Bryan


John Elrick

unread,
Jul 12, 2001, 8:38:01 PM7/12/01
to
"Bryan" <bryan@%nospam%ivisionsystems.com> wrote in message
news:3b4e29e4_2@dnews...


Or a command pattern. Create an object that contains the command code (from
say a form), pass it into another class (example a search class) and then
free the form. If you use an interface, you don't have to worry about who
is responsible for freeing the durn thing<g>


John Elrick


John Elrick

unread,
Jul 12, 2001, 8:36:30 PM7/12/01
to
"Rémon Sinnema" <rssi...@yahoo.com> wrote in message
news:3b4e1793$1_1@dnews...


If you pass a reference counted interface as a const, it can trigger a
_Release before an _AddRef freeing the instance.

I've forgotten the exact sequence, but suffice to say you are usually safer
passing interfaces without the const.

If you defeat reference counting, none of this matters.


John Elrick


Dennis Landi

unread,
Jul 12, 2001, 9:36:17 PM7/12/01
to
That's an excellent point. This is a very elegant way of dealing with a
shared object. I will have to experiment with this...

"Bryan" <bryan@%nospam%ivisionsystems.com> wrote in message
news:3b4e29e4_2@dnews...

William Meyer

unread,
Jul 13, 2001, 1:44:36 AM7/13/01
to
"Rick Rogers (TeamB)" <ri...@fenestra.com> wrote in message
news:bpfpktk7q8n1dlgf8...@4ax.com...

>
> I've been thinking about asking our CTO, Steve Schafer (former TeamB
> member), to write some of his design ideas for using interfaces up for
> an article or book. Steve really deeply understands how and where to
> use interfaces (much better than I do).

Could he be persuaded by a large acclaim here? <g> Add my vote to the list
of those who'd love to see it.

Bill


William Meyer

unread,
Jul 13, 2001, 1:48:27 AM7/13/01
to
"John Elrick" <jel...@adelphia.net> wrote in message
news:3b4dbaf3_2@dnews...
>
> I'd suspect you guess wrong. It's not they are a difficult concept, nor
is
> it not appreciating the OO benefits - it's that you must be extra careful
> when using them. As Rick Rogers pointed out, use them incorrectly and you
> can get seemingly random access violations.

I'm not sure of that. I haven't yet dug into them (something about being up
to my a** in alligators, and trying to drain the swamp), yet am sure they
would provide benefits in a number of areas in my largest app. It's one of
those vicious circle problems, suspecting they might be the key to solving
some knotty problems which currently make it hard to find the time to study
and understand them.

Bill


Jan Nordén

unread,
Jul 13, 2001, 4:19:32 AM7/13/01
to

"Rick Rogers (TeamB)" <ri...@fenestra.com> wrote in message
news:6n2nktg5gama44m1g...@4ax.com...
> On Wed, 11 Jul 2001 00:40:32 +0200, Raoul De Kezel
> <raould...@hotmail.com> wrote:

> No way. Most of our classes implement more than one interface. The
> average is probably somewhere between 3-4 interfaces per class, with
> the maximum being somewhere around 6-7. I don't think very many if any
> of our implementing classes only implement one interface.
>

Just for the record, I assume that you are aware that in Delphi each
implemented interface adds 4 bytes to the size of each instance of the
class.
This is probably not a problem for classes like TPerson, but can be for
classes like TString, or TCurrency., or for that matter TList.


Bryan

unread,
Jul 13, 2001, 6:55:33 AM7/13/01
to
That would require a 2-pass compiler, surely?
One of the big advantages of Delphi is its lightning fast 1-pass compiler.

Bryan

"Jan Nordén" <jan.n...@boldsoft.com> wrote in message
news:3b49b4f7$1_2@dnews...
> I'm afraid I don't have an answer, we would all like to see "real" forward
> declarations i OP (After all a class reference is only a pointer, so the
> interface
> section really doesn't need to know more).
>
> While we are at it I have another (related) wish, and that is that Borland
> would allow the private parts of classes to use types defined
> in the implementation section, or in units in the implemetnation sections
> uses claue. This is logical since private parts can only be accessed in
the
> implementation section.
>
> i.e
>
> interface
> type
> TA = class
> fA: TList
> fB: TB;
> end;
>
> implementation
> uses
> Classes;
> TB = class
> ....
>
>
>
>
>
>
>
> "Daniel Szasz" <dan...@hotmail.com> wrote in message
> news:3b481d24_1@dnews...
> > Hello
> >
> > I have a file with more than 20 classes and objects. Most of them have
> > properties from the type of other objects like that :
> >
> > type
> > TB= class
> > TC= clase
> > TA = object
> > b : TB;
> > c : TC;
> > end;
> > TB = object
> > a : TB;
> > c : TC;
> > end;
> > TC = object
> > b : TB;
> > a : TA;
> > end;
> > .....
> >
> > the file of course is growing more and more and I'm looking for a way to
> > break it in small files where each class have it's own file...
> >
> >
> > thanks
> >
> > dan
> >
> >
>
>


Rick Rogers (TeamB)

unread,
Jul 13, 2001, 7:53:08 AM7/13/01
to
On Fri, 13 Jul 2001 10:19:32 +0200, "Jan Nordén"
<jan.n...@boldsoft.com> wrote:

> I assume that you are aware that in Delphi each implemented interface
> adds 4 bytes to the size of each instance of the class.

Yep. We're also using WideStrings, so the application is not the most
efficient memory consumer possible anyway, but I will tell you what:
it is still pretty darn fast.

Bryan

unread,
Jul 13, 2001, 1:10:55 PM7/13/01
to
I did once get pissed off with a non-tech boss who constantly wanted to know
exactly what I was doing with my time, so I printed out 50,000 lines of code
(all colour coded thanks to the new Delphi feature of the time) for him to
read, and dumped this huge pile of paper on his desk for his attention. He
never asked again....

Bryan

"Dennis Landi" <dland...@yahoo.com> wrote in message
news:3b48d33d_1@dnews...
> > > I hardly ever print out source code. Therefore, I'm curious why you
> would
> > > want to do that?
> >
> > Well, probably it's just me. When proof-reading my new code or doing
> > peer review, I just find more bugs on paper than on screen. Perhaps
> > because the screen is a great tool for browsing, while paper is great
> > to study completely through in a calm, isolated place.
>
> I like to do that too... <g>
>
>


Anthony Richardson (Viewpoint SA)

unread,
Jul 14, 2001, 12:35:30 AM7/14/01
to
> I have more of a problem here. To be precise, our shared
code is
> reputed deliverable on site. Certainly not that we want to
be able
> to deliver at any time, but this policy breaks progress in
a number
> of honest, measurable milestones to prevent hidden
planning sideslipping.
>
> Therefore, we checkin only when a new feature is complete.
And many
> features need one week to be developped, not one day.

Perhaps you need a version control product that can allow
branching and merges of branches.

BASE_SOURCE
|
|\
| \
| NEW_FEATURE_START
| |
| MULTIPLE_CHECKINS
| |
| TEST_CHANGES
| /
| /
| /
|
NEW_SOURCE_FULLY_TESTED

Only ever deliver from the "main" thread of code, all
developments done in braches.

I believe both Rationals product and Star team offer this
functionality. In my honest opinion 1 week between checkins
is unacceptable.

Regards,

Anthony Richardson
ant...@viewpointsa.com


Rémon Sinnema

unread,
Jul 16, 2001, 4:46:51 PM7/16/01
to
"Raoul De Kezel" <raould...@hotmail.com> schreef in bericht
news:MPG.15b83cef...@forums.inprise.com...

> I have more of a problem here. To be precise, our shared code is
> reputed deliverable on site. Certainly not that we want to be able
> to deliver at any time, but this policy breaks progress in a number
> of honest, measurable milestones to prevent hidden planning sideslipping.

Of course we want measurable milestones as well. We have an automated
nightly build, that produces delivirables. We just don't deliver them each
day ;-)


> Therefore, we checkin only when a new feature is complete. And many
> features need one week to be developped, not one day.

Dunno about that. Usually sub-features can be identified. Or code can be
checked in that isn't called yet (but does pass the unit test).


> > we have an automated nightly build
> Doing C++ ?

Did that once, yeah. Really liked the time it gave me to talk about
yesterdays football with my colleagues while the compiler was crunching away
at those header files... Haven't touched C++ since the arrival of Delphi 1,
and certainly not planning on ever going back to it.

Rémon Sinnema

unread,
Jul 16, 2001, 4:49:40 PM7/16/01
to
"Anthony Richardson (Viewpoint SA)" <ant...@viewpointsa.com> schreef in
bericht news:3b4fcc5d_2@dnews...

> Perhaps you need a version control product that can allow
> branching and merges of branches.
>
> BASE_SOURCE
> |
> |\
> | \
> | NEW_FEATURE_START
> | |
> | MULTIPLE_CHECKINS
> | |
> | TEST_CHANGES
> | /
> | /
> | /
> |
> NEW_SOURCE_FULLY_TESTED
>
> Only ever deliver from the "main" thread of code, all
> developments done in braches.
>
> I believe both Rationals product and Star team offer this
> functionality. In my honest opinion 1 week between checkins
> is unacceptable.

Cvs also allows that, but we hardly ever use it. I prefer to check in code
on the main branch that is unit tested, but not called from anywhere. After
the feature is implemented, the system tests are run and the calling code
can be checked in.

Ritchie Annand

unread,
Aug 9, 2001, 9:24:09 PM8/9/01
to
Rick Rogers (TeamB) <ri...@fenestra.com> wrote in message
news:idfpktol0fr0ftr8d...@4ax.com...
> We've found it to be much too painful to mix interfaces references and
> object references... it always seems to cause strange untraceable AVs
> and other problems (as you alluded). I'd like the VCL to be more
> interface-compatible and designed, but they really can't break the
> existing framework either.

I've found one of the nastiest things to track down mixing interfaces and
object references is the fact that interfaces *call stuff* when they get set
to nil or go out of scope, unlike object references. One thing we had a
tough time tracking down, and it was in the vein of mixing the two object
models, was that we would set an interface property by grabbing the
interface from an object with GetInterface... which was fine... but we
hadn't bothered to set the interface property to nil early in the
destruction phases.

Turned out that the object the interface had pointed at managed to die
first. The access violation came up somewhere just before it bubbled all
the way back up to the current destructor, at which point, I imagine, Delphi
calls _Finalize (or whatever it is :) behind the scenes. If it's a regular
object property, that would be no problem. But it's an interface
property... so it's now determined to be out of scope, and
MyInterfaceProperty._Release gets called. Well, there's no object behind
it, so *boom* :)

If you can, adding notification on things really helps out :)

Just in case you ever brave those waters again :)

-- Ritchie Annand
Malibu Engineering & Software Ltd.
http://www.malibugroup.com/


Ritchie Annand

unread,
Aug 9, 2001, 9:35:53 PM8/9/01
to
*laugh* I poked around in System.pas to see exactly how it worked when I
first found it. Wow, directly calling Destroy; that is just freaky :)

Circular references are a scary thing in reference-counted objects, though.
If you want to "forcibly" free them, you need a way to tell objects that use
your objects to go away, and that can mean quite an excessive amount of
notification.

Danny Thorpe has a technique where you directly cast interface pointers
around so that, for example, a child won't keep the parent alive with an
extra reference, but the technique quite frankly scares me a bit :)

-- Ritchie Annand

Dennis Landi <all...@flashcom.net> wrote in message
news:3B4B5B00...@flashcom.net...
> > Well, you should not free a TInterfacedObject yourself.
> > It is automatically destroyed when the last interface
> > reference to it disappears.
> >
> I am not sure if I ever knew this about TInterfacedObject although it
makes
> perfect sense.
>
> -dennis


dennis landi

unread,
Aug 11, 2001, 5:30:08 PM8/11/01
to
Ritchie Annand wrote:

> Danny Thorpe has a technique where you directly cast interface
> pointers
> around so that, for example, a child won't keep the parent alive with
> an
> extra reference, but the technique quite frankly scares me a bit :)
>

Could you describe that technique in more detail?

Steve Schafer

unread,
Aug 11, 2001, 9:20:30 PM8/11/01
to
On Sat, 11 Aug 2001 17:30:08 -0400, dennis landi
<den...@dennislandi.com> wrote:

>Could you describe that technique in more detail?

It's called a "weak reference." (I don't think Danny invented it--it's
been discussed in the COM context for quite a long time.)

Here's an example:

type
TFoo = class(TInterfacedObject, IFoo)
private
_Parent: Pointer; // weak reference
protected
function Get_Parent: IFooParent;
property Parent: IFooParent read Get_Parent;
public
constructor Create(const AParent: IFooParent); end;

constructor TFoo.Create(const AParent: IFooParent);
begin
inherited Create;
_Parent := Pointer(AParent) end;

function TFoo.Get_Parent: IFooParent;
begin
Result := IFooParent(_Parent) end;

The idea is that the FooParent is not held as an interface reference,
and thus its refcount is not incremented, so an instance of TFoo can't
prevent its parent from being released because of circular reference
deadlock.

-Steve

dennis landi

unread,
Aug 11, 2001, 10:42:52 PM8/11/01
to
Hm... Dastardly. I can see why people have discomfort with this
approach.

There is nothing to stop the interface from disappearing out from under
tFoo without a trace. In this case I suppose that's the point... Just
not terribly good design IMO.
At a Silicon Valley Delphi User Group meeting in 1999 (i think) Danny
gave a talk actually introducing interfaces as a warm up for the
BorCon. I remember him cautioning us against "hard typecasting"
interfaces like this instead of using the "as" operator. This is
exactly why. tFoo will have to validate _Parent before using it and of
course some elaborate notification scheme will have to be worked out if
notification is needed...

There's gotta be a better way.

-dennis

Barry Kelly

unread,
Aug 12, 2001, 12:00:19 AM8/12/01
to
In article <3B75ED2B...@dennislandi.com>
dennis landi <den...@dennislandi.com> wrote:

> Hm... Dastardly. I can see why people have discomfort with this
> approach.
>
> There is nothing to stop the interface from disappearing out from under
> tFoo without a trace.

There is. IFooParent has a reference to TFoo. This is the whole point -
to get around circular references.

-- Barry

--
If you're not part of the solution, you're part of the precipitate.
Team JEDI: http://www.delphi-jedi.org
NNQ - Quoting Style in Newsgroup Postings
http://web.infoave.net/~dcalhoun/nnq/nquote.html

Steve Schafer

unread,
Aug 12, 2001, 2:46:50 AM8/12/01
to
On Sat, 11 Aug 2001 22:42:52 -0400, dennis landi
<den...@dennislandi.com> wrote:

>There is nothing to stop the interface from disappearing out from under
>tFoo without a trace. In this case I suppose that's the point...

That's correct. It generally only makes sense in a situation where the
parent holds a reference to the child instance, and doesn't release
that reference until it itself is destroyed. There's no problem then,
of course, since the parent goes away at the same time as the child,
and there's no possibility of the child attempting to access the
parent after the parent has been destroyed.

>Just not terribly good design IMO.

Hey, watch it. I use it all the time.... About the only scenario I use
it in is when I have a tree structure of some kind, and the children
need to point back, either to their immediate parent or to the root.
In the more general case, I usually allow the circular reference to
occur, then before dropping the last "external" references to the two
(or more) deadlocked objects (which would cause the objects to become
orphaned in memory), I invoke some sort of "finalization" method which
explicitly sets one of the references in the loop to nil. Then, when I
release the remaining external reference to the group, they all
release themselves like a row of dominoes.

>There's gotta be a better way.

It's called garbage collection, but it's not going to happen in
Delphi. At least, if it does happen, it will no longer be Delphi as we
know it.

-Steve

Raoul De Kezel

unread,
Aug 12, 2001, 3:19:54 AM8/12/01
to
In article <3B75ED2B...@dennislandi.com>, den...@dennislandi.com says...

> There's gotta be a better way.
>

Garbage collection :-)

Funny thing is that we can *almost* implement garbage collected
interfaces. We just miss RTTI information for non published fields.

--- Raoul

Bryan

unread,
Aug 12, 2001, 10:32:17 AM8/12/01
to
D6 has RTTI for interfaces.

Bryan

"Raoul De Kezel" <raould...@hotmail.com> wrote in message
news:MPG.15e04c80c...@forums.inprise.com...

Kyle Cordes

unread,
Aug 12, 2001, 2:09:13 PM8/12/01
to
> There's gotta be a better way.


Garbage Collection.


--
[ Kyle Cordes * ky...@kylecordes.com * www.kylecordes.com ]
[ Developer, Consultant, Trainer: Java, Delphi, ASTA, etc.]
[ Visit the site for articles, links, BDE Alternatives ]
[ Guide, JBuilder Open Tools, and a Delphi Wiki, and more ]


Raoul De Kezel

unread,
Aug 12, 2001, 3:25:19 PM8/12/01
to
In article <3b769311_1@dnews>, bryan@%nospam%ivisionsystems.com says...

> D6 has RTTI for interfaces.

For published members only, isn't it ?

--- Raoul

renuka...@gmail.com

unread,
Oct 3, 2013, 6:46:25 AM10/3/13
to
123Trainings is a Global Interactive Learning company started by proven industry experts with an aim to provide Quality Training in the latest IT Technologies.
123Trainings is offering Training services to Major IT giants and to individual students worldwide.
we have excellent Informatica faculty who have real time experience plus expert orientation in Online Training.
we will provide Project support also..
For more information contact :
INDIA: +91-9052820000, +91-9849966077, +91 9700010111
US : +1 2092073642,
India Land Line : +91 - 40 - 40182136
0 new messages