how to define classes inside classes in delphi? - delphi

I am in the progress of trying to learn a few new tricks in order to better organize some of the sourcecode in my units in Delphi.
I have noticed that some functions or methods I access appear to be classes inside classes, but I have not yet been successful in making a working class inside a class, although it compiles fine, I still get error messages when executing the code.
What I would like to do is to make kind of a tree with functions, procedures and values to set or get. I would be grateful if somebody could help me out a little bit with an example.
I have today some classes that are Types.
I then assign the types to a variable:
something=TSomething
and Then for something to happen I write "something.action".
My aim is to go further, and define sub-functions or/and sub-procedures.
Lets say I have three or four classes. TSnippet, TAction1, TAction2, TSubAction1, Etc.
I would like to use or assign these to a single variable and use them like:
Snippet.Action1.SubAction1.Organize(param1,param2);
Snippet.Action2.SubAction2.Returns='SomeString';
Snippet.Action1.SubAction1.SomeProcedure;
Is anybody able to help me with a useful example as in how to write code for this approach to work?
And also.. does anybydy know how such an implementation of code will affect CPYCycles needed in order to execute code versus the old fashioned method of having thousands of procedures with all different names, but more direct access (it feels like more direct access).
As of my first text was maybe a bit unclear, this follows.
I would like to make use of the editors automatic suggestions of procedures/functions available in order to simplify programming a little bit.
I started to make a Class for this, and it works great.
Consider a classname "Data". What can we do with data? We can Edit, Add, Delete, Save and Load.
Ok. This is my first Class.
Consider then another Class: "Encrypt". We can do DES, DES3, HASH, BITSHUFFLE.
I can go on with a third Class: "Compress". We can do LZW, ZIP, RAR, MP3, MPG, MP4, etc.
Instead of using these as 3 different classes, I would like to combine them in one, yet keeping them separate.
I would like to make a kind of an OwnerClass for the other classes. We can call this "MyStuff"
Whenever I type "MyStuff." in the editor, I should get up a list of "Data, Encrypt, Compress". Further, When I then choose "Compress", the next list for that class' procedures and functions will list up.
The classes may have some local variables, but the main functionality will be towards global arrays.
Maybe there are other ways of achieving this. I don't know.
My basic aim is to be able to categorize and put together routines that belong together.
This is already done in Units, but this does not help with the automatic list from the editor.
Thank you in advance.
Morten.

I think I understand what it is you're asking, after your edit.
What you're calling "classes in classes" are called properties and methods; they're other classes, variables, or procedures/functions that are declared in their containing class. The "list" you're talking about is called Code Insight, and it shows you the available properties and methods of the class you're referencing in your code at that point.
This should do something like you describe, and give you an idea of how to implement it in your own code:
unit MyStuffUnit;
interface
uses
SysUtils;
type
TEncryptionType = (etDES, etDES3, etHASH, etBITSHUFFLE);
TMyStuffEncryption = class(TObject)
private
FEncryptType: TEncryptionType;
public
constructor Create;
published
property EncryptionType: TEncryptionType read FEncryptType
write FEncryptType;
end;
TCompressionType = (ctLZW, ctZIP, ctRAR, ctMP3, ctMPG, ctMP4);
TMyStuffCompression = class(TObject)
private
FCompressionType: TCompressionType;
public
constructor Create;
published
property CompressionType: TCompressionType read FCompressionType
write FCompressionType;
end;
TMyStuff = class(TObject)
private
FCompression: TMyStuffCompression;
FEncryption: TMyStuffEncryption;
public
constructor Create;
destructor Destroy; override;
published
property Compression: TMyStuffCompression read FCompression
write FCompression;
property Encryption: TMyStuffEncryption read FEncryption
write FEncryption;
end;
implementation
constructor TMyStuffEncryption.Create;
begin
inherited;
FEncryptType := etDES;
end;
constructor TMyStuffCompression.Create;
begin
inherited;
FCompressionType := ctLZW;
end;
constructor TMyStuff.Create;
begin
inherited;
FCompression := TMyStuffCompression.Create;
FEncryption := TMyStuffEncryption.Create;
end;
destructor TMyStuff.Destroy;
begin
FCompression.Free;
FEncryption.Free;
inherited;
end;
end.
If you create an instance of TMyStuff in your code, you should be able to type MyStuff. and get the option of choosing Compression or Encryption. Choosing Compression should allow you to set the CompressionType property.
This should be enough to get you going. :-) Remember that Delphi includes the source code for the VCL and RTL in almost all editions, so you always have that to look at for examples. (They're not always the very best examples, but they should give you ideas on how to do things.)

Related

Delphi access private function in VCL unit

How do I access private function in a VCL unit?
I need to call the function ColorToPrettyName from ExtCtrls.pas.
Now I copied this to my source but IMO nicer would be to use that function instead.
Some more details:
The function is used in TColorBox, but I only need the pretty name.
I tried to instantiate TColorBox and get the pretty name from it, but this is only possible when I have some TWinControl to assign to its Parent. But that TWinControl I don't have in the place where I want to use the pretty name and I don't want to do any hacks.
You cannot readily call this function from outside the unit since it is not exposed.
You could create an instance of the control that calls the function, and persuade it to do the dirty work. That's perfectly feasible as another another answer demonstrates.
You could use a disassembler to find the address of the function and call it using a procedural variable. The madExcept source code is a great source of examples of that technique.
On balance, copying the source to your code is, in my view, the best option. All of your available options have drawbacks and this seems the simplest.
You will find a sample how to access the ColorToPrettyName here:
https://github.com/project-jedi/jvcl/blob/master/donations/Colors/JvFullColorSpaces.pas
// (outchy) Hook of TColorBox to have access to color's pretty names.
// Shame on me but that's the only way to access ColorToPrettyName array in the
// ExtCtrls unit. Thanks Borland.
{$IFDEF COMPILER6_UP}
type
TJvHookColorBox = class (TCustomColorBox)
protected
function GetItemsClass: TCustomComboBoxStringsClass; override;
procedure DestroyWindowHandle; override;
public
constructor Create; reintroduce;
procedure CreateWnd; override;
procedure DestroyWnd; override;
end;
........
This is what you asked, though this is definitely not good practice.
A better solution would be to use the same function from JEDI (JvJCLUtils.pas), although this adds a dependency.
You will find JEDI here: http://jvcl.delphi-jedi.org/
It contains many more useful utilities and components.
Like David said; copying the source to your own unit will be the best.
I believe ColorToPrettyName will not be changed that often but if you are worried that it will and your copied code will be different upon an upgrade of Delphi then you can add a compiler directive to your code that checks the version and warns you about it. Then you can update your code and wait till the next time you upgrade your Delphi. Easy.

Can "unused" classes be made available in Delphi XE

I'm working in Delphi XE, windows 7.
In an application I want to enable different report types for my users to select.
To do this, I have 1 base report class and a subclass per report type (xml, csv, ppt, etc).
{Just an illustrating example}
TBaseReport = class
public
constructor Create;
procedure GenerateReport; virtual; abstract;
class function ReportType: string; virtual; abstract;
end;
T*Report = class(TBaseReport);
//Etcetera.
What I want to do is use Rtti to detect all report classes and list their ReportType.
After that, I want to use Rtti to create an instance of the chosen report class and call GenerateReport. All in all, this is not too difficult to achieve.
However there is a major drawback: I'm never hard coding the use of the descending classes, so the code does not get included in the executable.
Is there a decent way to force the linker/compiler to include these classes?
A(n ugly) work around would be to simulate usage of the reports in their initialization section, but I'd rather not do that.
A better solution is to make the base class persistent and to call 'RegisterClass(T*Report);' in the initialization section. It works, but I do not see any other need to make them persistent, so again, I'd rather not do that. On the other hand, maybe this is the only way to do it?
Thanks in advance.
You can create your own version of RegisterClass. Something like RegisterReportClass. Internally you keep your own list of report classes that can be used. Your register function will take a TBaseReport class type - No need for TPersistent.
Your RegisterReportClass method should be called in the Initialization section making sure the classes are included.
If you look in the Graphics unit you can see TFileFormatsList = class(TList). This is the class that is used to hold the different Graphic Types and could be used as an example for creating your own TReportFormatsList. Delphi uses a static function TPicture.RegisterFileFormat to add items to their internal list.
You can use the {$STRONGLINKTYPES ON} Compiler Directive, to include all symbols of your app in the final exe, remember that this option increases the executable size, as more RTTI is included in the executable.

"Object Aware" GUI Controls

I have some business objects written in Delphi with a custom scheme of database persistence which is finally working for my needs. Ok, great. Now it's time for GUI implementations. And here begins the problems.
How to bind my object to the GUI properly?
I cannot use Data Aware controls since I isolated all data access components into the ORM layer, so I start to write some "Object Aware" controls using the RTTI unit (I'm working with Delphi 2010), but I have the feeling I'm going the wrong way...
Some ideas on how to resolve this using only the VCL controls?
You have several patterns for linking ORM with User Interface.
See for instance the Model GUI Mediator pattern. In short, you write an observer which will reflect the ORM content into the UI components, and vice versa. This has been implemented for instance in the tiOpf framework for Delphi (this link has videos).
Another approach is to map your data at runtime: you design your form just like usual, then you fill the content in the OnShow event, then the "Save" or "OK" button will validate then save the content into the ORM record. This is what is done in the main Sample application of our framework. Easy to code in this simple sample, but could lead into spaghetti code if you have a lot of fields and validation to operate.
The last approach is to let your ORM creates the form.
In our framework, you can define some UI properties about each table, in a dedicated structure. Then a single unit will create a form with all editable fields of your ORM object. Links to other records will be displayed as a combo box, booleans as checkboxes, sets as radioboxes, and so on. Then the filtering (e.g. trim a text field from spaces on left or right side) and the validation (e.g. ensure that a field value is unique or a valid IP address) is handled not in the UI part, but in the business logic itself, i.e. the ORM.
IMHO it's mandatory to keep a true multi-tier architecture. That is, the UI has to rely mostly on the business logic. For instance, data validation must be part of the ORM, not of the UI. For instance, if you decide to add a web client to your Delphi client application, you won't have to code the validation another time: it will be common to both clients, separated from the UI implementation details.
What you could do (though I have no code samples) is use a combination of
class helpers or interceptor classes
binding interfaces for single domain objects and/or domain object lists
Class helpers have the disadvantage that they are not officially supported and you cannot add any fields to the class you are helping.
Interceptor classes are simply descendant classes with the same name as their ancestor:
uses
stdctrls;
type
TButton = class(stdctrls.TButton)
end;
You can put interceptor classes in their own unit and use that whereever you want. Just make sure these units are included AFTER the standard unit, so your descendant is used at run time.
Benefit of interceptor classes:
You can continue to design your UI using standard VCL or third party controls.
You get all the advantages of descendants.
You do not need to create or install your own controls.
No need for special mapper classes or use of RTTI.
Easily (well, relatively easily) integrated into a (DUnit-) testable user interface along the lines of Julian Bucknall's article on this in the (distinct) Delphi Magazine as referred to in this question/answer: Unit-testing mouse event handlers
Pseudo sample of interceptor control with binding interface / command interface:
uses
stdctrls;
type
ICommandAction = interface(IInterface)
function IsEnabled: Boolean;
procedure Execute;
procedure Update;
end;
IBindSingle = interface(IInterface)
function GetValueFromControl: string;
procedure LoadValueIntoControl(const aValue: string);
end;
TButton = class(stdctrls.TButton, ICommandAction)
protected
function IsEnabled: Boolean;
procedure Execute;
procedure Update;
end;
TEdit = class(stdctrls.TEdit, IBindSingle)
function GetValueFromControl: string;
procedure LoadValueIntoControl(const aValue: string);
end;
implementation could be along the lines of:
function TButton.IsEnabled: Boolean;
begin
Result := Self.Enabled;
end;
procedure TButton.Execute;
begin
Self.Action.Execute;
end;
procedure TButton.Update;
begin
Self.Action.Update;
end;
function TEdit.GetValueFromControl: string;
begin
Result := Self.Text;
end;
procedure LoadValueIntoControl(const aValue: string);
begin
Self.Text := aValue;
end;
My current customer have made their own "mapper" classes in the past (before I came).
Their data objects have fields (which are objects), and you can map these fields to a control. I extended the framework by using a MVC-like approach:
edtTarraCode: TAdvEdit;
procedure TframTarraTab.InitMapping;
begin
...
Mapper.AddMapping(edtTarraCode, Controller.DataModel.tarID);
...
end;
Per control a simple "mapping" class is created:
TMappingAdvEdit = class(TBaseEditMapping)
protected
procedure InitControl; override;
procedure AppData2Control; override;
procedure Control2AppData; override;
end;
No rocket sience, and maybe better solutions are available in the mean time (this worked in D6 and lower :-) ) but it works good enough for the customer.
Btw: also a data object generator is used. So if a field changes in the database (for example tarra.tarid is changed into tareID) we get a compiler error because "tarid" does not longer exist. This works much better than "fixed string" mapping (runtime errors).
There's currently no way to do this using only VCL controls. I know that Lazarus has a set of RTTI-based data-aware controls; you might want to look at them for some basic ideas. But it's more difficult than you might think at first. For example, unlike a dataset, an object has no built-in signaling mechanism when its members' values change. Which means that unless your data-binding controls own the object completely and nothing else has access to it, it's possible for some other code to change some value and then that change doesn't get reflected in the UI.
I've heard various things from the Delphi team in the last few years about extending the object model or the RTTI model to allow for better data binding, but whatever that's about is still a few years out.
Take a look at EverClassy Dataset at http://www.inovativa.com.br. It may meet your needs. EverClassy Dataset is a Delphi dataset designed to be populated by objects instead records from a database system.
With this component you will have the chance to interoperate your domain objects with dataware componentes, what will give you great power to build your GUI.

Correct way to duplicate Delphi object

What are pros and cons of duplication an object instance with constructor or instance function?
Example A:
type
TMyObject = class
strict private
FField: integer;
public
constructor Create(srcObj: TMyObject); overload;
//alternatively:
//constructor CreateFrom(srcObj: TMyObject);
property Field: integer read FField;
end;
constructor TMyObject.Create(srcObj: TMyObject);
begin
inherited Create;
FField := srcObj.Field;
end;
Example B:
type
TMyObject = class
strict private
FField: integer;
public
function Clone: TMyObject;
property Field: integer read FField;
end;
function TMyObject.Clone: TMyObject;
begin
Result := TMyObject.Create;
Result.FField := FField;
end;
One major difference immediately springs to mind - in the latter case the Create constructor would have to be virtual so that a class hierarchy supporting Clone could be built basing on the TMyObject.
Assume that this is not a problem - that TMyObject and everything based on it is entirely under my control. What is your preferred way of doing copy constructor in Delphi? Which version do you find more readable? When would you use former or latter approach? Discuss. :)
EDIT:
My main concern with the first example is that the usage is very heavy compared to the second approach, i.e.
newObj := TMyObject.Create(oldObj)
vs.
newObj := oldObj.Clone;
EDIT2 or "Why I want single-line operation"
I agree that Assign is a reasonable approach in most cases. It's even reasonable to implement 'copy constructor' internally by simply using assign.
I'm usually creating such copies when multithreading and passing objects through the message queue. If object creation is fast, I usually pass a copy of the original object because that really simplifies the issues of object ownership.
IOW, I prefer to write
Send(TMyObject.Create(obj));
or
Send(obj.Clone);
to
newObj := TMyObject.Create;
newObj.Assign(obj);
Send(newObj);
The first adds information about which object to want to create, the second not. This can be used to instantiate e.g. a descendant or an ancestor of a class
The Delphi way (TPersistent) separates creation and cloning:
dest := TSomeClass.Create;
dest.Assign(source);
and has this same property that you explicitly choose the class to instantiate. But you don't need two constructors, one for normal use, and one where you want to clone.
edit due to oneline requirement
You can mix it of course using Delphi metaclasses (untested)
type
TBaseSomeObject = class;
TBaseObjectClass = class of TBaseSomeObject;
TBaseSomeObject = class(TPersistent)
function Clone(t: TBaseObjectClass = nil): TBaseSomeObject; virtual;
end;
...
function TBaseSomeObject.Clone(t: TBaseObjectClass = nil): TBaseSomeObject;
begin
if Assigned(t) then
Result := t.Create
else
Result := TBaseObjectClass(Self.ClassType).Create;
Result.Assign(Self);
end;
SendObject(obj.Clone); // full clone.
SendObject(obj.Clone(TDescandantObject)); // Cloned into Descendant object
For the rest, just implement your assign() operators, and you can mix multiple ways.
edit2
I replaced the code above with code tested in D2009. There are some dependencies of the types that might have confused you, hope it is clearer this way. Of course you'll have to study the assign mechanism. I also tested the metaclass=nil default parameter and it works, so I added it.
I don't think there is a correct way it just depend on personal style. (And as Marco pointed out, there are more ways.)
The constructor way is short but it violates the principle that the constructor must only construct the object. Which is possibly not a problem.
The clone way is short although you need to provide a call for each class.
The assign way is more Delphi like. It separates creation and initialization which is good because we like the one method one function concept that makes code better to maintain.
And if you implement Assign using streams, you have only one place to worry about which fields need to be available.
I like the clone style - but only in Java (or any other GC language). I used it some times in Delphi, but mostly I stay with Create and Assign, because it is much clearer who is responsible for the destruction of the object.
I use the second method, the one with the Clone function, and it works like a charm, even with complex classes. I find it more readable and error proof.

Get list of object's methods, properties and events?

When trying a new component for which there's no documentation, I need to go through its methods, properties, and events to try to figure out what it can do. Doing this through the IDE's Object Inspector is a bit tedious.
Is there a utility that presents this list in a more readable format?
Thank you.
When I want to know what something can do, I read the source code. The class declaration will contain a succinct list of all the methods and properties, unless there is a lot of inheritance. The definitions will tell you want the methods do.
Another thing is to declare a variable of the type you're interested in, type its name and a period, and then press Ctrl+Space to let Class Completion show you everything you can do.
As the others said, use the source. Also an UML tool will help.
But if you don't want to use this, you can use this procedure (you need Delphi 2010 for this, and be sure to add RTTI to your 'Uses' clause):
procedure DumpProps(aObject: TObject; aList: TStringList);
var
RttiContext: TRttiContext;
RttiType: TRttiType;
I: Integer;
n: integer;
props: TArray<TRttiProperty>;
begin
aList.Clear; //it must be <> nil
RttiType := RttiContext.GetType(aObject.ClassType);
props:=RttiType.GetProperties;
with aList do
begin
Append('');
Append('==========');
Append('Begin Dump');
Append('----------');
for I := Low(props) to High(props) do
begin
try
Append(props[i].Name+': '); //odd construction to see if the Getter blows
n:=Count-1;
Strings[n]:=Strings[n]+props[i].GetValue(aObject).AsString;
except
on E: Exception do
Strings[n]:=Strings[n]+' >>> ERROR! <<< '+E.Message;
end;
end;
end;
end;
The above you can use either at run-time, either if you build a Menu Wizard, you can have your info at design time.
HTH
You can use the Class Browser that comes with GExperts.
I would also recommend to build a Model diagram with the IDE or ModelMaker. It helps to see the visual relations.
In the immortal words of Obi Wan Kenobi -- "Use the source".
There is no substitute for reading and understanding the source code of a component (or anything) to understand what it does and what it is up to.
Source code is the Lingua Franca of programming.
Look at the .hpp that is generated for C++Builder support. It resembles the interface section of a Delphi unit.
I just use code completion. If you can't figure out what the component does from the names of the properties and methods, then it's probably poorly designed anyway, and you're better off not using it. Also, since you're asking the question, I'm guessing you do not have the source. If you don't, again, I wouldn't use the component. You're only storing trouble for yourself.
There is RTTI...

Resources