0
Tags:
delphi
Skrevet af
Bruger #58
@ 16.06.2001
OOP - hvordan og hvorfor, del 2
I denne opfølger til "OOP - hvordan og hvorfor" skal vi se på avancerede objektrelationer og samspil mellem flere objekter. Jeg vil også dække hvordan du kan lave din egen event. Jeg forventer at du har læst første del af denne artikel, da jeg vil bygge videre på eksemplet fra den.
TLandkort og hvad dertil hører
Som beskrevet i første del af artiklen skal TLastvogn have en metode der hedder KørTil. KørTil skal have en parameter: Destination. Destination kan være af typen TDestination, fordi der er flere slags destinationer med forskellige egenskaber. Her er TDestination:
type
TDestination = class(TCollectionItem)
private
FVejVæk: TRuter;
public
property VejeVæk: TRuter
read FVejVæk
write FVejVæk;
constructor Create(Collection: TCollection); override;
destructor Destroy; override;
end;
Som man kan se, så nedarver TDestination fra TCollectionItem. Et landkort kan man opfatte som en samling destinationer, derfor er deklarationen af TLandkort sådan her:
type
TLandkort = class(TCollection);
TDestination har også fået nye constructor og destructor metoder. De skal se sådan ud:
constructor TDestination.Create(Collection: TCollection);
begin
inherited;
FVejeVæk := TRuter.Create(TRute);
end;
destructor TDestination.Destroy;
begin
FVejeVæk.Free;
inherited;
end;
Nu er der TRute og TRuter tilbage. De skal se sådan ud:
type
TRuter = class(TCollection);
TRute = class(TCollectionItem)
private
FDestinationer: array[1..2] of TDestination;
function GetLængde: extended;
public
property Destinaitoner[Index: integer]: TDestination
read FDestinationer
write FDestinationer;
property Længde: extended
read GetLængde;
end;
Læg mærke til at Længde er read-only og bruger en funktion til at hente sin værdi. Dvs. at vi må have data om hvor hver destination ligger. Et nyt property til TDestination: (Her har jeg ikke taget de gamle dele med.)
type
TDestination = class(TCollectionItem)
private
FPosition: TPoint;
public
property Position: TPoint
read FPosition
write FPosition;
end;
Nu kan vi skrive en GetLængde metode til TRute. Den udregner længden mellem de to destinationer i fugleflugtslinie:
function TRute.GetLængde: extended;
var
P1, P2: TPoint;
begin
P1 := Destinationer[1].Position;
P2 := Destinationer[2].Position;
Result := Sqrt(Sqr(Abs(P1.x - P2.x)) + Sqr(Abs(P1.y - P2.y)));
end;
Nu skulle det være muligt at få TLastvogn til at køre.
KørTil
For at gøre det simpelt: KørTil tager en destination og ser om den kan komme dertil fra hvor den er nu. Den returnerer hvor lang tid det tager at køre ruten i minutter. Der er en chance for at der sker et trafikuheld hver gang der køres. Man kunne eventuelt kode metoden sådan, at chancen for trafikuheld er større når det er sent på aftenen, men det er der ingen grund til at komme ind på her. Her er metoden med et par kommentarer i:
function TLastvogn.KørTil(ADestination: TDestination): cardinal;
var
x: integer;
Rute: TRute;
begin
// For at tilfredsstille compileren
Result := 0;
// Først, tjek om vi allerede er der hvor vi skal hen
// Position er et nyt property i TLastvogn. Det indeholder lastvognens
// aktuelle position
if ADestination = Position then
begin
// Det taget nul minutter at komme fra her til samme sted
Result := 0;
Exit;
end;
// Se om vi kan komme fra aktuelle position til destinaiton
// Gøres ved at loope gennem alle ruter fra aktuelle position
for x := 0 to Position.VejeVæk.Count-1 do
begin
Rute := TRute(Position.VejeVæk.Items[x]);
if (ADestination = Rute.Destination[1]) or
(ADestination = Rute.Destination[2]) then
Break;
end;
// Ekstra sikkerhedstjek
if ((ADestination = Rute.Destination[1]) and
(Position = Rute.Destination[2])) or
((Position = Rute.Destination[1]) and
(ADestination = Rute.Destination[2])) then
begin
// Vi kører med konstant fart: 80 km/t
// Forudsætter at alle mål er i km
// Udregn den tid det tager at køre i minutter
Result := Round(Rute.Længde * 60 / 80);
// Chancen for trafikuheld er 2%
if Random <= 0.02 then
if Assigned(FOnTrafikuheld) then
begin
FOnTrafikuheld(Self, Rute);
end
else
// Vi når kun vores nye destination hvis vi ikke kommer ud for et uheld
FPosition = ADestination;
end;
end;
Den ny TLastvogn og anden abstrakt snak
Der kom lige 2 nye ting til TLastvogn klassen. Et property, Position, og en event, OnTrafikuheld. Position er bare af typen TDestination. OnTrafikuheld er lidt mere tricky at lave.
type
// En event skal altid deklareres som en type
TTrafikheldEvent = procedure(Sender: TObject; ARute: TRute) of object;
TTransportmiddel = class(TObject);
public
procedure BevægTil(ADestination: TDestination); virtual; abstract;
end;
TLastvogn = class(TTransportmiddel)
private
FAntalKasser: cardinal;
FKasseVægt: cardinal;
FPosition: TDestination;
FOnTrafikuheld: TTrafikuheldEvent;
function GetVægt: cardinal;
public
property AntalKasser: cardinal
read FAntalKasser
write FAntalKasser;
property KasseVægt: cardinal
read FKasseVægt
write FKasseVægt;
property Vægt: cardinal
read GetVægt;
property Position: TDestination
read FPosition;
property OnTrafikuheld: TTrafikuheldEvent
read FOnTrafikuheld
write FOnTrafikuheld;
procedure BevægTil(ADestination: TDestination); override;
end;
Her er det vigtigt at lægge mærke til TTransportmiddel klassen og den metode den indeholder. Kan du se det lille ord "abstract" til sidst på linien? Det betyder at vi ikke behøver at implementere metoden BevægTil for TTransportmiddel klassen. Faktisk må vi ikke. Men det betyder at alle transportmidler kan behandles ens. (Alle kan bedes om at bevæge sig til et andet sted, uden at man er klar over hvilken type der er tale om.) Jeg har også omdøbt KørTil af logiske grunde. Det er sjældent at man ser en jumbojet køre fra by til by.
Sidste destination
TDestination er en meget god klasse, men den kan godt forbedres. Hvad men nogen mere specifikke destinationer? Som TStorby og TVarelager? Intet problem, bare lav en ny klasse:
type
TStorby = class(TDestination)
private
FIndbyggere: cardinal;
public
property Indbyggere: cardinal
read FIndbyggere
write FIndbyggere;
end;
TVarelager = class(TDestination)
private
FArbejdere: cardinal;
FKasser: cardinal;
public
property Arbejdere: cardinal
read FArbejdere
write FArbejdere;
property Kasser: cardinal
read FKasser
write FKasser;
end;
Man kunne også give TStorby en metode, ModtagVarer, hvor der bliver losset så mange kasser fra en TLastvogn som byen nu kan bruge, udregnet fra indbyggertal. Og sådan kunne man fortsætte. Disse klasser kunne måske bruges til et spil ala Transport Tycoon, eller måske et system til en transport-virksomhed.
Det var så slut for denne gang. Hvis der er noget du ikke lige forstår, så kan du slå op i hjælpen, og hvis det stadig ikke hjælper, så er der sikkert en anden der vil hjælpe dig. Spørg i Delphi forummet.
Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.
Del også gerne artiklen med dine Facebook venner:
Kommentarer (0)
Du skal være
logget ind for at skrive en kommentar.