Zrozumieć klasy - jaka jest idea wykorzystywania obiektowości?

0

Przybierałem się do napisania tego tematu już od dłuższego czasu, ponieważ nie rozumie idei tworzenia i używania klas w aplikacjach, skoro ten sam efekt możemy otrzymać stosując odpowiednie pętle w kodzie danego komponentu. Czy chodziło o uporządkowanie kodu oraz wykorzystanie danej klasy przez innego programistę pracującego w zespole? Ktoś pomoże mi zrozumieć tą ideę?

Do tej pory programuję bez użycia klas ( po za licznymi kalkulatorami na laboratoriach ), dostrzegam że coraz lepiej mi idzie, tworzę pewien projekt, a mianowicie prostą lecz bardzo rozbudowanej gry klikanej z zapisem danych oraz bardzo rozbudowaną gałęzią metod ( wolę tworzyć pracować na czymś co ma jakiś sens i daje fajny efekt niż na kolejnych kalkulatorach ). Projekt tworzę od około miesiąca. Dzień w dzień poświęcając mu po kilka godzin czasu, a spoglądając na kod z początku swojej pracy widzę ile w nim jest do poprawy.

Na koniec bym zapomniał, jestem po artykule Adama, a mianowicie: OOP

1

Separacja kodu w pewne czarne skrzynki. Dzięki temu masz potem ładny, wysokopoziomowy kod który operuje na obiektach związanych z dziedziną programu, a nie na gołych zmiennych liczbowych. Dużo łatwiej szuka sie błędów w kodzie jeśli jest on tak podzielony. Spróbuj dla ćwiczenia napisać choćby i tą prostą grę, ale pisząc metodą top-down. To znaczy zaczynasz od bardzo wysokopoziomowego algorytmu i w miarę potrzeby tworzysz klasy i metody, ale nie implementujesz ich póki nie napiszesz wszystkiego na "wyższym poziomie". To znaczy na przykład dla gry karcianej w wojnę to byłoby coś w stylu:

Deck fullDeck = Deck.full;
Player players[X];
giveCards(fullDeck, players);
playGame(players);

Juz widać że potrzebna nam jest klasa do odsługi talii kart oraz gracza i że potrzebne nam są 2 metody. Tworzysz sobie puste klasy i metody i lecisz poziom niżej implementując giveCards() i playGame(). I robisz tak aż nie zejdziesz na sam dół.
Idea jest taka żeby mieć kod na jednym poziomie abstrakcji. To znaczy: w kodzie powyżej nie widać w ogóle w jaki sposób Deck przechowuje karty. I dobrze, bo na tym poziomie to jest zupełnie nie istotne. Jak będziesz potem szukał błędu w algorytmie to szuka sie go łatwiej w takim kodzie, niż kiedy masz tam 7 zagnieżdżonych pętli i operacje na jakichś integerach czy stringach. Jak zobaczysz w kodzie ''deck.shuffle'()' to od razu będziesz wiedział to się tam dzieje, a jak zobaczysz dwie zagnieżdżone pętle które przemieszczają elementy jakiejś tablicy to niestety ale chwilę zajmie ci zrozumienie co to w ogóle jest. Oczywiście te dwie pętlę i tak w kodzie będą, tylko że jak wchodzisz do metody shuffle() to wiesz czego sie spodziewać.

1

Klasy to esencja programowania obiektowego. Wiadomo da się program konsolowy czy pod WinAPI napisać bez klas. Jednak bardzo się przydają. Pod WinAPI są komunikaty wysyłane do kontrolek takich jak ListBoxy, ListViewy czy ComboBoxy itp, gdzie można dodać poza tekstem obiekty. Obiekt taki może przechować nam więcej danych niż tablica czy typ rekordowy. Może mieć również metody, czego te typy nie mogą mieć.

A i niemal całe VCL jest oparate o klasy, jak na przykład TWinControl. Także sens używania klas jest duży. Gdyby brak klas i obiektów to ja sobie nie wyobrażam powstania VCL. Wszyscy musieli by lubieć jak ja, rzeźbić - kiedy się w miarę łatwo da coś zrobić - pod czystym WinAPI :) Zaletą klas jest choćby dziedziczenie i posiadanie metod.

Nie wiem czy bez klas stworzono by na przykład TRegExpr. Trzeba było by mozolnie rzeźbić funkcje do ładowania tekstu i ładowania strumienia. Pewne parsowania i występowanie Matches. Także imo teraz bez Obiektowości ani rusz. Pewnie więcej osób tutaj Tobie uświadomi to, że nie ma co tam tak dziwnie rezumować, że są jakieś "pętle" i tyle. Używać klas, robić to z głową, jak najwięcej samodzielnie kodząć. I kropka :)

0

@Shalom - czyli o ile dobrze rozumiem na przykładzie następującego kodu ( pomijając już nazewnictwo, to właśnie początek projektu o którym wspomniałem ):

procedure TForm2.Timer1Timer(Sender: TObject);
begin


repeat
uu:=1;
 repeat
  ProgressBar1.Position:=ProgressBar1.Position+3;
   if CheckBox1.Checked then
    begin
     ProgressBar1.Position:=ProgressBar1.Position+4;
     Button3.Enabled:=False;
     CheckBox5.Enabled:=False;
     CheckBox5.Checked:=False;
   end;
   if CheckBox2.Checked then
      ProgressBar1.Position:=ProgressBar1.Position+5;
   if CheckBox3.Checked then
   ProgressBar1.Position:=ProgressBar1.Position+6;

   if ProgressBar1.Position > 97 then
     ProgressBar1.Position := 0;
     if ProgressBar1.Position = 0 then

     repeat
      poziom:=StrToInt(Edit1.Text)+1;
      i:=1;
      Edit1.Text:=IntToStr(n);
     until i=1;

   if CheckBox1.Checked then
    repeat
     Pieniadz:=StrToInt(Edit2.Text)+1;
     h:=1;
     Edit2.Text:=IntToStr(Pieniadz);
    until h=1;


     if (Poziom > 9) and (CheckBox1.Checked=False) then
      begin
       CheckBox5.Enabled:=True;
      end;
     if Sila<1 then
      Button4.Enabled:=True;

until Poziom>0 ;
until uu=1;



end;

Powinienem umieścić klasy Poziom, Pieniadz etc?

3

Odpowiedz sam sobie: czy osoba postronna wiedziałaby w ogóle że to jest kawałek kodu z gry? Bardzo wątpliwe ;]
Zresztą tobie w tym kodzie brakuje znacznie wiecej niż obiektowości ;] Nazywaj te zmienne z głową! Jak będziesz miał kod na 10k linijek to widząc zmienną Button735 osiwiejesz zanim przypomnisz sobie czy to jest button o który ci chodziło czy nie. Programowanie należy sobie ułatwiać a nie utrudniać. Nie wierzę że za tydzień będziesz pamiętał który checkbox to jest 1, który 2 a który 3. Będziesz musiał co chwilę sprawdzać...

0

"Brakuje znacznie więcej" mógłbyś to rozbudować? Ponieważ nazwy na chwilę obecną wprowadzam, ale według mnie to głupota edytować w tej chwili po 20 editów i 20 buttonów i wprowadzać im nazwy. Beginów i endów także dostrzegam że brakuje.

1

No to najwyższy czas wywalić ten projekt do kosza i zacząć od początku, tym razem zachowując porządek.

0

@wojas666 tak jak pisałem wyżej: spróbuj to pisać metodą top-down.
Nie mówię tutaj że pisanie bottom-up jest złe czy choćby gorsze, ale znacznie trudniej zachować wtedy sensowną architekturę, szczególnie dla kogoś początkującego.

0

@wojas666 - jeśli nie wiesz jaka jest idea wykorzystywania programowania obiektowego, to napisz sobie jedną prostą aplikację - jedną wersję wykorzystując VCL i OOP, a drugą wersję w WinAPI; Zobaczysz jak bardzo różnić się będzie kod, jakie ułatwienia daje obiektowość oraz jak dużo możliwości oferuje.

0

okey, mam załóżmy prosty kalkulator a w nim klasy:

unit Unit2;

interface
type
TCzworokat = Class
  private
  AA:Real;
  BB:Real;
  CC:Real;
  DD:Real;
  public
  function obwod:Real;
  constructor create(AAA, BBB, CCC, DDD: Real);
End;

TKwadrat1= class (TCzworokat)
  Function Pole:Real;
  Function Obwod:Real;
end;

implementation
  function TCzworokat.obwod:Real;
  begin
  obwod:=AA+BB+CC+DD;
  end;

  function TKwadrat1.Pole;
  begin
  pole:=AA*AA;
  end;

używam tego w następujący sposób:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Unit2;

var
  Form1: TForm1;
  AA:Real;
  BB:Real;
  CC:Real;
  DD:Real;
  Czworokat1:TCzworokat;
  kwadrat2:TKwadrat1;
  Prostokat1:TProstokat;
implementation

{$R *.dfm}



procedure TForm1.Button1Click(Sender: TObject);
begin
if RadioButton1.Checked and CheckBox2.Checked then begin
 AA:=StrToFloat(Edit1.Text);
  BB:=StrToFloat(B.Text);
   CC:=StrToFloat(C.Text);
    DD:=StrToFloat(D.Text);
     Czworokat1:=TCzworokat.create(AA, BB, CC, DD);
      Edit2.Text:=FloatToStr(Czworokat1.obwod);
 end;
begin
if RadioButton2.Checked and CheckBox1.Checked then begin
   Edit3.Text:=FloatToStr(kwadrat2.Pole);
  end;

  if RadioButton2.Checked and CheckBox2.Checked then begin
  kwadrat2 :=TKwadrat1.Create(AA,BB,CC,DD);
  Edit2.Text:=FloatToStr(kwadrat2.Obwod);
  end;

Okey, to rozumiem, ale czy da się zrobić coś w tym stylu by pobrać z Editów zmienne i wkomponować je w daną metodę klasy i jakoś ją potem wywołać? Chodzi mi o coś w tym stylu:

unit SilnikPostaci;
interface

uses
Postac1;
Type
TPoziom = class
var
AktualnyPoziom:integer;   
KoniecPentli:integer;     i
Function Poziom:Integer;
constructor create ( AktualnyPoziom, PoziomPobierzz:integer );
end;

implementation

Function TPoziom.Poziom;
begin
   repeat
      AktualnyPoziom:=PoziomPobierz+1;
      KoniecPentli:=1;
     until i=1;
end;

Constructor TPoziom.create(AktualnyPoziom: Integer; PoziomPobierzz: Integer);
begin
  PoziomPobierz:=PoziomPobierzz;
end; 

Sorry za te wszystkie pytania, ale to chyba jeden z najważniejszych tematów w programowaniu obiektowym i chcę go dobrze zrozumieć.

0

Jak na razie nie zrozumiałeś nawet Podstaw programowania.
Czytaj następujące zdanie dopóki go nie zrozumiesz:
Nie używać zmiennych globalnych no chyba że cię przypalają rozżarzonym żelazkiem.
Tym bardziej tylko po to aby zastąpić zmienne lokalne - to istne szaleństwo.

2

@wojas666 - po to właśnie są klasy, aby dane dotyczące pojedynczego elementu trzymać w polach klasy, a nie w zmiennych globalnych.

0

I o to właśnie mi chodziło, dzięki :)

0

Zacząłem nowy projekt, a mianowicie katalogowanie muzyki tyle że tym razem ćwiczę operacje na klasach, chciałbym przedstawić wam swój kod i jednocześnie prosić o wytknięcie błędów jakie popełniam. Wszystko działa jak należy, głównie chodzi mi o sam zapis czy operowanie klasami.

unit Silnik;

interface
type
TNastepny = class
  var
  AktualnyNumer:Integer;
  function Kolejny:Integer;
  constructor create ( AktualnyNumerr:Integer );
end;

TAktualna = class
  NowyNumer:Integer;
  function Aktualnaa:Integer;
  constructor create ( NowyNumerr:Integer);
end;

implementation

function TNastepny.Kolejny:integer;
begin
  Kolejny:=AktualnyNumer+1;
end;

constructor TNastepny.create(AktualnyNumerr: Integer);
begin
  AktualnyNumer:=AktualnyNumerr;
end;

function TAktualna.Aktualnaa:Integer;
begin
  Aktualnaa:=NowyNumer;
end;

constructor TAktualna.create( NowyNumerr:Integer);
begin
  NowyNumer:=NowyNumerr;
end;
end.
unit Dodaj;

interface
.
.
.

var
  Form2: TForm2;
  Numer: TIniFile;
  Zapisane: TIniFile;
  znajdz: string;
  NastepnyNumer:TNastepny;
  BiezacyNr:TAktualna;
  AktualnyNumer:Integer;
  NowyNumer:Integer;

implementation

procedure TForm2.FormCreate(Sender: TObject);
begin
Lokalizacjaa.Enabled:=False;
Pozycjaa.Enabled:=False;
 NastepnyNumer:=Nil;
 BiezacyNr:=Nil;
end;

procedure TForm2.WybierzPlikClick(Sender: TObject);
begin
  LokalizacjaZnajdz.Execute;
  Znajdz:=LokalizacjaZnajdz.FileName;
  Lokalizacjaa.Text:=Znajdz;
end;

procedure TForm2.ZapiszClick(Sender: TObject);
 begin
  Numer:=TIniFile.Create(ExtractFilePath(ParamStr(0))+ 'Numer.ini');
   Pozycjaa.Text:=Numer.ReadString('Numer', 'Pozycja', Pozycjaa.Text);
   AktualnyNumer:=StrToInt(Pozycjaa.Text);
   NastepnyNumer:=TNastepny.create(AktualnyNumer);
   Pozycjaa.Text:=IntToStr(NastepnyNumer.Kolejny);
    Numer.WriteString('Numer', 'Pozycja', Pozycjaa.Text);
     Zapisane:=TIniFile.Create(ExtractFilePath(ParamStr(0))+'Zapisane.ini');
      NowyNumer:=StrToInt(Pozycjaa.Text);
      BiezacyNr:=TAktualna.create(NowyNumer);
       Zapisane.WriteString('Zapisane', 'Nazwa'+IntToStr(BiezacyNr.Aktualnaa), Nazwaa.Text);
       Zapisane.WriteString('Zapisane', 'Gatunek'+IntToStr(BiezacyNr.Aktualnaa), Gatunekk.Text);
       Zapisane.WriteString('Zapisane', 'Wykonawca'+IntToStr(BiezacyNr.Aktualnaa), Wykonawcaa.Text);
       Zapisane.WriteString('Zapisane', 'Lokalizacja'+IntToStr(BiezacyNr.Aktualnaa), Lokalizacjaa.Text);
      BiezacyNr.Destroy;
     Zapisane.Free;
    NastepnyNumer.Destroy;
  Numer.Free;
 end;
end.
1

Największymi błędami jest brak stosowania sekcji dostępu w deklaracjach klas; Zapoznaj się z tymi słówkami kluczowymi: Private, Protected, Public i Published;

Po drugie nie stosujesz bloków Try Finally, stąd przy zaistnieniu wyjątku spowodujesz wycieki pamięci, bo obiekty nie zostaną zwolnione z pamięci; Z tym blokiem także się zapoznaj - przydaje się to bardzo, jeśli instancję klasy tworzysz i zwalniasz w tym samym bloku kodu, np. w ciele jednej procedury/funkcji/metody/zdarzenia; Składnia jest prosta, poniżej podaję przykład dla obiekty klasy TIniFile:

var
  iniInput: TIniFile; // nasz obiekt pliku Ini
begin
  // utworzenie w pamięci instancji klasy
  iniInput := TIniFile.Create('filename.ext');
  try
    // operacje na obiekcie
    // łącznie z tymi, które mogą wywalić program
  finally
    // zwolnienie instancji klasy z pamięci w każdym wypadku,
    // nawet gdy operacje na obiekcie spowodują wyjątek
    iniInput.Free();
  end;
end;

Jeśli referencja do instancji klasy przechowywana jest np. w polu klasy, to tworzyć ją można w konstruktorze, a zwalniać w destruktorze klasy; Ważne jest, aby zadbać o zwalnianie wszystkiego co ręcznie tworzymy/alokujemy i zabezpieczyć kod tak, aby zawsze wszystko było ładnie sprzątane; Nawet, jeśli kod wywołuje wyjątki;

Możesz też skorzystać z bloku Try Except, aby zaistniałe wyjątki przechwytywać i odpowiednio obsługiwać; Oba wymienione bloki możesz zagnieżdżać, np. w poniższy sposób:

var
  iniInput: TIniFile; // nasz obiekt pliku Ini
begin
  // utworzenie w pamięci instancji klasy
  iniInput := TIniFile.Create('filename.ext');
  try
    // operacje na obiekcie, które są bezpieczne

    try
      // operacje, które mogą wywalić program
    except
      // wyłapanie i obsługa zaistniałych wyjątków
    end;
  finally
    // zwolnienie instancji klasy z pamięci w każdym wypadku,
    // nawet gdy operacje na obiekcie spowodują wyjątek
    iniInput.Free();
  end;
end;

Dzięki temu będziesz miał większą kontrolę np. nad błędami, jakie może spowodować użytkownik (błędnie podane dane itd.);


Jest jeszcze jedna rzecz - jeśli korzystasz z bezparametrowych procedur/funkcji/metod, to zapisuj puste nawiasy przy ich wywołaniach; Czyli biorąc przykład z Twojego kodu:

LokalizacjaZnajdz.Execute;

metoda Execute nie posiada parametrów, ale zaznacz w kodzie że jest to metoda:

LokalizacjaZnajdz.Execute();

Dzięki temu od razu będzie wiadomo, że dane wywołanie to metoda, a nie np. właściwość; To dobra praktyka, choć niedobrze, że składnia pozwala na pominięcie podania pustych nawiasów.

0

Troszeczkę poćwiczyłem, i już pomijając wspomniane przez ciebie private, protected, public, published, try, except, finally popełniałem straszny błąd, którego nikt do tej pory mi nie wytknął, a który bardzo ułatwia pracę. A mianowicie w kodzie nie używałem w ogóle procedur ani funkcji.
Na chwilę obecną kod z prostego kalkulatora, który dziś tworzyłem wygląda następująco:

unit Silnik;

interface

type

TPodstawowe = Class
  private
  A:Real;
  B:Real;
  published
  function Dodaj:Real;
  function Odejmij:Real;
  function Pomnoz:Real;
  function Podziel:Real;
  constructor create ( AA, BB: Real );
End;

implementation

Function TPodstawowe.dodaj;
begin
  Dodaj:=A+B;
end;

Function TPodstawowe.odejmij;
begin
  Odejmij:=A-B;
end;

Function TPodstawowe.pomnoz;
begin
   Pomnoz:=A*B;
end;

Function TPodstawowe.Podziel;
begin
  Podziel:=A/B;
end;

Constructor TPodstawowe.create(AA: Real; BB: Real);
begin
  A:=AA;
  B:=BB;
end;

end.
unit FGlowny;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Silnik, StdCtrls;

type
.
.
.
    procedure WyczyscPola();
    procedure ZapiszZmienne(AA, BB:Real);
    procedure Button1Click(Sender: TObject);
    procedure uDodajClick(Sender: TObject);
    procedure uOdejmijClick(Sender: TObject);
    procedure uPomnozClick(Sender: TObject);
    procedure uPodzielClick(Sender: TObject);

var
  Form1: TForm1;
  uPodstawowe:TPodstawowe;
  AA, BB:Real;
implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
 WyczyscPola();
end;

procedure TForm1.ZapiszZmienne(AA: Real; BB: Real);
begin
  try
  AA:=StrToFloat(uA.Text);
  BB:=StrToFloat(uB.Text);
  except
  ShowMessage ( 'Wprowadziłeś błędne dane!' );
  WyczyscPola();
  end;
  uPodstawowe:=TPodstawowe.create(AA, BB );
end;

procedure TForm1.uDodajClick(Sender: TObject);
begin
ZapiszZmienne(AA, BB);
uWynik.Text:=FloatToStr(uPodstawowe.Dodaj);
end;

procedure TForm1.uOdejmijClick(Sender: TObject);
begin
ZapiszZmienne(AA, BB);
uWynik.Text:=FloatToStr(uPodstawowe.Odejmij);
end;

procedure TForm1.uPodzielClick(Sender: TObject);
begin
ZapiszZmienne(AA, BB);
uWynik.Text:=FloatToStr(uPodstawowe.Podziel);
end;

procedure TForm1.uPomnozClick(Sender: TObject);
begin
ZapiszZmienne(AA, BB);
uWynik.Text:=FloatToStr(uPodstawowe.Pomnoz);
end;

procedure TForm1.WyczyscPola();
begin
  uA.Text:='';
  uB.Text:='';
  uWynik.Text:='';
end;

end.

Liczę na uwagi i dalsze sugestie.

1

W klasie TPodstawowe, konstruktor i metody powinny być w sekcji Public;

Reszta była by raczej dobra (choć już myślenie mi wysiada) gdyby nie to, że obiekt uPodstawowe po pierwsze jest globalny, a po drugie nigdzie go nie zwalniasz, więc tworzysz wyciek pamięci, a to bardzo niedobrze; Ten uPodstawowe powinien być polem klasy formularza (w sekcji Private) i nazywać się FPodstawowe.

1

Nie wiem bo późna pora. I poza PC nie mam jak sprawdzić. Czy nie lepiej do konwersji zmienno przecinkowej użyć funkcji z przedrostkiem Try? Bo o ile pamiętam źródła VCL to funkcje do konwersji ze stringa rzucą i tak swoim wyjątkem. To czy to się nie zdubluje? Lepiej go obsłużyć w OnException dla aplikacji. Ewentualnie sprawdzać powodzenie funkcji if'em i or'em, pokazując komunikat o błędzie w przypadku kiedy sie nie uda konwersja.

Ja osobiście olewam często babranie się z klasą do wyjątków, bo modzę pod WinAPI. I nie lubię jak testowanie naoczne przerwie mi powrót do IDE. A tak jak zrobi się wyjątek to jedynie av.I wtedy wiem, że albo odwolałem się do nil, poza długośc łańcucha znaków, albo poza rozmiar tablicy. A i ja bym się raczej trzymał jednego nazewnictwa, raczej z angielska. Ale jako wprawka tak moze być.

Sorry za ewentualne literówki, ale fp będzie miał najwyżej okazję do poprawiana i darcia za mnie łacha :-)

1

Dokładnie - można skorzystać z funkcji TryStrToFloat, która nie wywoła wyjątku, więc całość można załatwić jednym **If**em; Czyli ten kod:

procedure TForm1.ZapiszZmienne(AA: Real; BB: Real);
begin
  try
  AA:=StrToFloat(uA.Text);
  BB:=StrToFloat(uB.Text);
  except
  ShowMessage ( 'Wprowadziłeś błędne dane!' );
  WyczyscPola();
  end;
  uPodstawowe:=TPodstawowe.create(AA, BB );
end;

zamienić na ten:

procedure TForm1.ZapiszZmienne(AA: Real; BB: Real);
begin
  if TryStrToFloat(uA.Text, AA) and TryStrToFloat(uB.Text, BB) then
    uPodstawowe := TPodstawowe.Create(AA, BB)
  else
  begin
    ShowMessage('Wprowadziłeś błędne dane!');
    WyczyscPola();
  end;
end;

Ale to tworzenie obiektu uPodstawowe nadal mi się nie podoba... Poza tym, w Twoim kodzie, obiekt ten zosanie i tak zawsze stworzony, bez względu na to, czy pojawią się wyjątki czy nie; I popraw formatowanie kodu, bo jeszcze nie jest całkiem dobre.

0
olesio napisał(a):

Nie wiem bo późna pora. I poza PC nie mam jak sprawdzić. Czy nie lepiej do konwersji zmienno przecinkowej użyć funkcji z przedrostkiem Try? Bo o ile pamiętam źródła VCL to funkcje do konwersji ze stringa rzucą i tak swoim wyjątkem. To czy to się nie zdubluje? Lepiej go obsłużyć w OnException dla aplikacji. Ewentualnie sprawdzać powodzenie funkcji if'em i or'em, pokazując komunikat o błędzie w przypadku kiedy sie nie uda konwersja.

Ja osobiście olewam często babranie się z klasą do wyjątków, bo modzę pod WinAPI. I nie lubię jak testowanie naoczne przerwie mi powrót do IDE. A tak jak zrobi się wyjątek to jedynie av.I wtedy wiem, że albo odwolałem się do nil, poza długośc łańcucha znaków, albo poza rozmiar tablicy. A i ja bym się raczej trzymał jednego nazewnictwa, raczj z angielska. Ale jako wprawka tak moze być.

Sorry za ewentualne literówki, ale fp będzie miał najwyżej okazję do poprawiana i darcia za mnie łacha :-)

Cały czas staram się nauczyć coś nowego, a gdy to się udaje satysfakcja wielka. Ale spoglądając czasem ile jeszcze przede mną troszkę mnie to przeraża. Nie rozumiem tylko czemu na Podstawach Programowania już w VCL uczyliśmy się Rekordów, Klas, Metod, ale wykładowca nie zwracał nam uwagi kiedy nie używaliśmy procedur po za obiektami, właśnie dziś dowiedziałem się jak można sobie ułatwić życie, tak samo try, except.

2

Ależ używanie funkcji poza klasami to nic złego. Ale faktycznie to co pamiętam kiedy chciałeś na początku robić osobne klasy do działań było trochę bez sensu i przekombinowaniem nie w te stronę. Odwołam się jeszcze raz do lubianego przez siebie WinAPI. To jak powstają pod VCL kontrolki też jest oparte o funkcje bazujące na systemowych. Całe VCL to trochę taka nakładka na WinAPI. I są to funkcje poza klasą, na przykład z modułu Windows po prostu.

1 użytkowników online, w tym zalogowanych: 0, gości: 1