 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Christian Kaufmann Guest
|
Posted: Thu Dec 04, 2003 7:14 am Post subject: interface inheritance problem |
|
|
Hi,
I have a new problem with interfaces. The following definition looks
reasonable to me:
ICell = interface
function GetAsString: String;
property AsString: String read GetAsString;
end;
IWritableCell = interface(ICell)
procedure SetAsString(const AValue: String);
property AsString: String read GetAsString write SetAsString;
end;
ISpreadSheet = interface
function GetCell(ARow, ACol: Integer): ICell;
property Cell[ARow, ACol: Integer]: ICell read GetCell;
end;
IWritableSpreadSheet = interface(ISpreadsheet)
function GetCell(ARow, ACol: Integer): IWritableCell;
procedure SetCell(ARow, ACol: Integer; const AValue:
IWritableCell);
property Cell[ARow, ACol: Integer]: IWritableCell read GetCell
write SetCell;
end;
Now these interfaces can be compiled with no problem. But there is no
way to create a valid implementation:
TSpreadSheet = class(TInterfacedObject, IWritableSpreadSheet)
function GetCell(ARow, ACol: Integer): IWritableCell;
procedure SetCell(ARow, ACol: Integer; const AValue:
IWritableCell);
end;
So my question is, how can I solve this problem? Or maybe better: What
is the bad part in this design assuming that if it doesn't compile, it
is probably not a very good design.
cu Christian
|
|
| Back to top |
|
 |
Constantine Yannakopoulos Guest
|
Posted: Thu Dec 04, 2003 10:40 am Post subject: Re: interface inheritance problem |
|
|
Author := "Christian Kaufmann";
| Quote: | ISpreadSheet = interface
function GetCell(ARow, ACol: Integer): ICell;
property Cell[ARow, ACol: Integer]: ICell read GetCell;
end;
IWritableSpreadSheet = interface(ISpreadsheet)
function GetCell(ARow, ACol: Integer): IWritableCell;
procedure SetCell(ARow, ACol: Integer; const AValue:
IWritableCell);
property Cell[ARow, ACol: Integer]: IWritableCell read GetCell
write SetCell;
end;
|
"Reintroducing" GetCell in IWritableSpreadSheet is not a good idea
imho. It will confuse the user of the interfaces. I would stick to
ICell as the result of GetCell() and have the caller do a QI for an
IWriteable cell. Another approach is to have a method Assign() to
IWriteableCell, so you do not have to have a Cell[] property setter.
| Quote: | Now these interfaces can be compiled with no problem. But there is no
way to create a valid implementation:
TSpreadSheet = class(TInterfacedObject, IWritableSpreadSheet)
function GetCell(ARow, ACol: Integer): IWritableCell;
procedure SetCell(ARow, ACol: Integer; const AValue:
IWritableCell);
end;
|
There is:
TSpreadSheet = class(TInterfacedObject, IWritableSpreadSheet)
function IWritableSpreadSheet_GetCell(ARow, ACol: Integer):
IWritableCell;
function IWritableSpreadSheet.GetCell = IWritableSpreadSheet_GetCell;
procedure ISpreadSheet_GetCell(ARow, ACol: Integer): ICell;
procedure ISpreadSheet.GetCell = procedure ISpreadSheet_GetCell;
procedure SetCell(ARow, ACol: Integer; const AValue: IWritableCell);
end;
--
Constantine
|
|
| Back to top |
|
 |
Ritchie Guest
|
Posted: Sat Dec 06, 2003 2:28 am Post subject: Re: interface inheritance problem |
|
|
In article <sdzOP5cc3AflP7ORjfA8YNnZTlFE (AT) 4ax (DOT) com>,
[email]christian.kaufmann (AT) gmx (DOT) net[/email] says...
Hi :)
| Quote: | I have a new problem with interfaces. The following definition looks
reasonable to me:
ICell = interface
function GetAsString: String;
property AsString: String read GetAsString;
end;
IWritableCell = interface(ICell)
procedure SetAsString(const AValue: String);
property AsString: String read GetAsString write SetAsString;
end;
|
Inheriting interfaces from one another is usually cause for some
troubles (they're supposed to be discrete), but this derivation of
IWritableCell is fine.
| Quote: | ISpreadSheet = interface
function GetCell(ARow, ACol: Integer): ICell;
property Cell[ARow, ACol: Integer]: ICell read GetCell;
end;
IWritableSpreadSheet = interface(ISpreadsheet)
function GetCell(ARow, ACol: Integer): IWritableCell;
procedure SetCell(ARow, ACol: Integer; const AValue:
IWritableCell);
property Cell[ARow, ACol: Integer]: IWritableCell read GetCell
write SetCell;
end;
|
This is a similar problem to plain old object inheritance.
You cannot go:
TSpreadSheet = class
function GetCell(ARow, ACol: Integer): ICell; virtual;
property Cell[ARow, ACol: Integer]: ICell read GetCell;
end;
TWritableSpreadSheet = class(TSpreadsheet)
function GetCell(ARow, ACol: Integer): IWritableCell; override;
procedure SetCell(ARow, ACol: Integer; const AValue:
IWritableCell); virtual;
property Cell[ARow, ACol: Integer]: IWritableCell read GetCell
write SetCell;
end;
You can make static declarations:
TSpreadSheet = class
function GetCell(ARow, ACol: Integer): ICell;
property Cell[ARow, ACol: Integer]: ICell read GetCell;
end;
TWritableSpreadSheet = class(TSpreadsheet)
function GetCell(ARow, ACol: Integer): IWritableCell;
procedure SetCell(ARow, ACol: Integer; const AValue:
IWritableCell);
property Cell[ARow, ACol: Integer]: IWritableCell read GetCell
write SetCell;
end;
....but the TSpreadSheet.GetCell is no longer polymorphic, meaning that
if you pass a TWritableSpreadSheet into a function like this:
procedure ExportSpreadSheet(ASpreadSheet: TSpreadSheet);
Inside ExportSpreadSheet, it would call TSpreadSheet.GetCell, -not-
TWritableSpreadSheet.GetCell.
| Quote: | Now these interfaces can be compiled with no problem. But there is no
way to create a valid implementation:
TSpreadSheet = class(TInterfacedObject, IWritableSpreadSheet)
function GetCell(ARow, ACol: Integer): IWritableCell;
procedure SetCell(ARow, ACol: Integer; const AValue:
IWritableCell);
end;
|
See Constantine's message for how to use method resolution clauses.
| Quote: | So my question is, how can I solve this problem? Or maybe better: What
is the bad part in this design assuming that if it doesn't compile, it
is probably not a very good design.
|
Technically speaking, you shouldn't be deriving classes and naming
methods the same as in base classes if the derived class's method takes
different parameters or returns a different type from the base class
(constructors being an exception, but they behave like static methods,
not object methods)
I'd suggest any number of different approaches...
* Have a single method function in IWritableCell called Write(Cell:
ICell): IWritableCell and do the Supports/as/QueryInterface cast
yourself.
* Have a single method procedure in IWritableCell called Write(Cell:
ICell; AValue: String).
* Have the user of IWritableSpreadsheet do the cast (ugly), or make a
plain function called Writable(Cell): ICell to do the cast, so that you
can just so Writable(MyCell).AsString := 'New item';
* Have a separate WritableCell[] property.
* Have one ICell interface with read/write properties and a ReadOnly
property which TCell always returns True for. After all, who says that
in a writable spreadsheet, *every* cell will be writable? :)
Hope that helps some
-- Ritchie Annand
Senior Software Architect
http://www.malibugroup.com
http://nimble.nimblebrain.net
http://wiki.nimblebrain.net
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|