Should I be referencing to Field or Property names? [duplicate] - delphi

This question already has answers here:
Using properties instead of fields in class methods of the same unit is a bad practice?
(3 answers)
Closed 9 years ago.
I have been writing different classes lately, and I have noticed that I have inadvertently been reading / writing to them using both field and property identifiers, and I am wondering what are the pitfalls if any of doing this?
Lets use a basic class as an example:
TMyClass = class
private
FName: string;
FID: Integer;
public
constructor Create(AName: string);
destructor Destroy; override;
published
property Name: string read FName write FName;
property ID: Integer read FID write FID;
end;
By field identifier I mean, for example FName and FID, and by property identifier I mean Name and ID for example.
The whole purpose of the published property is to be able to access it outside of the unit the class is written in if I am not mistaken. Which surely means the field identifiers should be used in the unit the class is written in, after all you cannot access those field identifiers outside the class.
This is where I have noticed that in some of the procedures (private or protected) I have not been using FName or FID, but instead have used the property equivalent, Name and ID - or sometimes mixed.
So far I have seen no issues, and in fact I normally would use FName and FID but like I say, have inadvertently not done so for some reason.
Is this bad practice or could it lead to something more sinister?
Thanks.

The purpose of a property is not to allow access to class data outside of the class per se.
This could be easily done by declaring your member data as public.
The purpose of properties is to facilitate good OOP.
The following points illustrate the concept.
Side effects
The main purpose of properties is to hide the implementation details of your class and allow 'side-effects' when setting properties.
This is obvious in the VCL where changing the height property will automatically change the window's appearance due to the side effects coded in the SetHeight Setter.
This is part of the OOP concept of information hiding.
These side effects are useful whether you are inside you class or not.
The other useful aspect kicks in when your class or one of its descendants changes the behavior of the property; adding a side-effect where there was none before.
If your old code inside the original class fiddles with the fields directly these side-effects will not trigger, breaking the change inside the descendant.
Rule of thumb: side effects
Always use the property, unless you explicitly want to prevent a side-effect from triggering. !!Remember getters can have side-effects as well.
Implementation hiding
Sometimes the fields are not a direct translation of their appearance in the property.
Or you may want to change the implementation under the hood, but keep the properties the same.
Again in this case you may want to hide these details in your own class as well, so that descendant classes do not break.
e.g. if you have your storage implemented as a red/black tree with pointers you want to minimize the impact to the rest of the routine when you decide to switch to an array based offset structure.
Rule of thumb: implementation hiding
Only access the fields directly in these routines that deal directly with the data.
Limit the number of those routines by putting general purpose routines in place, like: locaters, iterators etc.

Related

Possible to change property write methods programmatically using RTTI for creating object-aware controls?

I have a business object that I would like to "connect" to my UI better. I've seen some partial solutions for making objects data-aware, but they all involved significant changes to my business object, including an extra layer of abstraction.
I've been looking into the improved RTTI in new versions of Delphi, and it looks very interesting and useful. I'm wondering if I could use it to programmatically inject new write methods for all properties.
The way this would work is that my TEdit descendant would by given a reference to an object property when the form is built. The TEdit would then insert a reference to itself in an attribute for that property (and of course remove itself on destructor or being given another reference). The TEdit would also ensure that the write method for the property is replaced by one that notifies the TEdit of changes after calling the original write method.
Is this feasible? The big show stopper would be that injecting a new write method isn't possible, hence the title for this question.
There are also potential problems with derived properties, but it should be possible to find a solution for that.
Your question already puts you ahead of me with programming skill so I'll just add how I might approach this:
If I were to try to write something like that I'd probably start with a TList for each field in your TBusinessObject. That list would be used to indicate what needed to be updated when you needed to push out a change.
So when the TEdit is created it would add itself to a list which was associated with a piece of data in your TBusinessObject. When the TBusinessObject updated that piece of data it would then run through the list of attached objects. It would see the TEdit and, knowing it was a TEdit, would run code to update the .Text. If I attached a TCaption then the code would update the .Caption.
The TEdit, as you indicated, would need to tell the TBusinessObject when it's value was updated. I guess this is the tricky spot - you could create a new TEdit and add in a TList to maintain who it should inform when it changes. If you used the .Tag to indicate a field number in the TBusinessObject then the OnChange (or whatever event) could then call something like TBusinessObject.FieldUpdate[TEdit.Tag, NewValue] which then triggers your business logic. That, in turn, might make the TBusinessObject update other fields, which may have their own TLists to fields to update.
Preventing circular updates would require that you have a way of updating a control without triggering events. For one program I wrote I had two methods to update the control: SetValue and ChangeValue. SetValue disabled any events (OnChange, OnValidate), updated the control's value and then reenabled the events. ChangeValue simply changed the value and allowed any of the control's events to fire as required.
There are probably slicker ways to do this but hopefully this gives you food for thought.
Possible to change property write methods programmatically using RTTI for creating object-aware controls?
No, it's not possible. RTTI gives you information, it doesn't give the ability to alter types at runtime.
The big show stopper would be that injecting a new write method isn't possible, hence the title for this question
In order for you to change this at runtime there should be something similar to an event handler that you can set. It's an easy concept, but it has some runtime overhead, both in call time (it would be an indirection where a direct call would normally suffice) and in terms of required memory (each property would require an extra TEvent style field). This is easy for you to implement if you need it, but it would be harmful if the compiler automatically generated such code for all classes "just in case".
If you're thinking of somehow patching the code in memory at runtime, that's not going to work and it would be, at best, unreliable.
In this post entitled Inducing The Great Divide, Cobus Kruger talked about business objects.
The solution he cooked is essentially compliant to your requirements:
Make use of advanced RTTI features introduced in recent Delphi version.
Separation of the business logic from presentation logic.
Any PODO (Plain Old Delphi Object) will do as business object !
The magic lays in the TObjectBinding class which ties any TWinControl with any business object.
Excerpt:
TObjectBinding = class
private
fCtx: TRttiContext;
fControlType: TRttiType;
fObjType: TRttiType;
fPropFieldMapping: TDictionary<TRttiProperty, TRttiField>; // Dictionary of object Properties & corresponding Fields
fControl: TWinControl; // The control (normally form)
fObj: TObject; // Object it represents.
procedure CreateMappings;
function FindField(Prop: TRttiProperty; out Field: TRttiField): Boolean;
function FieldClass(Field: TRttiField): TClass;
// Modify these to change the rules about what should be matched.
function IsValidField(Field: TRttiField): Boolean;
function IsValidProp(Prop: TRttiProperty): Boolean;
// Modify these to change the mappings of property type to VCL control class.
procedure AssignField(Prop: TRttiProperty; Field: TRttiField);
procedure AssignProp(Prop: TRttiProperty; Field: TRttiField);
// Used from AssignField/AssignProp. Extend these to support a wider range of properties.
function GetPropText(Prop: TRttiProperty): string;
procedure SetPropText(Prop: TRttiProperty; const Text: string);
public
constructor Create(Control: TWinControl; Obj: TObject);
destructor Destroy; override;
//
procedure Load;
procedure Save;
end;
I hope that this will be a good starting point for you.

Class Reference as Property

Google is useless for these sorts of searches, because you get hundreds of millions of results absolutely none of which relate to the specific question.
The question is simply this:
Is it possible to have a Class Reference Property in Delphi?
If so, how?
Here's what I've tried...
type
TMyObject = class
// ...
end;
TMyObjectClass = class of TMyObject
TMyObjectA = class(TMyObject)
// specifics here
end;
TMyObjectB =class(TMyObject)
// specifics here
end;
TMyComponent = class(TComponent)
private
FObjectType: TMyObjectClass;
published
property ObjectType: TMyObjectClass read FObjectType write FObjectType;
end;
The above code compiles fine, however the Object Inspector does not show the ObjectType property at all.
My objective here (if you haven't already guessed) is to make it so that I can select a class descendant from a specific base class, to make the same component behave in a different way.
I want to do it this way so that the component doesn't need to know about the sub-classes directly (it needs to be fully modular).
Let me just make this bit clear: I cannot use an Enum to choose between the sub-class types as the component cannot directly link to the sub-class types (It's simply not possible in this particular case)
Anyway... thanks in advance!
You can find all classes that descend from a particular base class: Delphi: At runtime find classes that descend from a given base class? and make this a special property with list of values using TPropertyEditor.
If you were going to do this then you would need to provide a property editor. The IDE does not come with property editors for class type properties. You would also need to handle .dfm persistence. You would write the class type out to the .dfm file as a string and when the .dfm file is read, you would need to fixup the reference. New style RTTI could do that.
However, I don't think any of this is actually viable for the following reason. Your design time code runs in a package inside the IDE and does not have access to the class types in the active project in the IDE. Those class types only exist when that project runs. So the ObjectType property in the code in your question cannot be assigned to anything meaningful in the design time package. Well, you could use it for classes defined in the VCL and any other packages installed in your IDE but I rather imagine you'd want to use it on classes defined in the active project.
I think all this means that you should instead use a simple string property and fixup the class type references only at runtime.

SW-Design: Adapters for Class Hierarchy in Delphi (Generics vs. Downcast)

I would like to have some suggestions for the following problem:
Let's say you want to write adapters for the VCL controls. All Adapters should have the same base class, but differ in wrapping special controls (for example getting a value from a TEdit is different from getting a value from TSpinEdit).
So the first idea is to create a class hierarchy like
TAdapter = class
end;
TEditAdapter = class (TAdapter)
end;
TSpinEditAdapter = class (TAdapter)
end;
Now I want to introduce a field to hold a reference to the vcl control. In my special adapaters I want - of course - work with the concrete subclass. But the Base class should also contain a reference (for example if I want to use the adapter to make a control visible).
Possibility 1 (Downcast in Property Accessor):
TAdapter = class
protected
FCtrl : TControl;
end;
TEditAdapter = class (TAdapter)
public
property Control : TEdit read GetControl write Setcontrol;
end;
{...}
function TEditAdapter.GetControl : TEdit;
begin
Result := FCtrl as TEdit;
end;
So if I implement a specific method I work with the property Control, if I do something in my base class I use the protected field.
Possibility 2 (Use a generic base class):
TAdapter = class
end;
TAdapter <T : TControl> = class (TAdapter)
protected
FCtrl : T;
end;
TEditAdapter = class (TAdapter <TEdit>)
end;
Which solution would you prefer? Or is there a third solution, which is even better?
Kind regards,
Christian
You can't use generics to solve this problem, because you'll be in one of two situations:
The property or method you want to "Adapt" (the Text property for example) is defined in an ancestor class. In that case you don't need generics because you can use the one adapter for the ancestor and solve the problem for all descendants.
The property or method is introduced by the class you want to adapt. In that case you can't use generics because in order to access the property or method you'd need a generic type constraint of that given type. Example. Let's say you want an adapter for the Text property of TMyClass. Let's assume TMyClass is the one introducing the Text property. In order to access it, you'd need to declare the generic type as TGeneric<T:TMyClass> and that's not actually generic.
In my opinion the best bet is to write specific adapters for each class, as in your first option. You might be able to use RTTI tricks to make your first option easier to implement, but I'm not sure it'd be worth it.
The generics version could allow to avoid some duplicated code, at least in the TAdapter class. By using the T type, it will allow a lot of shared code.
On the other hand, due to the VCL hierarchy, most used properties and methods will already be in TControl. So I'm not sure there will be so many duplicated code in non-generic implementation.
I suspect the non-generic version will produce less code and RTTI, since the current generics implementation tends not to duplicate the source, but increase the exe size.
IMHO the generic-based design will add more abstraction to the implementation, but the non-generic would perhaps be more close to the VCL hierarchy it will adapt on.
So for your particular case (mapping the VCL), since your attempt is to map non generic classes, I'd rather investigate into the non-generic solution.
For another (non VCL-based) adapter architecture, I would probably have advised a pure generic implementation, from the bottom up.

Delphi class references... aka metaclasses... when to use them

I've read the official documentation and I understand what class references are but I fail to see when and why they are best solution compared to alternatives.
The example cited in the documentation is TCollection which can be instantiated with any descendant of TCollectionItem. The justification for using a class reference is that it allows you to invoke class methods whose type is unknown at compile time (I assume this is the compile time of TCollection). I'm just not seeing how using TCollectionItemClass as an argument is superior to using TCollectionItem. TCollection would still be able to hold any descendant of TCollectionItem and still be able to invoke any method declared in TCollectionItem. Wouldn't it?
Compare this with a generic collection. TObjectList appears to offer much the same functionality as TCollection with the added benefit of type safety. You are freed from the requirement to inherit from TCollectionItem in order to store your object type and you can make a collection as type specific as you want. And if you need to access item's members from within the collection you can use generic constraints. Other than the fact that class references are available to programmers prior to Delphi 2009 is there any other compelling reason to use them over generic containers?
The other example given in the documentation is passing a class reference to a function that acts as an object factory. In this case a factory for creating objects of type TControl. Its not really apparent but I'm assuming the TControl factory is invoking the constructor of the descendant type passed to it rather than the constructor of TControl. If this is the case then I'm starting to see at least some reason for using class references.
So I guess what I'm really trying to understand is when and where class references are most appropriate and what do they buy a developer?
MetaClasses and "class procedures"
MetaClasses are all about "class procedures". Starting with a basic class:
type
TAlgorithm = class
public
class procedure DoSomething;virtual;
end;
Because DoSomething is a class procedure we can call it without an instance of TAlgorithm (it behaves like any other global procedure). We can do this:
TAlgorithm.DoSomething; // this is perfectly valid and doesn't require an instance of TAlgorithm
Once we've got this setup we might create some alternative algorithms, all sharing some bits and pieces of the base algorithm. Like this:
type
TAlgorithm = class
protected
class procedure DoSomethingThatAllDescendentsNeedToDo;
public
class procedure DoSomething;virtual;
end;
TAlgorithmA = class(TAlgorithm)
public
class procedure DoSomething;override;
end;
TAlgorithmB = class(TAlgorithm)
public
class procedure DoSomething;override;
end;
We've now got one base class and two descendent classes. The following code is perfectly valid because we declared the methods as "class" methods:
TAlgorithm.DoSomething;
TAlgorithmA.DoSomething;
TAlgorithmB.DoSomething;
Let's introduce the class of type:
type
TAlgorithmClass = class of TAlgorithm;
procedure Test;
var X:TAlgorithmClass; // This holds a reference to the CLASS, not a instance of the CLASS!
begin
X := TAlgorithm; // Valid because TAlgorithmClass is supposed to be a "class of TAlgorithm"
X := TAlgorithmA; // Also valid because TAlgorithmA is an TAlgorithm!
X := TAlgorithmB;
end;
TAlgorithmClass is a data type that can be used like any other data type, it can be stored in a variable, passed as a parameter to a function. In other words we can do this:
procedure CrunchSomeData(Algo:TAlgorithmClass);
begin
Algo.DoSomething;
end;
CrunchSomeData(TAlgorithmA);
In this example the procedure CrunchSomeData can use any variation of the algorithm as long as it's an descendant of TAlgorithm.
Here's an example of how this behavior may be used in a real-world application: Imagine a payroll-type application, where some numbers need to be calculated according to an algorithm that's defined by Law. It's conceivable this algorithm will change in time, because the Law is some times changed. Our application needs to calculate salaries for both the current year (using the up-to-date calculator) and for other years, using older versions of the algorithm. Here's how things might look like:
// Algorithm definition
TTaxDeductionCalculator = class
public
class function ComputeTaxDeduction(Something, SomeOtherThing, ThisOtherThing):Currency;virtual;
end;
// Algorithm "factory"
function GetTaxDeductionCalculator(Year:Integer):TTaxDeductionCalculator;
begin
case Year of
2001: Result := TTaxDeductionCalculator_2001;
2006: Result := TTaxDeductionCalculator_2006;
else Result := TTaxDeductionCalculator_2010;
end;
end;
// And we'd use it from some other complex algorithm
procedure Compute;
begin
Taxes := (NetSalary - GetTaxDeductionCalculator(Year).ComputeTaxDeduction(...)) * 0.16;
end;
Virtual Constructors
A Delphi Constructor works just like a "class function"; If we have a metaclass, and the metaclass knows about an virtual constructor, we're able to create instances of descendant types. This is used by TCollection's IDE Editor to create new items when you hit the "Add" button. All TCollection needs to get this working is a MetaClass for a TCollectionItem.
Yes a Collection would still be able to hold any descendant of TCollectionItem and to invoke methods on it. BUT, it wouldn't be able to instantiate a new instance of any descendant of a TCollectionItem. Calling TCollectionItem.Create constructs an instance of TCollectionItem, whereas
private
FItemClass: TCollectionItemClass;
...
function AddItem: TCollectionItem;
begin
Result := FItemClass.Create;
end;
would construct an instance of whatever class of TCollectionItem descendant is held in FItemClass.
I haven't done much with generic containers, but I think that given a choice, I would opt for the generic container. But in either case I'd still have to use a metaclass if I wanted to have the list instantiate and do whatever else needs to be done when an item is added in the container and I do not know the exact class beforehand.
For example an observable TObjectList descendant (or generic container) could have something like:
function AddItem(aClass: TItemClass): TItem;
begin
Result := Add(aClass.Create);
FObservers.Notify(Result, cnNew);
...
end;
I guess in short the advantage/benefit of metaclasses is any method/class having only knowledge of
type
TMyThing = class(TObject)
end;
TMyThingClass = class of TMyThing;
is able to construct instances of any descendant of TMyThing whereever they may have been declared.
Generics are very useful, and I agree that TObjectList<T> is (usually) more useful than TCollection. But class references are more useful for different scenarios. They're really part of a different paradigm. For example, class references can be useful when you have a virtual method that needs to be overridden. Virtual method overrides have to have the same signature as the original, so the Generics paradigm doesn't apply here.
One place where class references are used heavily is in form streaming. View a DFM as text sometime, and you'll see that every object is referred to by name and class. (And the name is optional, actually.) When the form reader reads the first line of an object definition, it gets the name of the class. It looks it up in a lookup table and retrieves a class reference, and uses that class reference to call that class's override of TComponent.Create(AOwner: TComponent) so it can instantiate the right kind of object, and then it starts applying the properties described in the DFM. This is the sort of thing that class references buy you, and it can't be done with generics.
I also would use a metaclass whenever I need to be able to make a factory that can construct not only one hard-coded class, but any class that inherits from my base class.
Metaclasses are not the term I am familiar with in Delphi circles however. I believe we call them class references, which has a less "magic" sounding name, so it's great that you put both common monikers in your question.
A concrete example of a place I have seen this used well is in the JVCL JvDocking components where a "docking style" component provides metaclass information to the base docking component set, so that when a user drags their mouse and docks a client form to the docking host form, the "tab host" and "conjoin host" forms that show grabber bars (similar in appearance to the title bar of a regular undocked window) can be of a user-defined plug-in class, that provides a customized appearance and customized runtime functionality, on a plug-in basis.
In some of my applications I have a mechanism that connects classes to forms capable of editing instances of one or more of that classes. I have a central list where those pairs are stored: a class refernce and a form class reference. Thus when I have an instance of a class I can lookup the corresponding form class, create a form from it and let it edit the instance.
Of course this could also be implemented by having a class method returning the appropriate form class, but that would require the form class to be known by the class. My approach makes a more modular system. The form must be aware of the class, but not the other way round. This can be a key point when you cannot change the classes.

Delphi - Declaring in class or not?

Just recently, probably because I've been maintaining some old code, I've started to look at how / why I do things. As you do.
Most of my Delphi programming has been picked up in house, or from examples scattered across the web or manuals. And in some things are done just because "that's how I do it"
What I'm currently wondering about is Declaration, of variables, procedures, functions, etc.
When I am working with a form, I will place all my procedures and functions under public or private. Whilst I will try to avoid global vars and constants will generally go under var or const, either in the interface or implementation, depending on where they need to be called (occasionally though they will go in public / private)
Otherwise, if its just a unit I will declare the procedure in the interface and use in the implementation. Some of the code I've been maintaining recently has no interface declaration but instead has everything properly ordered with calls after procedures...
Is there a correct way to do this? Are there rules of what should / should not go in the class? Or is it a style / when you started thing?
Edit to add
My question is not about whether a declaration of a procedure goes in private/public but whether all declarations in a TForm Unit should go in one of these. Similarly should var / const be in one or the other?
Further clarification
I understand that not declaring in interface, or declaring in public/private/etc affects the visibility of procedures/functions to other units in my applicaiton.
The core of my question is why would i not want to declare? - especially when working in a form/unit when placing in private is much more explicit that the thing declared is not available to other units...
Cheers
Dan
Everything that can have a different value depending on the concrete instance belongs to the class, i.e.
TDog = class
strict private
FColor : TColor;
FName : String;
public
property Color : TColor read FColor write FColor;
property Name : String read FName write FName;
end;
Color and name are clearly attributes of each dog (and each dog will have other values here).
General rules:
Fields belong in private (visible in this class and in this unit) or strict private (visible only in this class)
If you need access to fields from other classes, create a public property. This gives you the freedom to change the simple field access to a more sophisticated getter / setter method lateron without changing the interface of your class.
Everything should be as local as possible. If private is enough, there's no need to make it protected (visible in subclasses too). And only make those things public that you really need from the outside.
Forms: only those things that you want to be stored in the DFM file should be published.
Put as much as you can in the implementation section and as little as you can in the interface section. This is also true for uses clauses.
You might be confusing the term global variable. If it's declared in a class it's not a global variable (even if declared public). Global variables (which you correctly consider good to avoid) always go in a var section either in the interface or the implementation section (which is preferrable following the general rules above)
The question seems to deal with scope. In other words, how easily accessible things can or should be.
As a general guideline, you want to reduce the scope of things as much as possible but still keep them accessible enough to be reused. The reason for this is:
that as your system grows and becomes more complex, the things that have are larger scope are more easily accessible.
as a result, they are more likely to be reused in an uncontrolled fashion.
(sounds great) but the problem comes when you want to make changes, and many things use that which you want to change...
it becomes far more difficult to make your changes without breaking something else.
Having said that, there is also a distinction between data (variables, constants, class fields, record attributes) and routines (functions, procedures, methods on classes). You'll want to apply the guidelines far more strictly to data because 'strange use' of data could interfere with some of your routines in highly unexpected and hard to debug ways.
Another thing to bear in mind is the special distinction between global variables and class fields or record attributes:
using global variables there is only one 'value' (term used loosely) for the entire application.
using class fields or record attributes, each new instance of the class or record has its own values independent of other instances.
This does seem to imply that you could use some form of global whenever your application only needs one thing. However, as alluded to earlier: this is not the only reason to avoid globals.
Personally I even tend to avoid global routines.
I'm frequently discovering that things that seemed okay declared global are not as universal as first thought. (E.g. Delphi VCL declares a global Screen object, I work on 2 screens; and many of our clients use 4 to 6.)
I also find it useful to associate routines that could have been global with specific classes as class methods. It generally makes it easier to understand the code.
So listing these 'locations' from largest scope to smallest, you would generally strive to pick locations lower down in the list (especially for data).
interface global
implementation global
interface threadvar
implementation threadvar
published (Note I don't really consider this to be a scope identifier; it's really public scope; "and includes RTTI information" - theoretically, it would be useful to also mark some private attributes as "include RTTI".)
public
protected
private
strict private
local variable
I must confess: what I have presented here is most certainly an over-simplification. It is one thing to know the goals, another to actually implement them. There is a balancing act between encapsulation (hiding things away) and exposing controlled interfaces to achieve high levels of re-usability.
The techniques to successfully balance these needs fall into a category of far more complicated (and sometimes even contentious) questions on system design. A poor design is likely to induce one to expose 'too much' with 'too large' a scope, and (perhaps paradoxically) also reduce re-usability.

Resources