Avatar billede vint Nybegynder
23. januar 2007 - 22:50 Der er 8 kommentarer og
1 løsning

Typer, klasser og komponenter

Er der ikke et potentielt behov for en artikel, der belyser forskellen på typer, klasser og komponenter på en brugervenlig måde?

Jeg vil gerne tilbyde en god slat point for at få det på plads en gang for alle.
Avatar billede hrc Mester
24. januar 2007 - 00:21 #1
En komponent er en visuel klasse der kan placeres på formen. Den nedarver fra TComponent m.fl. der laver de basale rutiner nødvendige for en komponent (Left, Top, Width, Height, Margin, Constraints, Align og lignende). Ellers er der ikke ret meget at sige om dem - medmindre man vil lave dem selv; men det kræver mange flere points...

Med "type" antages ADT (Abstract Data Type), dvs. en klassedefinition. En record definition er ikke særlig relevant længere, medmindre man sender datapakker med faste størrelser (men der bør man bruge TStream i stedet).

Forskellen beskrives kort her:

type
  TMyRecord = record
    CPR : string[30];
    Navn : string[30];
  end;

bør i stedet skrives således:

  TMyClass = class(TObject)
  private
    fCPR : string;
    fNavn : string;
  public
    constructor Create(const aCPR, aNavn : string);
    property CPR : string read fCPR;
    property Navn : string read fNavn;
    function ToString : string;
  end;

Her er den største fordel, at strengene er dynamiske. Det betyder også at selve ramforbruget sandsynligvis er mindre, idet recorden altid fylder 62  bytes mens en instans af TMyClass i mindste fald fylder 12 bytes, idet fCPR og fNavn i realiteten er pointere til de dynamiske strenge. De sidste 4 bytes forklares nedenfor:

En klassetype er en definition der indeholder datadefinitioner og algoritmer mens en objektinstans kun indeholder data. Når man opretter en instans bliver der kun allokeret ram for data-delen, mens alle algoritmerne ligger i klassen og derfor genbruges. Eksempelvis ligger ToString-funktionen i TMyClass mens instansen bærer rundt på fCPR og fNavn, samt en refererence til TMyClass. Til at oprette en ojektinstans af en klasse bruges en constructor mens den frigives vha. en destructor:

En klassedefinition er opbygget med følgende "synlighedsoperatorer:

type
  TMyObject = class(TObject) // TMyObject nedarver fra TObject
  private
  protected
  public
  published
  end;

Disse "synlighedsoperatorer" kan optræde 0 eller flere gange:
  - Private: variable og procedurer er synlige i klassen og i hele den unit hvor klassen findes. Ikke synlig ved nedarvinger.
  - Protected: variable og procedurer er synlige i klassen og i den unit hvor klassen findes. Synlig ved nedarvinger.
  - Public: synlig for alle. Her bør properties laves til de skjulte variable man har i Private eller Protected sektionerne.
  - Published: bruges i komponerter til at bestemme om de skal vises i Object Inspectoreren. Bruges ikke til andet.

NB: Det er noget rod at skjulte variable er synlige i den unit hvor de er defineret. Har du to klasser TMyClass1 og TMyClass2 som ligger i samme fil, så kan en instans af TMyClass2 se de skjulte variable i en instans af TMyClass1. I Delphi 2006 er det muligt at stoppe vha. "strict" som sættes foran "private" og "protected"

I VCL-udgaven (Win32)  skal oprettede objekter frigives igen. I .NET versionen er der en “garbage collecter” der rydder op efter en. Her beskrives VCL-versionen:

procedure TForm1.Test1;
var
  StringList : TStringList;
begin
  StringList := TStringList.Create; // Opret en instans af TStringList
  try // Altid husk at pakke det ind i en Try-finally når muligt
    StringList.Add('Hello World');
    ...
  finally
    StringList.Free; // Frigiv instansen igen
  end;
end;

Her er det vigtigt at mærke sig, at selvom man forlader scope på proceduren, bliver hukommelsen form StringList ikke frigivet. Nedenstående er et "memory leak" af de mere simple.

procedure TForm1.Test2;
begin
  StringList := TStringList.Create;
  StringList.Add('test');
  ...
end;

Det var lidt om klasser og objekter. Spørg videre og der bliver fyldt på.
Avatar billede vint Nybegynder
24. januar 2007 - 10:38 #2
Hej.

Jeg belønner dig lige med nogle points. Når jeg får læst dit svar 2. (evt. 3. gang) så kan det være, at jeg har et uddybende spørgsmål.

Men tak for et godt indlæg
Avatar billede tolderlund Nybegynder
25. januar 2007 - 08:48 #3
Du skriver "En komponent er en visuel klasse der kan placeres på formen.".
Det er kun delvist rigtigt.
En komponent kan sagtens være ikke-visuel, det er der mange komponenter der er.
Eksempelvis TTable, TQuery osv.
Og man kan placere dem på formen er også en sandhed med modifikationer. Man behøver ikke at placere dem på en form for at bruge komponenter. De kan placeres på et datamodul eller dannes udelukkende i koden.
Ellers er det en fin beskrivelse.
Avatar billede hrc Mester
25. januar 2007 - 09:39 #4
Jeg kunne have skrevet at komponenter kan man placere på komponenter der tillader det, eksempelvis kan man placere knapper på en TFrame, TForm, TToolBar eller TPanel (eller nedarvinger af do) og at komponenter kan være synlige i designøjeblikket, men ikke på kørselstidspunktet.

Jeg generaliserede for ikke at mudre for meget rundt i det.

Desuden, TQuery og lignende er synlige komponenter i designøjeblikket. At de er usynlige når programmet køres ændrer ikke meget ved deres status. De har jo Left, Top og lignende komponent-properties.
Avatar billede vint Nybegynder
25. januar 2007 - 11:16 #5
Er det rigtigt forstået, at

- typer er en form for "værdi-definitioner" (definering af hvilkens type en værdi er)
- Klasser kan indeholde typer samt funktioner og prrocedurer og er en samling af disse
- komponenter er klasser, som kan puttes på en form (visuelt eller nonvisuelt) og har events oveni de øvrige ting.
Avatar billede tolderlund Nybegynder
25. januar 2007 - 13:50 #6
hrc, jeg er nødt til at sige at du tager fejl.
Definitionen på visuelle og ikke-visuelle komponenter er om de er synlige runtime.
Avatar billede hrc Mester
25. januar 2007 - 15:03 #7
Er det ikke din definition? :-) Jeg kigger på hvordan de er nedarvet - og de kommer alle fra TComponent.


- Tjo, Hvis man ikke holder sig til objektorienteret ADT er typer definitionen på noget som du eller Delphi har defineret. Det kan være integer, char, TFormatSettings (en record) eller klasser - altså noget som ikke findes endnu. Først når du tildeler en variabel typen bliver der allokeret ram (i : integer); den store undtagelse er objekter hvor du jo skal oprette først (påstand: en Pointer er ingen type).


- Klasser er en logisk indkapsling af data (attributter) og algoritmer til behandling af do (metoder).
En klasses algoritmer bør ikke kende til den ydre verden, men kun vide hvad objektet indeholder eller får fortalt vha properties, procedurer eller constructoren (og messages, senere...). Hvis ekstern data skal føres over kan man enten lave en metode "SetData(...)" eller smide det med i constructoren eller lave Read/Write properties.

I TMyClass ovenfor er der 2 private attributter der tilgås' vha. en read-only property. Data til de to attributter sendes via constructoren. Det at de er read-only sikrer at andre ikke kan ændre værdierne - og det sikrer at man ikke retter i eksempelvis CPR-nummeret. Man kunne argumentere at navn skulle kunne rettes:

  property Navn : string read fNavn write SetNavn;

procedure TMyClass.SetNavn(const aValue);
begin
  fNavn := trim(aValue); // Trimmer mellemrum inden den sættes.
end;

constructoren TMyClass.Create(const aCPR, aNavn : string);
begin
  inherited Create;
  fCPR := aCPR; // Evt validering her.
  SetNavn(aNavn);
end;

En funktion kaldes når fNavn skal sættes. Kunne også gå direkte, men så ver der ingen kontrol over navnet:

  property Navn : string read fNavn write fNavn;

Et program der bruger klassen ved ikke hvordan data er arrangeret og hvad der sker i den - det rager heller ikke den - bare klassen gør det den skal.

I Objektorientering kan man nedarve hvorved man nedarver objektets data og algoritmer - og så bygger man bare på:

  TDanskStatsborger = class(TObject)
  private // Synlig hvis samme unit
    fCPR : string;
  protected // Synlig for klasser der nedarver
    fNavn : string;
    procedure SetNavn(const aValue : string);
  public
    constructor Create(const aCPR, aNavn : string);
    property CPR : string read fCPR;
    property Navn : string read fNavn write SetNavn;
    function ToString : string; // Returnerer "CPR: Navn"
  end;

  TDanskBorger = class(TDanskStatsborger)
  protected
    fAdresse : string;
    fPostNr : string;
    fPostBy : string;
  public
    constructor Create(aCPR, aNavn, aAdresse, aPostNr, aPostBy : string);
    property Adresse : string read fAdresse;
    property PostNr : string read fPostnr;
    property PostBy : string read fPostBy;
    procedure SetData(const aNavn, aAdresse, aPostNr, aPostBy : string);
  end;

...

constructor TDanskBorger.Create(aCPR, aNavn, aAdresse, aPostNr, aPostBy : string);
begin
  inherited Create(aCPR, aNavn); // kalder tidligere constructor
  SetData(aAdresse, aPostNr, aPostBy);
end;

procedure TDanskBorger.SetData(const aAdresse, aPostNr, aPostBy : string);
begin
  fAdresse := trim(aAdresse);
  fPostNr := trim(aPostNr); // Validering her..
  fPostNr := trim(aPostBy); // Ditto
end;

Da synligheden er protected for fNavn kan TDanskBorger-klassen rette den direkte, mens fCPR er skjult da den er i Private-delen (det passer ikke hvis de to klasser ligger i samme fil, men hvis man har D2006 kan man tilføje "strict"):

  TDanskStatsborger = class(TObject)
  strict private // Absolut skjult
    fCPR : string;
  strict protected // Synlige for dem der nedarver
    fNavn : string;
    procedure SetNavn(const aValue : string);
  public
    constructor Create(const aCPR, aNavn : string);
    property CPR : string read fCPR;
    property Navn : string read fNavn write SetNavn;
    function ToString : string; // Returnerer "CPR: Navn"
  end;


- Komponenter (visuelle og nonvisuelle) kan det du nævner, ja, men det er ikke kun komponenter der har events - du kan lave dine egne.


Bemærk i øvrigt at alle klasser (alle der nearver fra TObject, der er mother of all classes (der er også en TClas, men den glemmer vi i første omgang)) har en "self" variabel der er objektinstansen selv.

I en instans af TDanskStatsborger kan man skrive: self.fNavn := 'Tormud Ganebjørn'

Self er meget nyttig.


Bemærk også at alle de protected og private attributnavne er foranstillet et ”f”. Det er en defacto standard at bruge F’et så man altid kan genkende den i koden. Jeg bruger et lille f hvorved jeg afviger fra Borland. Det genererer mine øjne mindre.

Bemærk ligeledes at parametrene i mine metoder er foranstillet et ”a”. Igen fraviger jeg standarden ved at bruge et lille bogstav.

Ved globale kunne jeg bruge ”g”, men da jeg aldrig laver globale variable (glorien lyser klart lige nu), så er det ikke så aktuelt.

I øvrigt: Hvis der er interesse for det kan jeg godt skrive lidt om Is og As. Senere noget om Try-finally og try-except – men altså kun hvis der er interesse.
Avatar billede tolderlund Nybegynder
25. januar 2007 - 15:22 #8
Det er ikke min definition, jeg holder mig blot til hvad alle andre bruger.
Eks.
http://delphi.about.com/od/vclusing/l/aa020999.htm
"Some components are visual components; others are nonvisual components. A visual component, is one that can be seen by the user at run time. Visual components include components such as edit controls, buttons, list boxes, labels, and so on. A nonvisual component is one that cannot be seen by the user at run time (system timers, database components, image lists, etc.)"

Det er i øvrigt et glimrende sted at lære om Delphi:
http://delphi.about.com/
Avatar billede arne_v Ekspert
27. januar 2007 - 04:49 #9
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester