I have my main form. Form_Main
It creates two instances of two classes.
Candle_Data : TCandle_Data;
Indicator_2700 : TIndicator_2700;
In order for Indicator_2700 to properly compute its values it must have access to the candle data in the obect Candle_Data from inside one of its methods. Thus how can Indicator_2700 access data inside Candle_Data? Does Form_Main have to pass it as a argument at Constructor time?
Both Class declarations are in their own unit file.
You could use any of the following (non-exhaustive) methods:
Pass the object reference as a parameter to any methods that need it. Of course you need to get hold of Candle_Data so the suitability of this approach really depends who the caller is.
Pass the Candle_Data object reference to the constructor of the other object and then store it in a private member field.
Make the object reference a public property of the single instance of the main form and access it that way.
We don't really have enough information to advise you which is best but the starting point is always to prefer parameters and local variables over global state.
TIndicator_2700 could have a property to link it to the instance of TCandle_Data that is relevant to its own instance or you should supply it as an argument to the method that needs to access the data.
You could certainly pass the TCandle_Data instance into the constructor of Indicator_2700, and store a reference within the resulting instance until you needed it.
Both class declarations are in their own unit file.
That suggests that both have nothing to do with the other. But still you want one to have knowledge about the other. It sounds like a little design mixup, but that doesn't need to be the case.
There are multiple solutions, here are three of them, each with its own purpose:
Place both classes in the same unit, only if both classes have a common theme/subject (e.g. TCar and TAirplane in the unit Transport),
Use one unit in the other unit, only if both units represent different subjects, but one may depend on the other (e.g. unit Transport uses unit Fuel: TCar needs TDiesel, but TDiesel doesn't need a TCar). This only works one-way. Delphi prevents using in both ways with a compiler error: "Circular unit reference to 'Fuel'". The only workaround is to use the second unit in the implementation section, but that usually is considered a big nono.
Declare a new base-class in a new unit, only if the base-class has a common subject, but the final descendants do not (e.g. TFuel is used by all transportation classes like TCar, TAirplane and THorse, but TFood (a descendant of TFuel) is only used by THorse and TPerson).
As for how to link both classes together, see the already given answers.
Related
We have an app that makes fairly extensive use of TIniFile. In the past we created our own descendant class, let's call it TMyIniFile, that overrides WriteString. We create one instance of this that the entire app uses. That instance is passed all around through properties and parameters, but the type of all of these is still TIniFile, since that is what it was originally. This seems to work, calling our overridden method through polymorphism, even though all the variable types are still TIniFile. This seems to be proper since we descend from TIniFile.
Now we are making some changes where we want to switch TMyIniFile to descend from TMemIniFile instead of TIniFile. Those are both descendants of TCustomIniFile. We'll also probably be overriding some more methods. I'm inclined to leave all the declarations as TIniFile even though technically our class is no longer a descendant of it, just to avoid having to change a lot of source files if I don't need to.
In every tutorial example of polymorphism, the variable is declared as the base class, and an instance is created of the descendant class and assigned to the variable of the base class. So I assume this is the "right" way to do it. What I'm looking at doing now will end up having the variables declared as, what I guess you'd call a "sibling" class, so this "seems wrong". Is this a bad thing to do? Am I asking for trouble, or does polymorphism actually allow for this sort of thing?
TIniFile and TMemIniFile are distinct classes that do not derive from each other, so you simply cannot create a TMemIniFile object and assign it to a TIniFile variable, and vice versa. The compiler won't let you do that. And using a type-cast to force it will be dangerous.
You will just have to update the rest of your code to change all of the TIniFile declarations to TCustomIniFile instead, which is the common ancestor for both classes. That is the "correct" thing to do.
The compiler is your friend - why would you lie to it by using the wrong type ... and if you do lie to it why would you expect it to know what you want it to do?
You should use a base class that you derive from, like TCustomIniFile. I would expect compile issues if you are trying to make assignments which are known at compile time to be wrong.
The different classes have different signatures so the compiler needs to know which class it is using to call the correct method or access the correct property. With virtual methods the different classes setup their own implementation of those methods so that the correct one is called - so using a pointer to a base type when you call the virtual method it calls that method in the derived type because it is in the class vtable.
So if the code does compile, it's very likely that the compiler will not be doing the right thing ...
i added map(), reduce() and where(qlint : string) to a Spring4D fork of mine.
While i was programming these functions, i found out that there is a differnce in the behaviour of the lists, when they are created in different ways.
If i create them with TList<TSomeClass>.create the objects in the enumerables are of the type TSomeClass.
If i create them with TCollections.CreateList<TSomeClass> the objects in the enumerables are of the type TObject.
So the question is:
Is there a downside by using TList<TSomeClass>.create ?
Or in other words: Why should i use TCollections.CreateList<TSomeClass> ?
btw: with TCollections.CreateList i got a TObjectList and not a TList. So it should be called TCollections.CreateObjectList... but that's another story.
Depending on the compiler version many of the Spring.Collections.TCollections.Create methods are applying what the compiler is unable to: folding the implementation into only a very slim generic class. Some methods are doing that from XE on, some only since XE7 ( GetTypeKind intrinsic function makes it possible to do the type resolution at compile time - see the parameterless TCollections.CreateList<T> for example).
This greatly reduces the binary size if you are creating many different types of IList<T> (where T are classes or interfaces) because it folds them into TFolded(Object|Interface)List<T>. However via the interface you are accessing the items as what you specified them and also the ElementType property returns the correct type and not only TObject or IInterface. On Berlin it adds less than 1K for every different object list while it would add around 80K if the folding is not applied due to all the internal classes involved for the different operations you can call on an IList<T>.
As for TCollections.CreateList<T> returning an IList<T> that is backed by a TFoldedObjectList<T> when T is a class that is completely as designed. Since the OwnsObject was passed as False it has the exact same behavior as a TList<T>.
The Spring4D collections are interface based so it does not matter what class is behind an interface as long as it behaves accordingly to the contract of the interface.
Make sure that you only carry the lists around as IList<T> and not TList<T> - you can create them both ways (with the benefits I mentioned before when using the TCollections methods). In our own application some places are still using the constructor of the classes while many other places are using the static methods from Spring.Collections.TCollections.
BTW:
I saw the activity in your fork and imo there is no need to implement Map/Reduce because that is already there. Since the Spring4D collections are modelled after .NET they are called Select and Aggregate (see Spring.Collections.TEnumerable). They are not available on IEnumerable<T> directly though because interfaces must not have generic parameterized methods.
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.
As there is no garbage collection in Delphi, where exactly do you unload variables?
Say I have a type with a set of private vars.
Would it suffice to have a Destroy method that does this work?
Do I need to explicitly call this destroy method in my consuming classes?
The best way to organize destruction in delphi is to always think about "who will create the given vars".
If you also free them in this context (For you private vars the destroy method of the class) it's much less likely that you will encounter memory leaks.
Actually the destructor of a class is normally not called via
myInstance.Destroy();
instead the typical way of doing it is via
FreeAndNil(myInstance);
or
myInstance.Free();
As delphi will handle calling the destructor method at order
Objects need to be destroyed when you are ready with them: using .Free (which calls the destructor .Destroy) or FreeAndNil.
If you are using interfaces: they are reference counted and hence they are freed for you. The same for strings.
Most functions that you need to pass an object do not take ownership of the object so you should free it afterwards when you are done with them. If you create components on a form, please note that they are objects, are owned by the form and you should let the form free them for you.
For example: in your TLight you create a TTimer probably without an owner. In this case you should Free the timer in the destructor of the TLight class. If your TLight was a TControl or TComponent itself you could have created the Timer with Self as the Owner and it was automatically freed when your TLight instance was freed.
That's how I see it:
Primitive types (like integer, real, double, extended), arrays, sets, records: they are automatically destroyed when out of scope.
Reference-counted types (strings, interfaces types, interface references): they are automatically destroyed when their reference counter is equal to zero.
Objects from classes that fall in one of those situations: does not descend from TComponent, are in list objects that do not implement ownership or are a TComponent descendant but does not have an owner assigned to it:
When their class descend from TInterfacesObject and are accessed from a interface reference: same behavior from reference-counter types (see above)
Other cases: call Object.Free or FreeAndNil(Object) to manually destroy it (in a try..finally structure would be even better)
Objects that have an owner (TComponent descendants or are in list objects that implement ownership): the owner will take of them in proper time.
I noticed the following comment from OP:
I have noticed the concept of owner.
Yet I couldn't pass my custom class to
the TTimer.Create() method as it is
expecting a TComponent. Why do they
restrict things to be used in forms
only? Doesn't that furhter petrify the
bad habit of putting logic in your
views (forms)?
See, a TComponent descendant can be registered on the pallete to have the ability to be used in design-time (for example, in a form) . But it's doesn't means it must!!
You can pass your custom-class to TTimer.Create() if it descends from TComponent but you don't need to instantiate it in design-time - or even be owned by a TForm descendant (if you want to manage it's lifetime, you don't even need an owner!).
Ownership is a mechanism it's to help people to save time....
You keep referring to a "type with private vars" and asking about "unloading variables." I'm going to go out on a limb (as I may be have misinterpreted your message), and presume you're talking about class variables and are asking about when these class variables should be finalized when the "class unloads." (You've been doing Java or .NET programming at some point, no ;-).
The key to what you're asking is a unit finalization section. The overall layout of a unit includes 4 overall sections, two of which are optional, initialization and finalization.
unit Foo;
interface
// put things here you want other units to see
implementation
// put the *implementation* of the things from the interface here along with things
// you only want this unit to see.
initialization
// <optional> put code here that you want to execute when this unit is "loaded"
finalization
// <optional> put code here that you want to execute when this unit is "unloaded"
end.
If you're talking about instance variables of a class type, then some of the other answers you've already gotten (and are bound to get) should shed some light on that.
To avoid singletons and global variables I'd like to be able to pass parameters to a TFrame component. However since a TFrame normally is included on form at design time it is only possible to use the default constructor.
The parent form can of course set some properties in the OnCreate callback after the TFrame has been created. However this does not ensure that a property is not forgotten, and the dependencies are not as clear as using a constructor.
A nice way would be if it was possible to register a factory for creating components while the dfm file is being read. Then the required parameters could be passed to the TFrame constructor when created by the factory. Is there a way of accomplishing this?
Or does anyone have a better solution on how to pass parameters to a TFrame?
All components, including descendants of TFrame, need to be able to be constructed using the constructor inherited from TComponent. Otherwise, they can't be used properly at design time. If the restriction of design-time use is acceptable to you, then you could override that constructor and raise an exception. That would prevent the component from being placed on a form at design time. Simply provide some other constructor that requires other parameters.
Because of the design-time requirement, all components need to be able to exist with some or all of their properties still at their default values. That doesn't mean the components have to do useful things while they're in that state, but they do need to be able to stay in that state indefinitely. It should be OK, for example, to place a component on a form, save the form, and close Delphi, with the intention of resuming the form-designing at a later time. The component should allow itself to be saved and restored, even if all its properties haven't been set up for final use yet.
My preferred option is to enforce the component's rules only at run time. Check that all the properties are set to sensible values before you allow them to be used. You can use assertions to enforce the proper use of your components. Consumers of your classes will learn very quickly if they haven't finished setting up your components on their forms.
I would normally add a public, non-virtual "Initialise" or (Initialize to Americans) procedure which requires all parameters to be provided. This will then set the properties.
Make the properties protected or private if possible, so the only way they can be set is from calling Initialise(AFoo, ABar : integer).
Then in TFormXXX.FormCreate or TformXXX.Create, have:
inherited;
Initialise(foo, bar);
could you create/registercomponent your own tFrame component and
place that on the form - it's create could have anything passed to it.
If a factory could provide the parameters that you need, why don't you just override the default constructor for your frame, and ask the factory-class for parameters?
I usually make my own constructor. I don't like to create frames at designtime anyway.
a) a frame can be created dynamically when required and destroyed when not needed
b) give the frame a public property with either the parameter data type or a data structure and pass the values to the form through the property.
Example:
TAddress - a class to hold the usual elements of an address.
TAddressFra - a frame with the visual controls to display the address
populate an instance of TAddress with values
create an instance of TAddressFra
assign the TAddressFra.address property with the TAddress instance
use the procedure setAddress(o_address : TAddress) to assign the values of the TAddress attributes to the corresponding visual components on the TAddressFra