procedure XXX
begin
forma:=Tforma.create(application)
forma.showmodal;
forma.free;
end;
procedure XXX
var f : Tforma;
begin
f:=Tforma.create(application)
f.showmodal;
f.free;
end;
Czy jest jakas istotna roznica, pomijajac fakt ze w drugim przypadku forma
moze wywolac procedure xxx
Chodzi mi o zasoby, bezpieczenstwo i takie tam ....
procedure xxx;
var
f: TForma;
begin
f := TForma.Create(nil);
try
f.ShowModal;
finally
f.Free
end;
end;
można jeszcze tak:
procedure xxx;
begin
with TForma.Create(nil) do
begin
ShowModal;
Free
end;
end;
albo:
procedure xxx;
begin
TForma.Execute;
end;
gdzie
type TForma
...
public class procedure Execute;
...
end;
i w nią wpisać cały cyrk z tworzeniem/wykonaniem/niszczeniem
Tygrys
Jesteś tego pewien? Możesz wywoływać metody obiektu jeszcze nie
istniejącego?
(No, chyba, że jest to konstruktor)
Paweł
>> type TForma
>> ...
>> public class procedure Execute;
>> ...
>> end;
>>
>> i w ni wpisa ca y cyrk z tworzeniem/wykonaniem/niszczeniem
>>
>
> Jesteś tego pewien? Możesz wywoływać metody obiektu jeszcze nie
> istniejącego?
> (No, chyba, że jest to konstruktor)
Jestes pewien, ze nie mozna? Jestes pewien, ze rozumiesz ten kod?
--
pozdrawiam
Norbert
No daj spokój... nie wiesz do czego służy "class procedure" ??
--
Arivald
Zakładam że "forma" to globalna zmienna, np. ta dodawana przez IDE w
unicie z formą?
W takim wypadku każde wywołanie XXX z kodu formy Tforma skończy się
wyciekiem pamięci i próbą podwójnego zwolnienia obiektu.
A dlaczego, to już dojdź sam ;-)
Dla bezpieczeństwa, nie używaj zmiennej globalnej na formę.
Delphi od zawsze tworzy taką zmienną, i używa jej w swoim generowanym
kodzie... ale to uczy ludzi bardzo złych nawyków.
Ja w moim kodzie od razu usuwam tą zmienną.
> procedure XXX
> var f : Tforma;
> begin
> f:=Tforma.create(application)
> f.showmodal;
> f.free;
> end;
To jest ok, w miarę bezpieczne.
Chociaż ja bym dodał jeszcze try/finally, i użył Release zamiast Free
(dobry nawyk dla form).
>
> Czy jest jakas istotna roznica, pomijajac fakt ze w drugim przypadku forma
> moze wywolac procedure xxx
> Chodzi mi o zasoby, bezpieczenstwo i takie tam ....
Wersja z class procedure Tforma.Execute jest bardzo dobrym podejściem,
dla form słuzących do konfiguracji jakichś parametrów, np filtrów.
Ja używam jeszcze innego podejścia:
W jednym unicie mam obiekt danych (np dane filtru) i formę która ten
obiekt modyfikuje. przykładowy kod.
unit UnitFilter;
interface
....
type
TFilter = class(TComponent)
private
Fa: string;
Fb: boolean;
Fc: TStrings;
public
property A: string ...
property B: boolean ...
property C: TStrings ...
function ExecuteDialog(): boolean;
// lub jesli potrzebuje wiecej niż true/false
function ExecuteDialog(): TModalResult;
end;
TFilterForm = class(TForm)
EditA: TEdit;
CheckBoxB: TCheckBox;
ListBoxC: TListBox;
public
end;
implementation
...
function TFilter.ExecuteDialog(): boolean / TModalResult;
var
dialog: TFilterForm;
begin
dialog: TFilterForm.Create(self); //lub Create(Application) jesli
TFilter nie jest TComponent
try
//konfigurowanie formy
dialog.EditA.Text := Self.A;
dialog.CheckBoxB.Checked := Self.B;
dialog.ListBoxC.Items.Assign(Self.C);
Result := mrOk = dialog.ShowModal();
// lub
Result := dialog.ShowModal();
if not Result then Exit;
//lub
if Result = mrCancel then Exit;
//odebranie wyniku
Self.A := dialog.EditA.Text;
Self.B := dialog.CheckBoxB.Checked;
Self.C.Assign(dialog.ListBoxC.Items);
finally
dialog.Release();
end;
end;
end.
i potem używanie:
type TSomeForm ...
FFilter: TFilter;
....
procedure OnXXXClick(Sender: TObject);
begin
if FFilter.ExecuteDialog() then RebuildSQL();
end;
Zauważasz podobieństwo do wbudowanych dialogów?
Jeśli można to obiekt z danymi robię jako komponent. Można go
zarejestrować w IDE, i potem po prostu wrzucać na formę. Chociaż ja
rzadko tak robię.
Za to częściej używam FreeNotification(), dzieki czemu obiekt wie jak
się jakiś powiązany komponent zwolni.
--
Arivald
> Jesteś tego pewien? Możesz wywoływać metody obiektu jeszcze nie
> istniejącego?
> (No, chyba, że jest to konstruktor)
Tak, tak. Świat poszedł do przodu. Od długiego czasu jest wiele
ciekawych mechanizmów. Wyobraź sobie, że oprócz takich metod mogą być
jeszcze takie pola. Wreszcie można zrobić rasowy singleton, a nie
pseudo-singleton opierający się na zmiennej prywatnej modułu.
--
PaSkol
> W takim wypadku każde wywołanie XXX z kodu formy Tforma skończy się
> wyciekiem pamięci i próbą podwójnego zwolnienia obiektu.
> A dlaczego, to już dojdź sam ;-)
>
No wlasnie mam drobny wyciek i nie moge dojsc dlaczego. Jeden wyciek byl
spowodowany bledem w komponencie - nie niszczylem w nim zmiennej Tbitmap i
po kilkudziesieciu otwarciach okna dostawalem
out of resource. No ale to bylo ewidentne niedopatrzenie.
Zadbalem o reczne niszczenie wszytskiego co tworze (np dynamicznie tworzone
komponenty fibdata... ) ale i tak po kazdym zamkniecu okna brakuje mi okolo
240 bajtow i nie wiem czemu - to niby niewiele
ale z pustej formy nie wycieka nic. Obawiam sie ze to niedorobki TMSow - to
ich PageControle i inne wynalazki uzywam. Nie robilem jeszcze testow na XE
ale sie obawiam ze poprostu wersje dla D5 sa olane
- juz po kilku mianutach zabawy w XE zauwazylem ze te komponenty zupelnie
inaczej zachowuja sie w trybie design w D5 i XE - w d5 np po kliknieciu poza
okno - np w inspektora komponent potrafi zmienic polozenie ;)
Forme niszczylem i przez F.free i przez FreeAndNil(F) ale brez zauwazalnej
roznicy.
ja mam taką konstrukcję
begin
tForma.create(filter).done;
end;
gdzie
function tForma.done;
begin
result:=-1;
if self=nil then exit;
try
result:=showmodal;
finally
release;
end;
end;
> try
> //konfigurowanie formy
> dialog.EditA.Text := Self.A;
> dialog.CheckBoxB.Checked := Self.B;
> dialog.ListBoxC.Items.Assign(Self.C);
>
> //odebranie wyniku
> Self.A := dialog.EditA.Text;
> Self.B := dialog.CheckBoxB.Checked;
> Self.C.Assign(dialog.ListBoxC.Items);
u mnie elementy formy tak konstruowane, że same aktualizują odpowiednie
pola zadanego obiektu, więc zadanie i odebranie wyniku nie występuje,
wystarcza część konstrukcji formy, która jest umieszczona w
konstruktorze, natomiast reszta jest stała.
--
Darek
No to czemu nie pójść już na całość:
wywołanie:
TForma.ShowModalNewForm;
oraz class function w class helperze:
type
TCustomFormHelper = class helper for TCustomForm
class function ShowModalNewForm(AOwner: TComponent = nil): Integer;
end;
class function TCustomFormHelper.ShowModalNewForm(AOwner: TComponent = nil): Integer;
begin
with TCustomForm(NewInstance).Create(AOwner) do
try
Result := ShowModal;
finally
Release;
end;
end;
--
pozdrowienia
Krzysztof Szyszka, X-Files Software
Developer of X-Files Components
Borland/CodeGear/Embarcadero Technology Partner
_________________________________________
Website: http://www.x-files.pl/ E-mail: ne...@x-files.pl
to się niczym nie różni od wspominanej już funkcji klasowej
oprócz tego że w starszych Delphi tego nie ma
>
> type
> TCustomFormHelper = class helper for TCustomForm
> class function ShowModalNewForm(AOwner: TComponent = nil): Integer;
> end;
>
> class function TCustomFormHelper.ShowModalNewForm(AOwner: TComponent =
> nil): Integer;
> begin
> with TCustomForm(NewInstance).Create(AOwner) do
> try
> Result := ShowModal;
> finally
> Release;
> end;
> end;
>
gdzie jest deklarowane NewInstance
no i dlaczego ograniczamy się tylko do tCustomForm: dla każdej formy mam
tworzyć class helper?
--
Darek
No skoro 70% gospodyń domowych nie widzi żadnej różnicy ... ;-)
>> type
>> TCustomFormHelper = class helper for TCustomForm
>> class function ShowModalNewForm(AOwner: TComponent = nil): Integer;
>> end;
>>
>> class function TCustomFormHelper.ShowModalNewForm(AOwner: TComponent =
>> nil): Integer;
>> begin
>> with TCustomForm(NewInstance).Create(AOwner) do
>> try
>> Result := ShowModal;
>> finally
>> Release;
>> end;
>> end;
>
> gdzie jest deklarowane NewInstance
> no i dlaczego ograniczamy się tylko do tCustomForm: dla każdej formy mam tworzyć class helper?
Może najpierw doczytaj jak działa class helper, a potem komentuj ...
Dowcipne, ale mało treściwe.
Być może chodzi Tobie o to, że przy programowaniu wizualnym musisz
dziedziczyć bezpośrednio po tForm (nie używam to nie wiem), więc class
helper jest przydatny. Przy zwyczajnym dziedziczeniu jest to to samo.
>
>>> type
>>> TCustomFormHelper = class helper for TCustomForm
>>> class function ShowModalNewForm(AOwner: TComponent = nil): Integer;
>>> end;
>>>
>>> class function TCustomFormHelper.ShowModalNewForm(AOwner: TComponent =
>>> nil): Integer;
>>> begin
>>> with TCustomForm(NewInstance).Create(AOwner) do
>>> try
>>> Result := ShowModal;
>>> finally
>>> Release;
>>> end;
>>> end;
>>
>> gdzie jest deklarowane NewInstance
>> no i dlaczego ograniczamy się tylko do tCustomForm: dla każdej formy
>> mam tworzyć class helper?
>
> Może najpierw doczytaj jak działa class helper, a potem komentuj ...
>
Nie komentowałem tylko pytałem.
Ale niech będzie, zapytam szczegółowo: jak mam powyższe utworzyć dla
formy z własnym konstruktorem, który np. ma inne parametry.
--
Darek
>>>> type
>>>> TCustomFormHelper = class helper for TCustomForm
>>>> class function ShowModalNewForm(AOwner: TComponent = nil): Integer;
>>>> end;
>>>>
>>>> class function TCustomFormHelper.ShowModalNewForm(AOwner: TComponent =
>>>> nil): Integer;
>>>> begin
>>>> with TCustomForm(NewInstance).Create(AOwner) do
>>>> try
>>>> Result := ShowModal;
>>>> finally
>>>> Release;
>>>> end;
>>>> end;
>>>
>>> gdzie jest deklarowane NewInstance
>>> no i dlaczego ograniczamy się tylko do tCustomForm: dla każdej formy
>>> mam tworzyć class helper?
>>
>> Może najpierw doczytaj jak działa class helper, a potem komentuj ...
>>
>
> Nie komentowałem tylko pytałem.
> Ale niech będzie, zapytam szczegółowo: jak mam powyższe utworzyć dla
> formy z własnym konstruktorem, który np. ma inne parametry.
Normalnie, np. użyć RTTI do wywołania dowolnego konstruktora z dowolnymi
parametrami i je do niego przekazać; sama deklaracja wyglądać może np. tak:
class function TCustomFormHelper.ShowModalNewForm(AParams :
TArray<TValue>): Integer;
Można tez inaczej - można "wstrzyknąć" konkretną wartość do konkretnej
metody, nie tylko konstruktora i wyglądać może to np. tak:
constructor Create([Injection('diEntity')] AOwner: TComponent);
--
wloochacz
Nie. Chodziło mi o to, że przy użyciu class helpera można "dodać" taką
funkcjonalność do KAŻDEJ klasy, która dziedziczy po TCustomForm
i używać jej tak, jak każej innej publicznej metody zadeklarowanej w
TCustomForm bez definiowania jej w różnych formatkach bazowych,
z których potem musisz dziedziczyć, żeby tą funkcjonalność osiągnąć.
Krótko mówiąc: przeniosłem Twoją funkcjonalnośc z formularza bazowego
do TCustomForm, czyli de facto do każdego.
>>>> type
>>>> TCustomFormHelper = class helper for TCustomForm
>>>> class function ShowModalNewForm(AOwner: TComponent = nil): Integer;
>>>> end;
>>>>
>>>> class function TCustomFormHelper.ShowModalNewForm(AOwner: TComponent =
>>>> nil): Integer;
>>>> begin
>>>> with TCustomForm(NewInstance).Create(AOwner) do
>>>> try
>>>> Result := ShowModal;
>>>> finally
>>>> Release;
>>>> end;
>>>> end;
>>>
>>> gdzie jest deklarowane NewInstance
>>> no i dlaczego ograniczamy się tylko do tCustomForm: dla każdej formy
>>> mam tworzyć class helper?
>>
>> Może najpierw doczytaj jak działa class helper, a potem komentuj ...
>
> Nie komentowałem tylko pytałem.
Ale Twoje pytanie pokazuje, że nie wiesz, jak działa class helper, bo gdybyś
wiedział, to byś go nie zadał.
> Ale niech będzie, zapytam szczegółowo: jak mam powyższe utworzyć dla formy z własnym
> konstruktorem, który np. ma inne parametry.
Podałeś swój uniwersalny przykład wywołania modalnego formularza
z domyślnym konstruktorem, ale musisz go umieścić w każdej formie
bazowej, z której potem musisz dziedziczyć. Ja pokazałem swój przykład,
jak to samo można napisać przy użyciu class helpera dla DOWOLNEGO
formularza. Dodałem class helpera do TCustomForm, bo właśnie w tej
klasie pojawia metoda ShowModal.
Jeśli w jakimś SWOIM formularzu bazowym definiujesz własny konstruktor,
to możesz od razu w tej samej klasie dopisać metodę ShowModalNewForm
z odpowiednimi parametrami dla konstruktora, więc nie ma potrzeby pisania
do tego class helpera. Class helper jest przydatny dla tych klas, których TY
nie możesz rozszerzyć, czyli dla całego VCLa.
BTW. NewInstance to class function zdefiniowana w klasie TObject.
To tyle moich wyjaśnień, a teraz jeśli pozwolisz to ja się trochę poczepiam :-)
Tak wygląda Twoja metoda Done:
function tForma.done;
begin
result:=-1;
if self=nil then exit;
try
result:=showmodal;
finally
release;
end;
end;
Czy możesz mi podać w jakiej sytuacji będzie spełniony warunek "self=nil",
skoro umieściłeś w Done taką linijkę ?
if self=nil then exit;
nie zauważyłem class :)
Paweł
Otóż można - i wcale nie mam na myśli metod statycznych :)
Metoda jest taką procedurą albo funkcją, która dostaje dodatkowy, niejawny
parametr self zawierający wskaźnik na klasę, z której została wywołana. W
normalnym toku wykonywania programu wskaźnik ten wskazuje na zaalokowaną
wcześniej instancję klasy, na której wołana jest metoda. Możemy jednak
oszukać kompilator, sprzedając mu wskaźnik do nieistniejącego miejsca w
pamięci:
<code>
TMyClass = class(TObject)
public
procedure Test;
end;
...
procedure TMyClass.Test;
begin
Application.Messagebox('Method called!','App',MB_OK or
MB_ICONINFORMATION);
end;
procedure TForm1.Button1Click(Sender: TObject);
var i : integer;
c : TMyClass;
begin
i:=1234;
c := TMyClass(i);
c.Test;
end;
</code>
Dlaczego powyższy kod się wykona? Dlatego, że metoda Test nie ma żadnego -
jawnego, ani niejawnego (np. poprzez wywołanie innej metody) odwołania do
niejawnego parametru self. W ten sposób nigdy nie ma szansy dojść do
naruszenia dostępu do pamięci, więc w myśl zasady "wszystko, co nie jest
zabronione, jest dozwolone", system nie zgłasza żadnych zastrzeżeń do
powyższego programu w trakcie jego wykonywania.
Mam nadzieję, że nie muszę dodawać, że wykorzystywanie powyższego faktu
jest pomysłem dosyć smutnym i w takiej sytuacji powinno się raczej
zastosować metodę statyczną (klasową), w której parametru self w
kontekście instancji klasy użyć się nie da. Tym niemniej wywołanie metod
na nieistniejącej instancji jest możliwe :)
> Paweł
Pozdrawiam -- Spook.
--
! ._______. Warning: Lucida Console sig! //) !
! || spk || www.spook.freshsite.pl / _ """*!
! ||_____|| spook at op.pl / ' | ""!
! | ___ | tlen: spoko_ws gg:1290136 /. __/"\ '!
! |_|[]_|_| May the SOURCE be with you! \/) \ !
Jeśli dobrze pamiętam, jest ona potrzebna wizualnemu designerowi formatki,
bez niej jakaś część jego funkcjonalności nie będzie działać prawidłowo.
Niestety, odkryłem to dosyć dawno temu i już nie pamiętam dokładnie, na
czym problem polegał, ale wiem, że dla tej jednej zmiennej musiałem zawsze
robić wyjątek.
Ooo, a to co? Czyżby do Delphi zawitały atrybuty? Strasznie dawno nic nie
pisałem, mam trochę tyłów w zakresie nowinek w OP :)
Nie rozumiem. Jeżeli w tCustomForm zadeklaruję funkcję klasową, to mogę
ją użyć w każdym formularzu dziedziczącym po tCustomForm.
>
>>>>> type
>>>>> TCustomFormHelper = class helper for TCustomForm
>>>>> class function ShowModalNewForm(AOwner: TComponent = nil): Integer;
>>>>> end;
>>>>>
>>>>> class function TCustomFormHelper.ShowModalNewForm(AOwner: TComponent =
>>>>> nil): Integer;
>>>>> begin
>>>>> with TCustomForm(NewInstance).Create(AOwner) do
>>>>> try
>>>>> Result := ShowModal;
>>>>> finally
>>>>> Release;
>>>>> end;
>>>>> end;
>>>>
>>>> gdzie jest deklarowane NewInstance
>>>> no i dlaczego ograniczamy się tylko do tCustomForm: dla każdej formy
>>>> mam tworzyć class helper?
>>>
>>> Może najpierw doczytaj jak działa class helper, a potem komentuj ...
>>
>> Nie komentowałem tylko pytałem.
>
> Ale Twoje pytanie pokazuje, że nie wiesz, jak działa class helper, bo
> gdybyś
> wiedział, to byś go nie zadał.
To już ani pytać ani komentować nie można?
>
>> Ale niech będzie, zapytam szczegółowo: jak mam powyższe utworzyć dla
>> formy z własnym konstruktorem, który np. ma inne parametry.
>
> Podałeś swój uniwersalny przykład wywołania modalnego formularza
> z domyślnym konstruktorem,
to ty tak twierdzisz. Bo przykład pozwalał wywołać dowolny konstruktor z
dowolnymi parametrami
ale musisz go umieścić w każdej formie
> bazowej, z której potem musisz dziedziczyć.
nie, w jednej formie bazowej, z której dziedziczę
Ja pokazałem swój przykład,
> jak to samo można napisać przy użyciu class helpera dla DOWOLNEGO
> formularza. Dodałem class helpera do TCustomForm, bo właśnie w tej
> klasie pojawia metoda ShowModal.
>
> Jeśli w jakimś SWOIM formularzu bazowym definiujesz własny konstruktor,
> to możesz od razu w tej samej klasie dopisać metodę ShowModalNewForm
> z odpowiednimi parametrami dla konstruktora,
ależ o tym mówię: nie muszę dopisywać żadnych funkcji pomocniczych,
tylko konstruktor, który zawiera opis formy, samo działanie zaszyte jest
w funkcjach bazowych i nie jest reimplementowane.
więc nie ma potrzeby pisania
> do tego class helpera. Class helper jest przydatny dla tych klas,
> których TY
> nie możesz rozszerzyć, czyli dla całego VCLa.
Ciekawe, nie mogę rozszerzać VCL? nie zauważyłem.
>
> BTW. NewInstance to class function zdefiniowana w klasie TObject.
> To tyle moich wyjaśnień, a teraz jeśli pozwolisz to ja się trochę
> poczepiam :-)
Już wiem. W sumie ciekawa funkcja, przydatna przy tworzeniu własnej
polityki alokacji obiektów albo wtedy gdy niezbędne są działania przed
wywołaniem konstruktora, czyli w sumie bardzo specyficzne zastosowania.
Natomiast w tym przykładzie zupełnie nie na miejscu.
A "czepiać" się możesz do woli.
>
> Tak wygląda Twoja metoda Done:
>
> function tForma.done;
> begin
> result:=-1;
> if self=nil then exit;
> try
> result:=showmodal;
> finally
> release;
> end;
> end;
>
> Czy możesz mi podać w jakiej sytuacji będzie spełniony warunek "self=nil",
> skoro umieściłeś w Done taką linijkę ?
> if self=nil then exit;
Jeżeli stosujesz newInstance to powinieneś wiedzieć: gdy konstruktorowi
nie uda się utworzyć obiektu.
--
Darek
Chwila, to class helpers zostały ostatnio dodane a nie funkcje klasowe,
więc to pytanie nie do mnie. To ja się pytam co jest lepszego w
rozwiązaniu z class helpers.
Normalnie to by można wykorzystać rzutowanie.
Tylko po co, nie dość że to rząd wielkości wolniejsze, potrzeba więcej
kodu, to jeszcze pozbawiamy się opieki kompilatora nad kontrolą
przekazywanych parametrów.
--
Darek
> Strasznie dawno nic
> nie pisałem, mam trochę tyłów w zakresie nowinek w OP :)
Trochę, to bardzo szerokie pojęcie ;-)
--
wloochacz
No możesz. Czego tu nie rozumiesz ?
>> Tak wygląda Twoja metoda Done:
>>
>> function tForma.done;
>> begin
>> result:=-1;
>> if self=nil then exit;
>> try
>> result:=showmodal;
>> finally
>> release;
>> end;
>> end;
>>
>> Czy możesz mi podać w jakiej sytuacji będzie spełniony warunek "self=nil",
>> skoro umieściłeś w Done taką linijkę ?
>> if self=nil then exit;
>
> Jeżeli stosujesz newInstance to powinieneś wiedzieć: gdy konstruktorowi nie uda się utworzyć
> obiektu.
No przyznam się, że nie wiem. Czy mógłbyś to poprzeć jakimś przykładem ?
> więc to pytanie nie do mnie. To ja się pytam co jest lepszego w
> rozwiązaniu z class helpers.
Nic, tylko tyle że jest prostsze i wygodniejsze w używaniu.
Ale miałem gdzieś pod ręką wzorcowe wykorzystanie (jak dla mnie) Class
Helpers, tylko nie pamiętam o co chodziło :D
Jasne.
Tylko, żeby rzutować trzeba znać typ i mieć dostęp do jego deklaracji.
A ja nie chcę ani jednego ani drugiego.
> Tylko po co, nie dość że to rząd wielkości wolniejsze,
I co z tego?
O ile wolniejsze będzie utworzeni formatki (sic!) za pomocą RTTI vs
rzutowania?
To nawet nie jest przesada - to jest śmieszne.
> potrzeba więcej
> kodu, to jeszcze pozbawiamy się opieki kompilatora nad kontrolą
> przekazywanych parametrów.
Kompilator w sensie, na etapie kompilacji kodu?
Prawda - ale zawsze możesz sprawdzić w czasie wykonania.
OK - to są jakieś wady, za to zalet jest zdecydowanie więcej.
--
wloochacz
>> więc to pytanie nie do mnie. To ja się pytam co jest lepszego w
>> rozwiązaniu z class helpers.
> Nic, tylko tyle że jest prostsze i wygodniejsze w używaniu.
> Ale miałem gdzieś pod ręką wzorcowe wykorzystanie (jak dla mnie) Class
> Helpers, tylko nie pamiętam o co chodziło :D
Moze z dostosowywaniem niektorych klas do obslugi petli for in?
>>> Można tez inaczej - można "wstrzyknąć" konkretną wartość do konkretnej
>>> metody, nie tylko konstruktora i wyglądać może to np. tak:
>>> constructor Create([Injection('diEntity')] AOwner: TComponent);
Mozesz w kilku slowach napisac co to robi?
--
pozdrawiam
Norbert
Niby tak. ale jak zmienisz ABI VCLa, to nie będziesz mógł skorzystać z
BPLi. zostanie Ci tylko statyczne linkowanie.
No chyba że do aktualnych Delphi dają cały kod źródłowy VCL, i da się go
przebudować...
>> Tak wygląda Twoja metoda Done:
>>
>> function tForma.done;
>> begin
>> result:=-1;
>> if self=nil then exit;
>> try
>> result:=showmodal;
>> finally
>> release;
>> end;
>> end;
>>
>> Czy możesz mi podać w jakiej sytuacji będzie spełniony warunek
>> "self=nil",
>> skoro umieściłeś w Done taką linijkę ?
>> if self=nil then exit;
>
> Jeżeli stosujesz newInstance to powinieneś wiedzieć: gdy konstruktorowi
> nie uda się utworzyć obiektu.
Konstruktory z definicji nie mogą się nie powieść. Konstruktor zawsze
zwraca skonstruowany obiekt (o ile w ogóle zwraca). Jedynie wyjątek może
przerwać konstruktor, le w takim wypadku i tak done() nie będzie wołane.
Ergo sprawdzanie self=nil jest zbyteczne.
--
Arivald
Dopiero po odpowiedzi Arivalda zrozumiałem, że Ty chcesz tą funkcję klasową
zadeklarować bezpośrednio w module Forms, a nie przy pomocy class hepera.
To mam dla Ciebie inną odpowiedź. Po prostu zrób to i spróbuj uzywać.
Życzę powodzenia.
No i popsułeś mi moją pułapkę ;-)
A Propos enumeratora DataSetu - taki może być? ;-)
var
v : Variant;
c : Currency;
begin
for v in JakisDataSet do
begin
if v.PoleZData < Now then
c := v.PoleZWartosciaCurrency + c;
end;
end;
Źródła tu:
http://cc.embarcadero.com/Item/25386
>>>> Można tez inaczej - można "wstrzyknąć" konkretną wartość do konkretnej
>>>> metody, nie tylko konstruktora i wyglądać może to np. tak:
>>>> constructor Create([Injection('diEntity')] AOwner: TComponent);
>
> Mozesz w kilku slowach napisac co to robi?
Oznacza parametr metody (tu - konstruktora) i nic więcej nie robi poza
oznaczaniem.
Ważne jest, co dalej się z tym dzieje i do czego w/w oznaczenie jest
wykorzystywane.
Wykorzystuję je do przekazania konstruktorowi klasy, konkretnej
instancji konkretnego obiektu - bo on tego oczekuje.
A konstruktor klasy mówi mi, że tego oczekuje bo oznaczyłem ten parametr
odpowiednim atrybutem.
Po co?
Po to, żeby nie opisać kodu - ja takie (i nie tylko takie) związki
pomiędzy klasami tworzę za pomocą metadanych (upraszczając -
konfiguracji), a nie za pomocą kodu.
--
wloochacz
Ale w jakim celu. Nie zmieniam ABI, tylko stosuję zwykłe dziedziczenie,
Mi wystarcza wprowadzenie pośredniej klasy tCustomForm, po którym
dziedziczą wszystkie te formy, które potrzebują.
>
>>> Tak wygląda Twoja metoda Done:
>>>
>>> function tForma.done;
>>> begin
>>> result:=-1;
>>> if self=nil then exit;
>>> try
>>> result:=showmodal;
>>> finally
>>> release;
>>> end;
>>> end;
>>>
>>> Czy możesz mi podać w jakiej sytuacji będzie spełniony warunek
>>> "self=nil",
>>> skoro umieściłeś w Done taką linijkę ?
>>> if self=nil then exit;
>>
>> Jeżeli stosujesz newInstance to powinieneś wiedzieć: gdy konstruktorowi
>> nie uda się utworzyć obiektu.
>
> Konstruktory z definicji nie mogą się nie powieść. Konstruktor zawsze
> zwraca skonstruowany obiekt (o ile w ogóle zwraca). Jedynie wyjątek może
> przerwać konstruktor, le w takim wypadku i tak done() nie będzie wołane.
> Ergo sprawdzanie self=nil jest zbyteczne.
>
Wystarczy używać FAIL, wtedy nie jest konieczne przechwytywanie
wyjątków, oczywiście można to robić w bardzo ograniczonym zakresie, ale
zysk jest widoczny: jedna linia wywołania bez konieczności opakowywania
wszystkiego w try finally.
--
Darek
Nie, zadeklarowane w nowym unicie nowa klasa dziedzicząca po tForms,
natomiast wszystkie formy dziedziczą po tej nowej (lub innej, mam kilka
takich bazowych, w zależności od potrzeb)
>
> To mam dla Ciebie inną odpowiedź. Po prostu zrób to i spróbuj uzywać.
> Życzę powodzenia.
>
Nie muszę próbować. Po prostu to pracuje dla mnie od nastu lat.
--
Darek
Nie chodzi o czas tylko kolejność, zarówno przykład jak i samo narzędzie
zostało przedstawione później, a to nowsze powinno wykazać że jest
lepsze od starego, nie odwrotnie. To że coś jest nowsze, modniejsze,
politycznie poprawne nie znaczy że jest zawsze lepsze, natomiast
wszystkie argumenty sprowadzają się do tego typu stwierdzeń.
>
>> więc to pytanie nie do mnie. To ja się pytam co jest lepszego w
>> rozwiązaniu z class helpers.
> Nic, tylko tyle że jest prostsze i wygodniejsze w używaniu.
W powyższym przykładzie? Wykaż, albo przynajmniej rozwiej moje wątpliwości.
> Ale miałem gdzieś pod ręką wzorcowe wykorzystanie (jak dla mnie) Class
> Helpers, tylko nie pamiętam o co chodziło :D
Mówimy o konkretnym zastosowaniu, a nie wzorcowym.
Aby wyświetlić pola na formatce, musisz wiedzieć co ma być wyświetlone,
czyli znać typy danych. Możesz to przekazywać dowolnie, metod jest
kilkanaście. Ale w całym wątku mowa o formatce z kilkoma polami, więc
nie zasadne jest wykorzystywanie metod które są wolniejsze, wymagają
więcej tekstu programu i potencjalnie generują więcej błędów
>
>> Tylko po co, nie dość że to rząd wielkości wolniejsze,
> I co z tego?
> O ile wolniejsze będzie utworzeni formatki (sic!) za pomocą RTTI vs
> rzutowania?
Na oko rząd wielkości, może więcej
> To nawet nie jest przesada - to jest śmieszne.
Wolniejsze to znaczy gorzej, nie ważne o ile, ważne jakim kosztem.
Może Tobie to nie przeszkadza, dla mnie, gdy formatka tworzona jest
dłużej niż 10ms to problem. Świat poza Twoim podwórkiem może mieć inne
wymagania, gdzie właśnie Twoje problemy mogą być śmieszne. Przyzwoitość
nakazuje nie kpić z czegoś, czego się nie zna.
>
>> potrzeba więcej
>> kodu, to jeszcze pozbawiamy się opieki kompilatora nad kontrolą
>> przekazywanych parametrów.
> Kompilator w sensie, na etapie kompilacji kodu?
> Prawda - ale zawsze możesz sprawdzić w czasie wykonania.
Mogę też nie popełnić błędu, ale nie o to chodzi.
Im mniej linii w programie, im więcej automatycznej kontroli przez
kompilator, tym mniej błędów, tym prostsza rozbudowa itp.
> OK - to są jakieś wady, za to zalet jest zdecydowanie więcej.
Podaj przynajmniej jedną zaletę, ale taką policzalną, bo jeszcze żadnej
nie było.
>
--
Darek
Przykład z mojego podwórka - obsługa DataSetów; nie ma czegoś takiego
jak bazowa forma do obsługi bazy danych, jest usługa która realizuje w/w
funkcjonalność i w zależności od potrzeb - jest do formy wstrzykiwana.
>> To mam dla Ciebie inną odpowiedź. Po prostu zrób to i spróbuj uzywać.
>> Życzę powodzenia.
>>
>
> Nie muszę próbować. Po prostu to pracuje dla mnie od nastu lat.
I nie irytuję Cię to, że taki model (oparty na dziedziczeniu) jest albo
bardzo rozbudowany, a tym samym jego zwinność i zdolność adaptacji jest
na poziomie nosorożca - albo jest bardzo ograniczony a jego
funkcjonalność nie jest wystarczająca?
A najczęściej i jedno i drugie - robiłem tak przez kilka lat i pewnie -
działa.
Ale mi nigdy nie wystarczało to, że coś działa...
--
wloochacz
A jak z technicznego punktu widzenia jest organizowane to "wstrzykiwanie"?
Bo w modelu komponentowym, dodając komponenty do form i innych
komponentów też dodajesz nową funkcjonalność... Chociaż co nieco kleju
pomiędzy komponentami trzeba zakodować...
Tym nie mniej typową formatkę do edycji datasetu można wyklikać, zero kodu.
BTW, wystarczy mi link lub hasło dla googla.
--
Arivald
Pozwolisz że odpowiem Twoimi własnymi słowami:
> Jeżeli w tCustomForm zadeklaruję funkcję klasową, to mogę
> ją użyć w każdym formularzu dziedziczącym po tCustomForm.
Każda zmiana klasie VCL to zmiana ABI, i zwiazanw z tym problemy.
> tylko stosuję zwykłe dziedziczenie,
> Mi wystarcza wprowadzenie pośredniej klasy tCustomForm, po którym
> dziedziczą wszystkie te formy, które potrzebują.
No widzisz... Teraz mówisz inaczej... Się więc naucz proszę precyzyjnie
wypowiadać. To bardzo pomaga.
WTF?
Problemem była linia kodu ze sprawdzaniem Self = nil. Napisąłem Ci
dlaczego twoje przeświadczenie o konieczności jej stosowania jest
błędne. I wcale nie musisz używać try/finally...
--
Arivald
Przypuszczam, ze chodzi o coś takiego:
http://msdn.microsoft.com/en-us/library/aa973811.aspx
--
PaSkol
> Przypuszczam, ze chodzi o coś takiego:
> http://msdn.microsoft.com/en-us/library/aa973811.aspx
Tak - tam jest to całkiem udatnie opisane.
Technicznie, to ja używam Spring for Delphi:
http://code.google.com/p/delphi-spring-framework/
A podczas projektowania kontenera DI korzystałem z opisu podanego przez
PaSKol'a i dokumentacji Castle Windsor - aczkolwiek, bardziej chodziło o
ideę - oczywiście.
Tyle, że to co tam napisali bardzo pasowało do mojej wizji wykorzystania
takiego kontenera i np. wprost przejąłem takie pojęcia jak Entity i Service.
--
wloochacz
Jakie problemy (gdy nie stosuję BPL)
I nie zmieniam VCL (tzn nie modyfikuję unitów Forms itp) tylko go rozszerzam
>
>> tylko stosuję zwykłe dziedziczenie,
>> Mi wystarcza wprowadzenie pośredniej klasy tCustomForm, po którym
>> dziedziczą wszystkie te formy, które potrzebują.
>
> No widzisz... Teraz mówisz inaczej... Się więc naucz proszę precyzyjnie
> wypowiadać. To bardzo pomaga.
Z Waszych wypowiedzi wynikało że rozumiecie, ale się nie zgadzacie, nie
zakładałem że muszę to aż tak obrazowo opisywać.
sam pisałeś o wykorzystywaniu tego przy obj.free
podobna konstrukcja występuje kilka razy w kodzie Delphi
> Problemem była linia kodu ze sprawdzaniem Self = nil. Napisąłem Ci
> dlaczego twoje przeświadczenie o konieczności jej stosowania jest
> błędne. I wcale nie musisz używać try/finally...
to jak uzyskasz efekt, gdy konstruktor nie chce/nie może się skutecznie
wykonać. Jak rzucisz wyjątek to go musisz gdzieś przechwycić. Jak to
zrobisz poniżej, funkcje biblioteczne posprzątają a całość zwróci nil, a
dalej -1. Użycie całej konstrukcji zajmuje 1 linię i dostaję odpowiedni
rezultat
--
Darek
Nie zrozum mnie źle. Nic nie mam przeciwko class helpers jako takim.
Faktycznie w niektórych przypadkach mogą być pomocne, szczególnie gdy
nie można w określony sposób rozbudować klasy bazowej. Natomiast dla
mnie ważna jest ścisła kontrola typów, deklaruję co można zrobić z
poszczególnymi danymi a kompilator pilnuje abym tego nie przekroczył.
Każde narzędzie i metodę, która zwiększa precyzję definiowania i
ogranicza sposoby wywołania (a tym samym zmniejsza prawdopodobieństwo
wystąpienia błędu) witam z otwartymi ramionami. Helpers zmieniają sposób
działania klasy poza miejscem gdzie jest ona implementowana, co jest w
pewnym sensie łamaniem kontraktu. Jest to wygodne, ale może prowadzić do
nadużyć.
>
> Przykład z mojego podwórka - obsługa DataSetów; nie ma czegoś takiego
> jak bazowa forma do obsługi bazy danych, jest usługa która realizuje w/w
> funkcjonalność i w zależności od potrzeb - jest do formy wstrzykiwana.
>
>>> To mam dla Ciebie inną odpowiedź. Po prostu zrób to i spróbuj uzywać.
>>> Życzę powodzenia.
>>>
>>
>> Nie muszę próbować. Po prostu to pracuje dla mnie od nastu lat.
> I nie irytuję Cię to, że taki model (oparty na dziedziczeniu) jest albo
> bardzo rozbudowany, a tym samym jego zwinność i zdolność adaptacji jest
> na poziomie nosorożca - albo jest bardzo ograniczony a jego
> funkcjonalność nie jest wystarczająca?
Nie trafiony przykład. Nosorożec jest bardzo zwinnym i szybkim
zwierzęciem jak na swoją masę. W otwartym terenie człowiek ma z nim
niewielkie szanse.
Ale wracając do tematu. Do dyspozycji jest szereg mechanizmów, nie tylko
dziedziczenie. Faktycznie gdy za bardzo rozbudujemy klasy to się to robi
nieładne, ale od czego jest dekompozycja. Natomiast bardziej od
rozbudowanych klas i wielopiętrowych dziedziczeń utrudniają pisanie
zawiłe powiązania pomiędzy modułami. Poza wykluczeniem cykli z
zależności teraz koncentruję się na minimalizacji powiązań. Mówiąc
obrazowo: sekcja uses powinna być w części interface jak najmniejsza, w
części implementation pusta. Aby to uzyskać w programie składającym się
z kilkuset modułów wykorzystuje się wszelkie dostępne narzędzia.
Co do zdolności adaptacji powiem tylko tyle: ten sam kod pracuje na
różnych systemach operacyjnych, różnych procesorach, stacjonarnie i
przez przeglądarkę. Znasz bardziej widowiskową możliwość adaptacji?
--
Darek
> Nie zrozum mnie źle. Nic nie mam przeciwko class helpers jako takim.
> Faktycznie w niektórych przypadkach mogą być pomocne, szczególnie gdy
> nie można w określony sposób rozbudować klasy bazowej. Natomiast dla
> mnie ważna jest ścisła kontrola typów, deklaruję co można zrobić z
> poszczególnymi danymi a kompilator pilnuje abym tego nie przekroczył.
> Każde narzędzie i metodę, która zwiększa precyzję definiowania i
> ogranicza sposoby wywołania (a tym samym zmniejsza prawdopodobieństwo
> wystąpienia błędu) witam z otwartymi ramionami. Helpers zmieniają sposób
> działania klasy poza miejscem gdzie jest ona implementowana, co jest w
> pewnym sensie łamaniem kontraktu. Jest to wygodne, ale może prowadzić do
> nadużyć.
Ja też byłem niechętnie nastawiony do class helperów (traktowałem je
jako hacki). Jednak po ostatniej dyskusji na temat wzorca Obserwator,
wpakowałem taki wzorzec w class helper i przyznam, że korzystanie z
niego jest bardzo przyjemne. Wystarczy w klasie która ma być
obserwatorem lub obserwowaną dodać:
uses
ObserverHelper;
i już mogę korzystać z komunikacji pomiędzy obiektami. Nie cierpię
zbytnio z powodu braku (explicite) deklaracji interfejsów wymaganych
przez wzorzec. Jednocześnie logika wzorca zapisana jest tylko w jednym
miejscu czyli dobrze hermetyzowana.
--
pozdrawiam
Piotr
XLR250&bmw_f650_dakar
> Faktycznie w niektórych przypadkach mogą być pomocne, szczególnie gdy
> nie można w określony sposób rozbudować klasy bazowej.
Lub byłoby to bezzasadne - i właśnie do tego należy tego używać.
> Natomiast dla
> mnie ważna jest ścisła kontrola typów, deklaruję co można zrobić z
> poszczególnymi danymi a kompilator pilnuje abym tego nie przekroczył.
> Każde narzędzie i metodę, która zwiększa precyzję definiowania i
> ogranicza sposoby wywołania (a tym samym zmniejsza prawdopodobieństwo
> wystąpienia błędu) witam z otwartymi ramionami.
> Helpers zmieniają sposób
> działania klasy poza miejscem gdzie jest ona implementowana, co jest w
> pewnym sensie łamaniem kontraktu. Jest to wygodne, ale może prowadzić do
> nadużyć.
Tak, równie dobrze mógłbyś zabronić wykonywania Free na obiekcie który
jest agregowany w innym.
Robisz to?
Ja nie, bo uważam to za niepotrzebne - a można taki kod napisać.
Innymi słowy - mam w nosie utyskiwania obiektowych purystów nad łamaniem
kontraktu, jeżeli owo łamanie jest wygodne i działa pewnie.
A sam kontrakt to ja sobie zapewniam ciut inaczej niż na poziomie
kompilatora - konwencją i konfiguracją dopuszczającą działanie tylko
zgodnie z konwencją.
>> Przykład z mojego podwórka - obsługa DataSetów; nie ma czegoś takiego
>> jak bazowa forma do obsługi bazy danych, jest usługa która realizuje w/w
>> funkcjonalność i w zależności od potrzeb - jest do formy wstrzykiwana.
>>
>>>> To mam dla Ciebie inną odpowiedź. Po prostu zrób to i spróbuj uzywać.
>>>> Życzę powodzenia.
>>>>
>>>
>>> Nie muszę próbować. Po prostu to pracuje dla mnie od nastu lat.
>> I nie irytuję Cię to, że taki model (oparty na dziedziczeniu) jest albo
>> bardzo rozbudowany, a tym samym jego zwinność i zdolność adaptacji jest
>> na poziomie nosorożca - albo jest bardzo ograniczony a jego
>> funkcjonalność nie jest wystarczająca?
>
> Nie trafiony przykład. Nosorożec jest bardzo zwinnym i szybkim
> zwierzęciem jak na swoją masę.
Zwinnym? Raczej nie. Szybkim, owszem i do tego raczej marnie widzącym.
> W otwartym terenie człowiek ma z nim niewielkie szanse.
Nieuzbrojony człowiek ma marne szanse z większością zwierząt - i to nie
tylko tych z grupy Big Five.
> Ale wracając do tematu. Do dyspozycji jest szereg mechanizmów, nie tylko
> dziedziczenie. Faktycznie gdy za bardzo rozbudujemy klasy to się to robi
> nieładne, ale od czego jest dekompozycja.
Ano więc właśnie.
Dekompozycja to pierwsza iteracja takiego podejścia - napisać klasy tak,
aby nie stanowiły monolitu zrealizowanych za pomocą zależnych od siebie
klas. A zamiast tego zestaw luźno powiązanych obiektów.
A jak uzyskać to luźne powiązanie?
Ja wykorzystuję do tego celu dependency injection - po prostu.
> Natomiast bardziej od
> rozbudowanych klas i wielopiętrowych dziedziczeń utrudniają pisanie
> zawiłe powiązania pomiędzy modułami. Poza wykluczeniem cykli z
> zależności teraz koncentruję się na minimalizacji powiązań. Mówiąc
> obrazowo: sekcja uses powinna być w części interface jak najmniejsza, w
> części implementation pusta. Aby to uzyskać w programie składającym się
> z kilkuset modułów wykorzystuje się wszelkie dostępne narzędzia.
Czyli jakie to narzędzia - ciekawość bierze?
> Co do zdolności adaptacji powiem tylko tyle: ten sam kod pracuje na
> różnych systemach operacyjnych, różnych procesorach, stacjonarnie i
> przez przeglądarkę. Znasz bardziej widowiskową możliwość adaptacji?
Mam zupełnie inne potrzeby i z mojego punktu widzenia - znam.
Mnie nie interesuje wspracie "egzotycznych" platform, a implementacja
"egzotycznej" logiki minimalnym kosztem bez reimplmentacji core systemu.
--
wloochacz
Nie wiem czy można mówić o łamaniu kontraktu, bo - jak nazwa wskazuje -
jest to umowa, więc można się umówić, że obiekt agregowany w innym może
być z niego wyodrębniony (dajmy na to metodą Publicate), czyli
przenosimy odpowiedzialność za niego na inny obiekt. I wtedy takie Free
jest jak najbardziej w porządku, a puryści nawet nie pisną.
>> Natomiast bardziej od
>> rozbudowanych klas i wielopiętrowych dziedziczeń utrudniają pisanie
>> zawiłe powiązania pomiędzy modułami. Poza wykluczeniem cykli z
>> zależności teraz koncentruję się na minimalizacji powiązań. Mówiąc
>> obrazowo: sekcja uses powinna być w części interface jak najmniejsza, w
>> części implementation pusta. Aby to uzyskać w programie składającym się
>> z kilkuset modułów wykorzystuje się wszelkie dostępne narzędzia.
> Czyli jakie to narzędzia - ciekawość bierze?
Nie tylko Ciebie, bo wietrzę, że jest szansa - może nie tyle na rewizję
wiedzy - ale na skonfrontowanie jej z innym punktem widzenia - na pewno.
--
PaSkol
gdybym myślał inaczej, nie dyskutowałbym.
ale cytując:"co on wnosi i co zmienia"
> Tak samo jak wykorzystanie RTTI do tego celu - negowanie, że to jest o
> rząd wielkości wolniejsze uważam za śmiesze.
> Bo co to znaczy - zamiast 0,0001s będzie 0,001s?
Ale po co wykonywać setki linii kodu, jak można 3.
Po co pisać jakieś super inteligentne procedury, jak można w
konstruktorze zdefiniować wygląd formy, pole po polu, literalnie
> Otwierasz 1000 okien w ciągu sekundy?
Ależ nie rób z tego że jest wolniej (niewiele ale jednak) i wymaga
bardziej skomplikowanych bibliotek, zalety.
Przy okazji, w aplikacji webowej, gdy pracuje kilkadziesiąt użytkowników
może się zdarzyć.
>
>> Faktycznie w niektórych przypadkach mogą być pomocne, szczególnie gdy
>> nie można w określony sposób rozbudować klasy bazowej.
> Lub byłoby to bezzasadne - i właśnie do tego należy tego używać.
>
>> Natomiast dla
>> mnie ważna jest ścisła kontrola typów, deklaruję co można zrobić z
>> poszczególnymi danymi a kompilator pilnuje abym tego nie przekroczył.
>> Każde narzędzie i metodę, która zwiększa precyzję definiowania i
>> ogranicza sposoby wywołania (a tym samym zmniejsza prawdopodobieństwo
>> wystąpienia błędu) witam z otwartymi ramionami.
>
>> Helpers zmieniają sposób
>> działania klasy poza miejscem gdzie jest ona implementowana, co jest w
>> pewnym sensie łamaniem kontraktu. Jest to wygodne, ale może prowadzić do
>> nadużyć.
> Tak, równie dobrze mógłbyś zabronić wykonywania Free na obiekcie który
> jest agregowany w innym.
> Robisz to?
> Ja nie, bo uważam to za niepotrzebne - a można taki kod napisać.
Kontrola pamięci jest prosta, choć wielu utyskuje gdy nie ma
automatycznego odśmiecania. Natomiast tego typu zagadnień są tysiące,
samodzielnie każde trywialne, ale w sumie stanowią o niezawodności i
rozszerzalności kodu.
> Innymi słowy - mam w nosie utyskiwania obiektowych purystów nad łamaniem
> kontraktu, jeżeli owo łamanie jest wygodne i działa pewnie.
> A sam kontrakt to ja sobie zapewniam ciut inaczej niż na poziomie
> kompilatora - konwencją i konfiguracją dopuszczającą działanie tylko
> zgodnie z konwencją.
Rób jak chcesz, ale przyjmij że inni uważają że można inaczej bez utraty
elastyczności. Dla Ciebie jeżeli w kodzie nie ma bardziej zaawansowanych
(czytaj bardziej zawiłych) rozwiązań to to jest nie ważne. Ja wolę
prosty kod, im bardziej trywialny, im mniej powiązań tym lepiej.
Wprowadzanie ograniczeń (np typami), zakaz chodzenia "na skróty" bo tak
wygodniej tylko z pozoru powoduje więcej pracy. Zwraca się z nawiązką
przy wprowadzaniu nowej logiki biznesowej (a ludzka głupota jest
nieograniczona). Zauważyłem to właśnie przy przechodzeniu na inne
platformy, bo to wymusiło wstępne wyczyszczenie kodu. A wprowadzanie na
większą skalę współbieżności to już jest piekło. tam każda linia kodu
musi być "koszerna". Teraz widzę, że im czystszy kod tym rozbudowa
prostsza i szybsza, niezależnie od aspektu, wg którego następuje.
--
Darek
>>> Natomiast bardziej od
>>> rozbudowanych klas i wielopiętrowych dziedziczeń utrudniają pisanie
>>> zawiłe powiązania pomiędzy modułami. Poza wykluczeniem cykli z
>>> zależności teraz koncentruję się na minimalizacji powiązań. Mówiąc
>>> obrazowo: sekcja uses powinna być w części interface jak najmniejsza, w
>>> części implementation pusta. Aby to uzyskać w programie składającym się
>>> z kilkuset modułów wykorzystuje się wszelkie dostępne narzędzia.
>> Czyli jakie to narzędzia - ciekawość bierze?
>
> Nie tylko Ciebie, bo wietrzę, że jest szansa - może nie tyle na rewizję
> wiedzy - ale na skonfrontowanie jej z innym punktem widzenia - na pewno.
Obym się mylił, ale mam zgoła przeciwne wyobrażenia ;-)
--
wloochacz
>> Tak samo jak wykorzystanie RTTI do tego celu - negowanie, że to jest o
>> rząd wielkości wolniejsze uważam za śmiesze.
>> Bo co to znaczy - zamiast 0,0001s będzie 0,001s?
>
> Ale po co wykonywać setki linii kodu, jak można 3.
Ale jakie setki?
> Po co pisać jakieś super inteligentne procedury, jak można w
> konstruktorze zdefiniować wygląd formy, pole po polu, literalnie
No właśnie po to, żeby *nie* definiować literalnie wyglądu formy pole po
polu.
BTW - poważnie tak to robisz??
Odrzucam na wejściu takie rozwiązanie, bo:
1) Nic nie daje w sensie "dobra" dla systemu czy jego architektury.
2) Jest nudne jak flaki z olejem - a programista *musi* się rozwijać.
3) Jest czasochłonne, a refaktoring tego musi być naprawdę "zabawny".
>> Otwierasz 1000 okien w ciągu sekundy?
>
> Ależ nie rób z tego że jest wolniej (niewiele ale jednak) i wymaga
> bardziej skomplikowanych bibliotek, zalety.
Ooook... ale ja nie rozumiem tego zdania.
> Przy okazji, w aplikacji webowej, gdy pracuje kilkadziesiąt użytkowników
> może się zdarzyć.
A czym rożni się aplikacja webowa od desktopwej, w której pracuje
kilkudziesięciu użytkowników?
W żadnym przypadku nie dojdzie do sytuacji, kiedy zażąda się od systemu
otwarcia takiej ilości okien - przynajmniej nie w moim wyobrażeniu.
No i czym jest okno w aplikacji webowej - formularzem?
>>> Faktycznie w niektórych przypadkach mogą być pomocne, szczególnie gdy
>>> nie można w określony sposób rozbudować klasy bazowej.
>> Lub byłoby to bezzasadne - i właśnie do tego należy tego używać.
>>
>>> Natomiast dla
>>> mnie ważna jest ścisła kontrola typów, deklaruję co można zrobić z
>>> poszczególnymi danymi a kompilator pilnuje abym tego nie przekroczył.
>>> Każde narzędzie i metodę, która zwiększa precyzję definiowania i
>>> ogranicza sposoby wywołania (a tym samym zmniejsza prawdopodobieństwo
>>> wystąpienia błędu) witam z otwartymi ramionami.
>>
>>> Helpers zmieniają sposób
>>> działania klasy poza miejscem gdzie jest ona implementowana, co jest w
>>> pewnym sensie łamaniem kontraktu. Jest to wygodne, ale może prowadzić do
>>> nadużyć.
>> Tak, równie dobrze mógłbyś zabronić wykonywania Free na obiekcie który
>> jest agregowany w innym.
>> Robisz to?
>> Ja nie, bo uważam to za niepotrzebne - a można taki kod napisać.
>
> Kontrola pamięci jest prosta, choć wielu utyskuje gdy nie ma
> automatycznego odśmiecania. Natomiast tego typu zagadnień są tysiące,
> samodzielnie każde trywialne, ale w sumie stanowią o niezawodności i
> rozszerzalności kodu.
Ciekawe - mam to samo na myśli, ale dlaczego uważasz że moje podejście
nie jest niezawodne i rozszerzalne?
Mam dokładnie odwrotne przypuszczenia - to właśnie moje podejście jest
niezawodne i rozszerzalne, ponieważ używam dokładnie zdefiniowanych
klas, dokładnie przetestowanych i zaimplementowanych.
Różnica jest taka, że ja dopuszczam tworzenie zależności pomiędzy
klasami w czasie wykonania - nie kompilacji.
>> Innymi słowy - mam w nosie utyskiwania obiektowych purystów nad łamaniem
>> kontraktu, jeżeli owo łamanie jest wygodne i działa pewnie.
>
>
>> A sam kontrakt to ja sobie zapewniam ciut inaczej niż na poziomie
>> kompilatora - konwencją i konfiguracją dopuszczającą działanie tylko
>> zgodnie z konwencją.
>
>
> Rób jak chcesz, ale przyjmij że inni uważają że można inaczej bez utraty
> elastyczności.
A niech sobie inni uważają co chcą - mnie interesuje dlaczego a nie co
uważają.
Po raz kolejny umykasz, kiedy dostaniesz konkretne pytanie o implementację.
Dlaczego?
Pokaż, że można inaczej bez utraty elastyczności - to, że tak napisałeś
nie robi na mnie wrażenia. Najmniejszego.
Z drugiej jednak strony, wiem że na Tobie również nie robią wrażenie
takie zaczepki i, że wcale nie musisz niczego udowadniać.
A więc mamy impas - tylko po co w ogóle pisać o tym?
> Dla Ciebie jeżeli w kodzie nie ma bardziej zaawansowanych
> (czytaj bardziej zawiłych) rozwiązań to to jest nie ważne.
:)
Wiesz jak mówią - wszystko jest proste jak to umiesz.
Skoro Ty tego nie czujesz i nie potrafisz, jest to dla Ciebie zawiłe.
Dla mnie odwrotnie - pozwala mi uzyskać elastyczność, a kod wcale nie
jest zawiły/zaawansowany, imo.
On po prostu wygląda "ciut" inaczej niż się człowiek przyzwyczaił do
tego co w Delphi jest.
I oczywiście wywraca do góry nogami wszelkie *Delphiowe* podejście - co
akurat uważam za zabawne.
Jest to moja próba praktycznego rozwiązania dwóch podstawowych
problemów; Code reusability i Code
> Ja wolę
> prosty kod, im bardziej trywialny, im mniej powiązań tym lepiej.
No to Ci próbuję powiedzieć - kompozycja = powiązanie.
Wstrzykiwanie zależności = brak powiązań, a właściwie "loose coupling".
> Wprowadzanie ograniczeń (np typami), zakaz chodzenia "na skróty" bo tak
> wygodniej tylko z pozoru powoduje więcej pracy.
Nadinterpretacja.
Class Helper jest wygodny - ale czy to jest chodzenie na skróty, które
ma oddźwięk jednoznacznie pejoratywny?
Nie - zdecydowanie nie.
> Zwraca się z nawiązką
> przy wprowadzaniu nowej logiki biznesowej (a ludzka głupota jest
> nieograniczona).
Na temat wprowadzania nowej logiki biznesowej lub modyfikowania
istniejącej bez dotykania (przez dotykanie mam na myśli np. literalną
definicję formy w kodzie lub redeklarację (nawet przez dziedziczenie)
istniejącej klasy w celu umożliwienia agregacji obiektu niosącego nową
logikę) pozostałych części systemu, to można dyskutować naprawdę długo.
> Zauważyłem to właśnie przy przechodzeniu na inne
> platformy, bo to wymusiło wstępne wyczyszczenie kodu. A wprowadzanie na
> większą skalę współbieżności to już jest piekło. tam każda linia kodu
> musi być "koszerna". Teraz widzę, że im czystszy kod tym rozbudowa
> prostsza i szybsza, niezależnie od aspektu, wg którego następuje.
Wszystko prawda - ale dlaczego zakładasz, że mój kod nie jest czysty a
za to jest zbyt skomplikowany?
--
wloochacz
Po prostu ściemniasz i tyle. Najpierw piszesz, że "w tCustomForm zadeklaruję
funkcję klasową", potem że "wystarcza wprowadzenie pośredniej klasy tCustomForm".
Nie ma czegoś takiego, jak "pośrednia klasa tCustomForm". Albo jest TCustomForm
zadeklarowane w module Forms, albo jest klasa po niej dziedzicząca. Nawet jak ją sobie
też nazwiesz TCustomForm, to będzie to już klasa potomna po Forms.TCustomForm
i będzie dostępna tylko dla Twoich formularzy (bo będzie zadeklarowana w Twoim unicie),
a wszystkie inne formularze dostępne w VCL lub w pakietach obcych firm będą nadal
dziedziczyć po Forms.TCustomForm i dla nich ta funkcja nie będzie dostępna.
Niestety, wychodzi na to, że sam nie wiesz, jak działa Twój kod.
Po pierwsze, jak w konstruktorze formularza użyjesz Fail, to możesz zapomnieć
o dalszej kontroli nad swoim programem, bo pójdzie on w maliny, a nowszych
wersjach Delphi dostaniesz błąd już na etapie kompilacji.
Jak w konstruktorze zgłosisz wyjątek, to możesz to nadal kontrolować, choć jak
już napisał Ci Arivald, konstruktor powinien być tak napisany, żeby zawsze
zwrócił skonstruowany obiekt.
Jeśli jednak w konstruktorze wystąpi wyjątek, to Twoja metoda Done się nie
wykona, a sterowanie zostanie przekazane na poziom, z którego konstruktor
został wywołany i tam możesz ten wyjątek wyłapać i obsłużyć, ale w takiej
sytuacji ani nie doczekasz się tam wskaźnika do utworzonego obiektu, ani
tym bardziej żadnego wyniku funkcji Done.
Wcześniej chciałem, żebyś swoje tezy poparł jakimś konkretnym przykładem,
ale widzę, że na konkrety, to się od Ciebie nie doczekam, więc sam napisałem
prosty przykład z użyciem Twojej metody Done. Jeśli nadal bronisz swojej tezy,
że linijka "if self=nil then exit;" (a także wcześniejsza "Result := -1;") jest w tej
metodzie potrzebna, to wpisz do konstruktora cokolwiek, co spowoduje,
że zobaczę komunikat "Self = nil".
Uwaga. FailUnit trzeba usunąć z auto-create forms.
unit FailUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TFailForm = class(TForm)
public
constructor Create(AOwner: TComponent); override;
function Done: Integer;
end;
implementation
{$R *.dfm}
constructor TFailForm.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
// Fail;
Abort;
end;
function TFailForm.Done: Integer;
begin
Result := -1;
if Self = nil then
begin
ShowMessage('Self = nil');
Exit;
end;
try
Result := ShowModal;
finally
Release;
end;
end;
Dla tych, którzy chcieliby uruchomić kompletny przykład podaję również
moje MainForm, z którego próbowałem tworzyć TFailForm.
unit MainUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TMainForm = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
end;
var
MainForm: TMainForm;
implementation
uses FailUnit;
{$R *.dfm}
procedure TMainForm.Button1Click(Sender: TObject);
var
Result: Integer;
begin
try
Result := TFailForm.Create(Application).Done;
ShowMessage('ModalResult is ' + IntToStr(Result));
except
ShowMessage('Utworzenie TFailForm nie powiodło się');
end;
end;
end.
No i tak jak te 70% gospodyń domowych nie widzisz różnicy pomiędzy
Forms.TCustomForm, a MojUnit.TCustomForm ;-)
Właśnie się dowiedziałam ze w Delphi jest instrukcja "fail". Zaszłość z
TurboPascala... I jak pięknie można nią zepsuć kod...
Teraz bardziej rozumiem Darka, po prostu zaczynał z Turbo Pascalem, albo
jeszcze czymś starszym. Jak to mówią, starego psa nie nauczysz nowych
sztuczek ;-)
Przy wykorzystaniu "fail" owszem konstruktor się nie wykona do końca,
pamięć zostanie zwolniona. A specyficzna cecha pseudo-zmiennej Self
pozwala na bezpieczne porównania jej do nil-a.
Jego kod zadziała... ale jak by mi ktoś takie coś zakomitował, to bym go
wywalił z wilczym biletem.
Albo sam bym odszedł z projektu/firmy. Trzeba szanować swoje zdrowie.
--
Arivald
To oczywiste, drogi Watsonie: Cache. ;-)
Metadane opisujące okna zmieniają się rzadko, pobierasz całość na
starcie programu. Lub na starcie tylko to co konieczne, plus to co
oznaczone tagiem "preload", a potem w tle dociągasz resztę.
Potem tylko dociągasz ewentualne zmiany.
--
Arivald
Sorry, ale jego kod nie zadziała dla żadnego formularza. Nie wprowadzaj ludzi
w błąd. Sprawdziłem to dzisiaj z ciekawości, więc wiem, co piszę. Fakt, że to
zaszłość, ale w Delphi XE dostaniesz do niej nawet stosowny komunikat błędu
na etapie kompilacji, a pod Delphi 6 program koncertowo idzie w maliny. Na
innych wersjach nie chciało mi się już sprawdzać.
Na D4 z wyłączoną optymalizacją działa. Po włączeniu optymalizacj AV
przy opuszczaniu konstruktora. Tyle że sprawdzalem na pustym obiekcie,
nie na formie.
Może On używa jeszcze starszej wersji Delphi?
--
Arivald
function DisplayModalForm(aFClass: TFormClass;
aOwner:TComponent):TModalResult;
var
F: TForm;
begin
Result := 0;
try
// Ważne, że konstruktor TForm.Create jest wirtualny,
// inaczej nie działałoby
F := aFClass.Create(aOwner);
Result := F.ShowModal;
finally
F.Free;
end;
end;
// przykład:
MR := DisplayModalForm(TMyFavoriteForm, NIL);
Ewentualnie można jako dodatkowe parametry umieścić metody podpinane do
zdarzeń OnCreate, OnDestroy itp.
Na pewno staroświeckie i mało odkrywcze, ale działa.
Do czasu, aż w konstruktorze nie wystąpi wyjątek, bo wtedy
nastąpi zwolnienie obiektu (F.Free), który nie został utworzony,
a zmienna F ma nadal wartość nieokreśloną, więc dostaniemy AV.
Bo pusty obiekt (TObject) nie ma żadnych pól inicjowanych w konstruktorze.
Dokąd nie odwołujesz się do jakiegoś pola obiektu, ani do VMT, to nil nie
przeszkadza i można wywołać F.NiewirtualnaMetodaObiektu, nawet gdy F = nil.
> Może On używa jeszcze starszej wersji Delphi?
Nie sądzę ;-)
Pisze na kilka platform i z tego co pamiętam to chyba we Free Pascalu,
więc może tam ta konstrukcja działa.
--
PaSkol
Wy to jednak w cuda wierzycie ;-)
Weź przeczytaj jeszcze raz ze zrozumieniem, to czego nie zacytowałeś
z mojej poprzedniej wypowiedzi, zobacz sobie jak wygląda konstruktor
TCustomForm i zastanów się, jak ma się on wykonać, gdy Self będzie nil.
Nie chce mi się :). Tak sobie po prostu z rana gdybam ;). W sumie mnie
to obojętne.
--
PaSkol
O wypraszam sobie :P. Ja też zaczynałem z TP (3.0 i 5.0) i nowych
sztuczek uczę się bez problemu ;).
--
PaSkol
Niestety, na razie wychodzi na Twoje :(.
--
PaSkol
>> Po co pisać jakieś super inteligentne procedury, jak można w
>> konstruktorze zdefiniować wygląd formy, pole po polu, literalnie
> No właśnie po to, żeby *nie* definiować literalnie wyglądu formy pole po
> polu.
> BTW - poważnie tak to robisz??
> Odrzucam na wejściu takie rozwiązanie, bo:
> 1) Nic nie daje w sensie "dobra" dla systemu czy jego architektury.
> 2) Jest nudne jak flaki z olejem - a programista *musi* się rozwijać.
> 3) Jest czasochłonne, a refaktoring tego musi być naprawdę "zabawny".
(2) w tym wypadku jest już kwestią gustu, chociaż akurat tu mamy gusty
kompatybilne :). Przy tej okazji spróbuję jednak zapytać (licząc na to,
że właśnie _masz_ czas) czy definiujesz położenie literalnie, czy jednak
bardziej orientacyjnie? Te wszystkie pola na formularzu jednak jakoś
muszą być umieszczone. O ile idea, aby formularz sam się budował, bo
chcemy, aby wyświetlił dane i te dane to właściwie prawie wszystko co
musi wiedzieć, aby się zbudować. O tyle do tego "prawie" brakuje mu
właśnie ułożenia pól.
>> Ależ nie rób z tego że jest wolniej (niewiele ale jednak) i wymaga
>> bardziej skomplikowanych bibliotek, zalety.
> Ooook... ale ja nie rozumiem tego zdania.
No szyk z lekka awangardowy :). Chyba chodziło o to, abyś nie robił
zalety z tego, że co jest wolniejsze i wymaga bardziej skomplikowanych
bibliotek. Bo adwersarz te cechy uznaje za wady. Faktem jest, że nie
uwzględnia tutaj kwestii, iż nawet jeśli jest dłużej, nigdy nie zostanie
zauważone, natomiast przez to, że u niego jest szybciej, każda zmiana
paradoksalnie może wymagać więcej czasu na wprowadzenie i nie jest tak
elastyczna, bo jak wynika z mojej dotychczasowej analizy wątku, gdyby
chcieć zadowolić dwóch klientów o przeciwnych potrzebach, należałoby
przygotować kod w obu wersjach, a nie po prostu dostosować (jak to
uprościłeś) konfigurację u każdego z nich (przy _zerowych_ zmianach w
kodzie).
--
PaSkol
Nie ściemniam, tylko nadużyłem skrótów myślowych
i zapomniałem że istnieje forms.tCustomForms
przepraszam
Najpierw piszesz, że "w tCustomForm
> zadeklaruję
> funkcję klasową", potem że "wystarcza wprowadzenie pośredniej klasy
> tCustomForm".
>
> Nie ma czegoś takiego, jak "pośrednia klasa tCustomForm". Albo jest
> TCustomForm
> zadeklarowane w module Forms, albo jest klasa po niej dziedzicząca.
> Nawet jak ją sobie
> też nazwiesz TCustomForm, to będzie to już klasa potomna po
> Forms.TCustomForm
> i będzie dostępna tylko dla Twoich formularzy (bo będzie zadeklarowana w
> Twoim unicie),
Właśnie o takim przypadku mówię MyForms.tCustomForm dziedziczące po tForms
> a wszystkie inne formularze dostępne w VCL lub w pakietach obcych firm
> będą nadal
> dziedziczyć po Forms.TCustomForm i dla nich ta funkcja nie będzie dostępna.
>
tego nie oczekuję, a co więcej nie powinno się zdarzyć że następuje
zmiana API podstawowych klas poza unitami je definiującymi
o tym właśnie mówię pisząc że class helpers jest niebezpieczny.
Mechanizm podobny jak przy WIDTH, nagle może zostać wywołana zupełnie
inna funkcja gdy pokryją się identyfikatory,
Ograniczasz się w analizie tylko do formularzy, do tego tworzonych
wizualnie, dlatego ciężko się rozmawia.
jeszcze raz konstrukcja
begin
tMyObject.create(myData).done(myFinish);
end;
jest bardzo elastyczna,
w zależności od potrzeb definiujemy nowy konstruktor, możemy nadpisać
virtualnie done, przekazać typ klasowy do wywołania
samo użycie to jedna linia, nawet nie musi być definiowana zmienna w
sekcji VAR
najciekawsze jest możliwość zdefiniowania lokalnie nowej klasy, gdzie
implementujemy tylko i wyłącznie jedną metodę - kontruktor, a reszta
leci wg schematu. Tego mi brakuje w Twoim przykładzie z class helpers.
używam tego zarówno do formularzy jak i innych obiektów.
Ale ponieważ nie używamy jawnie destruktora to możemy też myśleć w
innych kategoriach o konstruktorze
self jest w tym przypadku jedną z metod przekazywania danej
tak samo jak wyjątek czy pole, i tyle. Jak to zostanie uzyskane to inna
sprawa.
--
Darek
Podałem już dwa przykłady,
przy pierwszym rozmowa zeszła na trzeciorzędne elementy (wydajność RTTI)
do drugiego nikt się nie odniósł
--
Darek
wg mnie nie, bo muszę stosować nietrywialne mechanizmy do konstrukcji
obiektu
zmienia podejście do rozwiązywania podobnych problemów.
z tym zgoda, tylko oczekuję określenia zakresu stosowalności, bo w
zestawieniu z moim przykładem ograniczona jest swoboda wołania
konstruktora
>
>>> Tak samo jak wykorzystanie RTTI do tego celu - negowanie, że to jest o
>>> rząd wielkości wolniejsze uważam za śmiesze.
>>> Bo co to znaczy - zamiast 0,0001s będzie 0,001s?
>>
>> Ale po co wykonywać setki linii kodu, jak można 3.
> Ale jakie setki?
z biblioteki , RTTI samo się nie zrobi, a z tego co wiem nie jest to
rozwiązywane na etapie kompilacji tylko przy wykonaniu
>
>> Po co pisać jakieś super inteligentne procedury, jak można w
>> konstruktorze zdefiniować wygląd formy, pole po polu, literalnie
> No właśnie po to, żeby *nie* definiować literalnie wyglądu formy pole po
> polu.
> BTW - poważnie tak to robisz??
TAK
> Odrzucam na wejściu takie rozwiązanie, bo:
> 1) Nic nie daje w sensie "dobra" dla systemu czy jego architektury.
> 2) Jest nudne jak flaki z olejem - a programista *musi* się rozwijać.
> 3) Jest czasochłonne, a refaktoring tego musi być naprawdę "zabawny".
wydaje Ci się, jak by było tak jak mówisz nie zbudował bym tej wielkości
systemu jaki mam
>
>>> Otwierasz 1000 okien w ciągu sekundy?
>>
>> Ależ nie rób z tego że jest wolniej (niewiele ale jednak) i wymaga
>> bardziej skomplikowanych bibliotek, zalety.
> Ooook... ale ja nie rozumiem tego zdania.
>
>> Przy okazji, w aplikacji webowej, gdy pracuje kilkadziesiąt użytkowników
>> może się zdarzyć.
> A czym rożni się aplikacja webowa od desktopwej, w której pracuje
> kilkudziesięciu użytkowników?
tym że wszystkie procesy dotychczas odpalone na kilkudziesięciu
maszynach, pracują na jednej - serwerze. Oczywiście nie wszystko robią
bo tylko generują kod HTML, ale jednak
> W żadnym przypadku nie dojdzie do sytuacji, kiedy zażąda się od systemu
> otwarcia takiej ilości okien - przynajmniej nie w moim wyobrażeniu.
> No i czym jest okno w aplikacji webowej - formularzem?
>
Tak. W części "biznesowej" u mnie nie ma różnicy pomiędzy oknem w
Windows a oknem webowym (z punktu widzenia API)
>> Kontrola pamięci jest prosta, choć wielu utyskuje gdy nie ma
>> automatycznego odśmiecania. Natomiast tego typu zagadnień są tysiące,
>> samodzielnie każde trywialne, ale w sumie stanowią o niezawodności i
>> rozszerzalności kodu.
> Ciekawe - mam to samo na myśli, ale dlaczego uważasz że moje podejście
> nie jest niezawodne i rozszerzalne?
> Mam dokładnie odwrotne przypuszczenia - to właśnie moje podejście jest
> niezawodne i rozszerzalne, ponieważ używam dokładnie zdefiniowanych
> klas, dokładnie przetestowanych i zaimplementowanych.
> Różnica jest taka, że ja dopuszczam tworzenie zależności pomiędzy
> klasami w czasie wykonania - nie kompilacji.
>
To nie jest kwestia dopuszczalności tylko ograniczania.
Nie ma czegoś takiego jak klasa dokładnie przetestowana i
zaimplementowana. To mrzonka.
>>> Innymi słowy - mam w nosie utyskiwania obiektowych purystów nad łamaniem
>>> kontraktu, jeżeli owo łamanie jest wygodne i działa pewnie.
>>
>>
>>> A sam kontrakt to ja sobie zapewniam ciut inaczej niż na poziomie
>>> kompilatora - konwencją i konfiguracją dopuszczającą działanie tylko
>>> zgodnie z konwencją.
>>
>>
>> Rób jak chcesz, ale przyjmij że inni uważają że można inaczej bez utraty
>> elastyczności.
> A niech sobie inni uważają co chcą - mnie interesuje dlaczego a nie co
> uważają.
> Po raz kolejny umykasz, kiedy dostaniesz konkretne pytanie o implementację.
> Dlaczego?
> Pokaż, że można inaczej bez utraty elastyczności - to, że tak napisałeś
> nie robi na mnie wrażenia. Najmniejszego.
Możemy porównać jeszcze raz od początku rozwiązanie z done i z class
helpers, ale licząc ile różnych błędów można popełnić i ile kodu napisać
aby uzyskać to samo.
> Z drugiej jednak strony, wiem że na Tobie również nie robią wrażenie
> takie zaczepki i, że wcale nie musisz niczego udowadniać.
> A więc mamy impas - tylko po co w ogóle pisać o tym?
>
TO jest czasami jedyna metoda dowiedzenia się czegoś nowego.
>> Dla Ciebie jeżeli w kodzie nie ma bardziej zaawansowanych
>> (czytaj bardziej zawiłych) rozwiązań to to jest nie ważne.
> :)
> Wiesz jak mówią - wszystko jest proste jak to umiesz.
> Skoro Ty tego nie czujesz i nie potrafisz, jest to dla Ciebie zawiłe.
> Dla mnie odwrotnie - pozwala mi uzyskać elastyczność, a kod wcale nie
> jest zawiły/zaawansowany, imo.
> On po prostu wygląda "ciut" inaczej niż się człowiek przyzwyczaił do
> tego co w Delphi jest.
> I oczywiście wywraca do góry nogami wszelkie *Delphiowe* podejście - co
> akurat uważam za zabawne.
Dziwne, bo jak widzisz to u kogoś innego to reagujesz alergicznie (patrz
punkty 1,2,3)
> Jest to moja próba praktycznego rozwiązania dwóch podstawowych
> problemów; Code reusability i Code
>
>> Ja wolę
>> prosty kod, im bardziej trywialny, im mniej powiązań tym lepiej.
> No to Ci próbuję powiedzieć - kompozycja = powiązanie.
> Wstrzykiwanie zależności = brak powiązań, a właściwie "loose coupling".
>
>> Wprowadzanie ograniczeń (np typami), zakaz chodzenia "na skróty" bo tak
>> wygodniej tylko z pozoru powoduje więcej pracy.
> Nadinterpretacja.
> Class Helper jest wygodny - ale czy to jest chodzenie na skróty, które
> ma oddźwięk jednoznacznie pejoratywny?
Powiedz czym class helpers różni się od zwykłej metody w klasie. Wg mnie
prawie niczym, oprócz tego że jest zdefiniowane poza głównym modułem.
Czyli jeżeli możemy go zdefiniować w głównym to tam powinniśmy to
zrobić. A kiedy nie możemy: Ty mi odpowiedz.
> Nie - zdecydowanie nie.
>
>> Zwraca się z nawiązką
>> przy wprowadzaniu nowej logiki biznesowej (a ludzka głupota jest
>> nieograniczona).
> Na temat wprowadzania nowej logiki biznesowej lub modyfikowania
> istniejącej bez dotykania (przez dotykanie mam na myśli np. literalną
> definicję formy w kodzie lub redeklarację (nawet przez dziedziczenie)
> istniejącej klasy w celu umożliwienia agregacji obiektu niosącego nową
> logikę) pozostałych części systemu, to można dyskutować naprawdę długo.
>
>> Zauważyłem to właśnie przy przechodzeniu na inne
>> platformy, bo to wymusiło wstępne wyczyszczenie kodu. A wprowadzanie na
>> większą skalę współbieżności to już jest piekło. tam każda linia kodu
>> musi być "koszerna". Teraz widzę, że im czystszy kod tym rozbudowa
>> prostsza i szybsza, niezależnie od aspektu, wg którego następuje.
> Wszystko prawda - ale dlaczego zakładasz, że mój kod nie jest czysty a
> za to jest zbyt skomplikowany?
>
--
Darek
A ja Ci po raz kolejny napiszę, że nie znasz, albo nie rozumiesz jak działa class helper.
> Mechanizm podobny jak przy WIDTH, nagle może zostać wywołana zupełnie inna funkcja gdy pokryją się
> identyfikatory,
Class helper nie wprowadza żadnych zmian do deklaracji klas, nie następuje żadna zmiana
API, nie ma też łamania kontraktu, jak to niektórzy wcześniej sugerowali, to tylko eleganckie
rozszerzenie składni. Wszystko, co można elegancko zapisać przy pomocy class helpera
da się również uzyskać bez niego, ale wtedy trzeba deklarować globalne funkcje/procedury
albo używać wytrychów typu: type TCustomForm = (Forms.TCustomForm). Class helper
pozwala Ci to zapisać przy pomocy składni obiektowej Object.Done zamiast proceduralnej
Done(Object).
I jeszcze jedno: nie ma możliwości wywołania zupełnie innej funkcji, bo identyfikatory nie
mogą się pokryć (chyba, że użyjesz dyrektywy overload), bo nad tym czuwa sam kompilator.
Poza tym, żeby skorzystać z class helpera musisz jego unit umieścić na liście uses. Samo
zdefiniowanie class helpera nie wystarczy.
>> Po pierwsze, jak w konstruktorze formularza użyjesz Fail, to możesz zapomnieć
>> o dalszej kontroli nad swoim programem, bo pójdzie on w maliny, a nowszych
>> wersjach Delphi dostaniesz błąd już na etapie kompilacji.
>
> Ograniczasz się w analizie tylko do formularzy, do tego tworzonych wizualnie, dlatego ciężko się
> rozmawia.
No fakt, ciężko się rozmawia z kimś, kto nie zauważa, że w tej odnodze tego wątku
już od kilku maili próbujemy ustalić, czy konstruktor formularza może w jakikolwiek
sposób zwrócić nam nil.
> jeszcze raz konstrukcja
>
>
> begin
> tMyObject.create(myData).done(myFinish);
> end;
>
> jest bardzo elastyczna,
Ale ja się z tym zgadzam. Dla kogoś, kto definiuje własne konstruktory formularzy,
rozdzielenie etapu tworzenia komponentu przez konstruktor od etapu pokazania
tego formularza przez ShowModal jest zupełnie naturalne.
> w zależności od potrzeb definiujemy nowy konstruktor, możemy nadpisać virtualnie done, przekazać
> typ klasowy do wywołania
> samo użycie to jedna linia, nawet nie musi być definiowana zmienna w sekcji VAR
Nie chcę tu dyskutować o sposobie przekazywania parametrów do tworzonego
formularza, bo tych jest całe mnóstwo i każdy to może robić po swojemu.
> najciekawsze jest możliwość zdefiniowania lokalnie nowej klasy, gdzie implementujemy tylko i
> wyłącznie jedną metodę - kontruktor, a reszta leci wg schematu. Tego mi brakuje w Twoim
> przykładzie z class helpers.
Ale ja Ci od razu napisałem, że ten class helper służy do wywołania domyślnego
konstruktora, za to działa z KAŻDYM formularzem, nie tylko Twoim własnym i taki
był cel napisania tego class helpera. Dla przeciążonego konstruktora potrzebna
będzie przeciążona wersja ShowModalNewForm z innymi parametrami.
Ale nie wszyscy tworzą od razu nowy konstruktor, żeby przykazać do formularza
parametry. Można to zrobić na wiele sposobów. Można nawet przyjąć zasadę,
że to konstruktor jest zawsze w domyślnej postaci, a parametry przykazujemy
w ShowModalNewForm pisząc w danej klasie jej przeciążoną wersję. Można
nawet w tym samym wywołaniu odebrać wynik działania takiego formularza
zamiast zwracać ModalResult. Kwestia indywidualnego podejścia.
> używam tego zarówno do formularzy jak i innych obiektów.
> Ale ponieważ nie używamy jawnie destruktora to możemy też myśleć w innych kategoriach o
> konstruktorze
> self jest w tym przypadku jedną z metod przekazywania danej
> tak samo jak wyjątek czy pole, i tyle. Jak to zostanie uzyskane to inna sprawa.
No i właśnie o to Self rozbija się sprawa od kilku maili w tej odnodze tego wątku.
Po prostu usuń te dwie pierwsze linijki z Twojej metody Done, albo pokaż nam
wszystkim, że są one do czegokolwiek potrzebne.
Dokładnie tak. Wtedy w ogóle nie potrzebujemy class helpera. Nie powinni go np.
używać twórcy komponentów, bo mogą to samo zdefiniować od razu w odpowiedniej
klasie komponentu
> A kiedy nie możemy:
Kiedy nie możemy zmodyfikować danej klasy, czyli (jak już napisałem wiele KB temu)
dla wszystkich klas VCLa (i obcych komponentów, których nie powinniśmy zmieniać).
> Te wszystkie pola na formularzu jednak jakoś
> muszą być umieszczone. O ile idea, aby formularz sam się budował, bo
> chcemy, aby wyświetlił dane i te dane to właściwie prawie wszystko co
> musi wiedzieć, aby się zbudować. O tyle do tego "prawie" brakuje mu
> właśnie ułożenia pól.
No co ja będę tłumaczył - lepiej pokażę:
http://tv.devexpress.com/#VCLGridLayoutView;ExpressQuantumGrid+Suite.product;1
W stosunku do filmu, różnice u mnie są dwie:
1) Całość dzieje się w runtime a nie w IDE.
2) Dotyczy formularza w sensie okna, a nie widoku grida - dla tego jakoś
nie znalazłem zastosowania - jeszcze.
Reasumując:
1) Kontrolki tworzą się automatycznie na podstawie zdefiniowanego
"środowiska" jednoznacznie definiującego dane (upraszczając - DataSety,
które ze sobą powiązane). A więc wiem jakie kontrolki mają się utworzyć
z jakimi typami danych, dla jakich pól mają być zbindowane i jak mają
być udekorowane (Caption, Format, Słownik, Lookup, itd.)
2) Automat tworzący kontrolki bierze pod uwagę predefiniowany układ
"szpaltowy".
3) Często/gęsto, taki automatyczny layout nie nadaje się do przekazania
użytkownikowi, dlatego też trzeba go poprawić w runtime i tak poprawiony
zapisać jako domyślny. Układ domyślny jest ładowany dla każdego
użytkownika, który nie ma zdefiniowanego własnego układu konkretnego
formularza.
4) Synchronizacja; jeżeli dodam nowe pole, które nie ma odzwierciedlenia
w layout, zostanie automatycznie stworzone, ale nigdzie nie pokazane -
będzie niewidoczne. I analogicznie, kiedy usunę pole z metadanych -
kontrolka zostanie usunięta.
5) Jak łatwo zauważyć - każdy user może mieć własny layout okna, ale
wszystkie działają tak samo w sensie przetwarzania danych, logiki,
walidacji itd.
Taki jest plan, bo to na razie jest na etapie proof-of-concept :D
Harmonogram zakłada realizację w/w postulatów na pierwszy tydzień
kwietnia. A jak będzie - zobaczymy, za to zdeterminowanyjestem aby
właśnie tak się to odbywało.
/ciach/
--
wloochacz
albo rozszerzyć składnię w potomku
>
> I jeszcze jedno: nie ma możliwości wywołania zupełnie innej funkcji, bo
> identyfikatory nie
> mogą się pokryć (chyba, że użyjesz dyrektywy overload), bo nad tym czuwa
> sam kompilator.
a co będzie (pytam) jeżeli w potomku będzie kolizja nazw,
wtedy zależnie od tego czy wymienimy moduł definiujący czy też nie,
program się będzie kompilował ale da zupełnie inne wyniki?
> Poza tym, żeby skorzystać z class helpera musisz jego unit umieścić na
> liście uses. Samo
> zdefiniowanie class helpera nie wystarczy.
>
>>> Po pierwsze, jak w konstruktorze formularza użyjesz Fail, to możesz
>>> zapomnieć
>>> o dalszej kontroli nad swoim programem, bo pójdzie on w maliny, a
>>> nowszych
>>> wersjach Delphi dostaniesz błąd już na etapie kompilacji.
>>
>> Ograniczasz się w analizie tylko do formularzy, do tego tworzonych
>> wizualnie, dlatego ciężko się rozmawia.
>
> No fakt, ciężko się rozmawia z kimś, kto nie zauważa, że w tej odnodze
> tego wątku
> już od kilku maili próbujemy ustalić, czy konstruktor formularza może w
> jakikolwiek
> sposób zwrócić nam nil.
A co ciężkiego jest w dyskusji, jakby nie było problemu to i dyskusji
również. Problem jest gdy założenia albo sposób oceny jest niespójny.
Poza tym sam zmieniłeś kontekst wątku. Pytałem się w czym Twoja
propozycja jest lepsza, w szczególności w kontekście konstruktora. A Ty
kpisz ze mnie i wytykasz nieistotne szczegóły. Problem z self prawie
nigdy nie wystąpi, więc czy sporna linia jest czy jej nie ma ...
>
>> jeszcze raz konstrukcja
>>
>>
>> begin
>> tMyObject.create(myData).done(myFinish);
>> end;
>>
>> jest bardzo elastyczna,
>
> Ale ja się z tym zgadzam. Dla kogoś, kto definiuje własne konstruktory
> formularzy,
> rozdzielenie etapu tworzenia komponentu przez konstruktor od etapu
> pokazania
> tego formularza przez ShowModal jest zupełnie naturalne.
>
>> najciekawsze jest możliwość zdefiniowania lokalnie nowej klasy, gdzie
>> implementujemy tylko i wyłącznie jedną metodę - kontruktor, a reszta
>> leci wg schematu. Tego mi brakuje w Twoim przykładzie z class helpers.
>
> Ale ja Ci od razu napisałem, że ten class helper służy do wywołania
> domyślnego
> konstruktora, za to działa z KAŻDYM formularzem, nie tylko Twoim własnym
> i taki
> był cel napisania tego class helpera. Dla przeciążonego konstruktora
> potrzebna
> będzie przeciążona wersja ShowModalNewForm z innymi parametrami.
To poproszę o jeszcze jedno wyjaśnienie.
Mam dwa formularze tForm1 i tForm2, jak mam je zdefiniować (mają różną
zawartość, albo np tylko kolor tła i nie muszą mieć dodatkowych
parametrów), aby móc wykorzystać Twoje ShowModalnewForm,
tForm1.ShowModalNewForm(application);
class function TCustomFormHelper.ShowModalNewForm(AOwner: TComponent =
nil): Integer;
begin
with TCustomForm(NewInstance).Create(AOwner) do
try
Result := ShowModal;
finally
Release;
end;
end;
Bo dla mnie instancja zawsze będzie typu tCustomForm, albo musi być
wprowadzona detekcja klasy na rzecz której zostało to wywołane.
>
> Ale nie wszyscy tworzą od razu nowy konstruktor, żeby przykazać do
> formularza
> parametry. Można to zrobić na wiele sposobów. Można nawet przyjąć zasadę,
> że to konstruktor jest zawsze w domyślnej postaci, a parametry przykazujemy
> w ShowModalNewForm pisząc w danej klasie jej przeciążoną wersję. Można
> nawet w tym samym wywołaniu odebrać wynik działania takiego formularza
> zamiast zwracać ModalResult. Kwestia indywidualnego podejścia.
Ależ właśnie o to chodzi. Czy to ma być class helper, czy zwykła metoda,
to nie ma znaczenie. Kluczowe dla jest właśnie rozdzielenie na
konstruktor i połączenie reszty czyli destruktora z trywialnym działaniem
czyli piszemy:
class procedure TCustomFormHelper.ShowModalFree;
begin
try
Result := ShowModal;
finally
Release;
end;
end;
i używamy
tFormA.create(AOwner).ShowModalFree;
>
>> używam tego zarówno do formularzy jak i innych obiektów.
>> Ale ponieważ nie używamy jawnie destruktora to możemy też myśleć w
>> innych kategoriach o konstruktorze
>> self jest w tym przypadku jedną z metod przekazywania danej
>> tak samo jak wyjątek czy pole, i tyle. Jak to zostanie uzyskane to
>> inna sprawa.
>
> No i właśnie o to Self rozbija się sprawa od kilku maili w tej odnodze
> tego wątku.
> Po prostu usuń te dwie pierwsze linijki z Twojej metody Done, albo pokaż
> nam
> wszystkim, że są one do czegokolwiek potrzebne.
Dobra sprawdziłem. Faktycznie nie mam nigdzie Fail (można się było
pokusić o użycie gdy brakuje pamięci, ale ostatnio inaczej w takim
przypadku reaguję: po prostu program powinien natychmiast zakończyć
działanie, bo inaczej napsuje nie wiadomo co i gdzie)
przyczyna umieszczenia jest nieco inna, dokładnie taka jak Arivald pisał
kilka wątków temu. Done ma zastępować free (czyli nie tylko w
jednolinijkowych wywołaniach ale również
begin
tm:=nil;
....
if a =1 then begin
tm:=tMyObject.create;
tm.prop1:=1;
end;
....
tm.done
end;
--
Darek
>> A kiedy nie możemy:
>
> Kiedy nie możemy zmodyfikować danej klasy, czyli (jak już napisałem
> wiele KB temu)
> dla wszystkich klas VCLa (i obcych komponentów, których nie powinniśmy
> zmieniać).
>
Piszesz to tak, jakby każde rozszerzenie wymagało modyfikacji źródeł
Forms itp. A przecież można stosować zwyczajne dziedziczenie, co też
jest formą modyfikacji klasy. Więc ponawiam pytanie: kiedy nie możemy
rozszerzyć tForm do tMyCustomForm, które będzie klasą bazową dla
tworzonych formatek? Czy to ograniczenie jest powodowane tylko przez
komponenty firm trzecich (które są "przywiązane" do tForm) czy też
istnieje jeszcze inne ograniczenie.
--
Darek
Weźcie skończcie już tą bezproduktywną dyskusję...
Obaj używacie innej semantyki, te same słowa mają dla was inne
znaczenie... Się nie porozumiecie...
--
Arivald
Czytając poprzedni post Darka doszedłem do tego samego wniosku co Ty.
Z mojej strony EOT, bo nie będę się kopał z koniem ...
> To oczywiscie najprostsze i logiczne ale mam watpliwosci czy u wloochacza
> tak wlasnie jest - widzilem jego soft, widzilem wyrywki kodu, montuje sobie
> w glowie urywki tego co pisze tu, na grupie .
> Jesli na budowe okna sklada sie kilka metadanych, zapisanych chyba w xmlu i
> to niekoniecznie w formie czystych property dla obiektow ale cos mi sie
> zdaje ze czasami cale obiekty leca z bazy (moze sie myle ale
> ja mam w jednym programie wszytsko co jest na formach zapisane jako
> strumienie w bazie tak jak to robi delphi w plikach dpr i chyba wloochacz
> tez stosuje serializacje calych obiektow)
Tak jak napisałeś - serializacja/deserializacja całych obiektów,
trzymana w XML (a ten w pliku i w bazie danych - zależy co).
Na jedno okienko takich obiektów może być i kilkanaście.
> to przy setkach okien ( setkach
> potencjalnych widookow tych samych okien bo jak sam wloochacz kiedys
> napisal - okien ma az 5 ;)) ) troche tych danych jest i raczej sobie nie
> wyobrazam aby tak poprostu sciagal to wszytsko na starcie programu.
Nie, nie ściągam.
Ściągam tylko to co jest dokładnie potrzebne w danej chwili, a to co już
zostało ściągnięte - jest keszowane w pamięci.
To są małe obiekty i nie zajmują jej dużo.
> A nawet jesli tak jest to jakos trzeba tym zarzadzac -
No i jest to zarządzane - automatycznie.
> u mnie byloby to
> proste - ja mam w najwiekszym projekcie moze ze 100 okien (widokow bo wiele
> okien jest reorganizowane na starcie) a przeciez wiem ze niektorzy z was
> tworza aplikacje gdzie tych potencjalnych widokow sa wrecz tysiace.
Myślę, że będzie tego ponad 10k.
Ale to naprawdę nie jest żaden problem, jeżeli idzie o wydajność.
Są zaprojektowane pewne mechanizmy predykcji tych danych, ale póki co
nie zostały wprowadzone do kodu produkcyjnego.
Z dwóch powodów:
1) Bo nie ma takiej potrzeby, po prostu wszystko działa akceptowalne
wydajnie.
2) Bo nie ma czasu na robienie tego co nie jest w tej chwili niezbędne :)
> A tak jak pisalem wyzej - user moze czekac godzinami na dane ale jak okno
> nie pojawi sie od razu to mu sie rece zaciskaja na kilofie ;))))
Nie, nie - tak źle to nie ma.
A jak będzie, to pobieranie danych będzie przerzucone do wątków
pobocznych - w tej chwili tak się dzieje tylko na wyraźne żądanie
developera dla funkcjonalności, które po prostu trochę trwają
(zestawienia raporty itd.).
--
wloochacz
Ale nie upieram się, popraw mnie, jeśli się mylę. Swoją droga warto
byłoby przeprowadzić eksperyment w nowych wersjach Delphi, definiując
formularz, w którym konstruktor celowo wali się przed końcem.
A jeśli się mylę, to jak sobie poradzić z problemem, o którym piszesz?
Nie na temat...
Po za tym "Create zwraca NIL" jest oczywistą nieprawdą, co już w tym
wątku zostało opisane dość dokładnie.
Krzysztofowi chodzi o to ze jeśli w Create() będzie wyjątek, to
wykonywanie zostanie przerwane jeszcze przed przypisaniem do zmiennej F,
a sama zmienna nie jest w żaden sposób inicjalizowana.
> Ale nie upieram się, popraw mnie, jeśli się mylę. Swoją droga warto
> byłoby przeprowadzić eksperyment w nowych wersjach Delphi, definiując
> formularz, w którym konstruktor celowo wali się przed końcem.
>
> A jeśli się mylę, to jak sobie poradzić z problemem, o którym piszesz?
Dla potomności, dwie poprawne wersje kodu:
function DisplayModalForm(aFClass: TFormClass;
aOwner:TComponent):TModalResult;
var
F: TForm;
begin
f := nil; <--- inicjalizacja do bezpiecznej wartości
try
F := aFClass.Create(aOwner);
Result := F.ShowModal;
finally
F.Free;
end;
end;
lub
function DisplayModalForm(aFClass: TFormClass;
aOwner:TComponent):TModalResult;
var
F: TForm;
begin
F := aFClass.Create(aOwner);
try <-- try za przypisaniem, ergo Free zostanie wywołane dla
przypisanej zmiennej.
Result := F.ShowModal;
finally
F.Free;
end;
end;
i trzecia wersja, już nie tak ładna ale też działająca:
function DisplayModalForm(aFClass: TFormClass;
aOwner:TComponent):TModalResult;
begin
with aFClass.Create(aOwner) do
try
Result := ShowModal;
finally
Free;
end;
end;
Zauważ że z każdej wersji usunąłem "Result := 0;" zakładam że wiesz
dlaczego ;-)
--
Arivald
W dowolnej wersji Delphi propagacja wyjątku na wyższy poziom wywołania
zawsze wygląda tak samo. Nie licz na to, że konstruktor lub funkcja zwrócą
jakiś wynik. Wyjątek zawsze przerywa normalne wykonywanie instrukcji, a
sterowanie jest przekazane od najbliższego bloku finally end lub except end.
Po drodze są jedynie zwalniane lokalne zmienne (zaalokowane na stosie)
o kontrolowanym czasie życia (zobacz: lifetime-managed types).
> A jeśli się mylę, to jak sobie poradzić z problemem, o którym piszesz?
Arivald mnie wyręczył i poprawił Twój kod nawet na trzy różne sposoby.
Od siebie mogę jedynie dodać, że pierwsza wersja jest typowa dla
sposobu pisania przez Arivalda i bazuje na tym, że Free można
wykonać nawet gdy F = nil (ja nie polecam), druga wersja jest wersją
prawie "podręcznikową" (zalecaną do stosowania zawsze), a trzecia
wersja, jest wersją optymalną pod względem generowanego kodu
wynikowego, ale niektórzy mają awersję do stosowania "with". Ja
osobiście używałbym trzeciej wersji, bo nie mam awersji do "with".
Druga uwaga dotyczy stosowania Free do zwalniania formularzy.
Arivald poprawiał Twój kod, więc zostawił Free, ale do zwolnienia
formularza powinno się stosować Release, a nie Free. Powód
jest taki, że przy zamykaniu formularza w kolejce komunikatów
mogą jeszcze zostać niepobrane komunikaty przeznaczone dla
tego formularza. Free powoduje natychmiastowe zniszczenie
formularza, a Release wysyła do kolejki komunikat CM_RELEASE
i zniszczenie formularza nastąpi dopiero po odebraniu wszystkich
wcześniejszych komunikatów przeznaczonych dla tego okna.
Takie podejście jest najbardziej bezpieczne i ogólnie zalecane.
I tu dochodzimy do sedna sprawy. Chcąc używać Release, nie
możemy użyć pierwszej wersji podanej przez Arivalda, bo dla
F = nil dostalibyśmy AV przy wywołaniu Release. Zostaje nam
wersja druga lub trzecia.
Ja nie mam awersji do stosowania "with", szczególnie przy
prostych i czytelnych blokach instrukcji, więc moja wersja
(przy Twojej konwencji wywołania) wyglądałaby tak:
function DisplayModalForm(FormClass: TFormClass; AOwner:TComponent):TModalResult;
begin
with FormClass.Create(AOwner) do
try
Result := ShowModal;
finally
Release;
end;
end;
jedna drobna wątpliwość:
Czy fakt, że w konstruktorze nie wystąpi wyjątek, gwarantuje, że
zwrócony przez konstruktor wynik będzie różny od NIL? Wychowałem się na
Turbo Pascalu i jestem na to wrażliwy. Choć prawdę mówiąc nie wiem (nie
zastanawiałem się), czy w Delphi taki przypadek jest możliwy.
Ponadto optowałbym za tym, aby procedura DisplayModalForm nie
wypuszczała nieobsłużonego wyjątku konstruktora. W przypadku wystapienia
wyjątku w konstruktorze powinien być wyświetlony komunikat (zamiast
formularza) i zwrócony wynik MR_CANCEL. Moim zdaniem tak będzie
wygodniej, lecz kwestia gustu. Ostatecznie tę kwestię można regulować
dodatkowym parametrem boolowskim.
Natomiast wyjątek w ShowModal może być utożsamiany z wyjątkiem w funkcji
DisplayModalForm, więc taki wyjątek należy bezwzględnie wypuścić.
function DisplayModalForm(FormClass: TFormClass;
> AOwner:TComponent; aCaptureExceptions:Boolean = TRUE):TModalResult;
var
F: TForm;
begin
Result := MR_CANCEL;
F := NIL // na wszelki wypadek, w każdym razie nie zaszkodzi ;
try
F := FormClass.Create(AOwner)
except
F := NIL;
if aCaptureExceptions then
begin
.... tu stosowny komunikat .....
end
else
begin
raise;
end;
end;
if F <> NIL then
begin
try
Result := F.ShowModal;
finally
F.Release;
end;
end;
end;
Co do with, tez nie mam do niej awersji, zwłaszcza w kontekście
anonimowych rekordów zwracanych jako wynik funkcji.
A znasz jakąś metodę na zmianę tego wyniku?
Tak naprawdę ten wskaźnik zwracany z kontruktore jest ustalany w
TObject.NewInstance(), i to jest jedyne miejsce w trakcie konstukcji
gdzie możesz go zmienić. Możesz takze zwrócić nil'a, ale to skutkuje
dostaniem AV. Dlaczego to sprawdź w System._ClassCreate().
> Ponadto optowałbym za tym, aby procedura DisplayModalForm nie
> wypuszczała nieobsłużonego wyjątku konstruktora.
A czemu? Przecież duża część inicjalizacji zazwyczaj idzie w
FormCreate(), które jest wołane wewnątrz konstruktora.
> W przypadku wystapienia
> wyjątku w konstruktorze powinien być wyświetlony komunikat (zamiast
> formularza)
No, to by było dopiero... Po prostu cofanie się w rozwoju...
Po to są wyjątki aby z nich korzystać.
Zresztą Twój przykład realizacji Twojej metody jest już wystarczająco
dobrym anty-wzorcem...
--
Arivald
To poczytaj sobie Cały ten wątek. Ja na ten temat już nie zamierzam się odzywać.
> Ponadto optowałbym za tym, aby procedura DisplayModalForm nie wypuszczała nieobsłużonego wyjątku
> konstruktora. W przypadku wystapienia wyjątku w konstruktorze powinien być wyświetlony komunikat
> (zamiast formularza) i zwrócony wynik MR_CANCEL. Moim zdaniem tak będzie wygodniej, lecz kwestia
> gustu. Ostatecznie tę kwestię można regulować dodatkowym parametrem boolowskim.
Właściwie to Twoja funkcja, więc możesz zrobić w niej co zechcesz, tylko napisz
mi po co, skoro i tak nie potrafisz w tym miejscu w żaden sensowny sposób
obsłużyć takiego wyjątku, a stosowny komunikat i tak się pokaże, tylko poźniej.
Dodatkowo (poprzez wynik mrCancel) chcesz utożsamić wystąpienie wyjątku
w konstruktorze z rezygnacją użytkownika z wykonania jakiejś akcji, a przecież
to nie to samo.
> Natomiast wyjątek w ShowModal może być utożsamiany z wyjątkiem w funkcji DisplayModalForm, więc
> taki wyjątek należy bezwzględnie wypuścić.
A co za różnica (z punktu widzenia użytkownika tej funkcji), czy wyjątek nastąpił w
konstruktorze, czy w ShowModal?
Czyli implementation dependent i nie ma gwarancji, że tak będzie zawsze.
>
>
>> Ponadto optowałbym za tym, aby procedura DisplayModalForm nie
>> wypuszczała nieobsłużonego wyjątku konstruktora.
Gdy wyjątek wystąpi w konstruktorze, nie ma w ogóle formularza/dialogu.
gdy wystąpi w ShowModal, formularz przez chwilę jest, potem się
załamuje, być może użytkownik kliknął już OK - tego nie można odpuścić
>
>> W przypadku wystapienia
>> wyjątku w konstruktorze powinien być wyświetlony komunikat (zamiast
>> formularza)
>
> No, to by było dopiero... Po prostu cofanie się w rozwoju...
> Po to są wyjątki aby z nich korzystać.
Często jest tak, że w pewnej procedurze chcę załatwić pewną sprawę i ze
względu na jej pomniejsze znaczenie nie chcę przejmować się jej
wyjątkami (jeśli dialog będzie tylko info o programie, które zamyka się
przez OK). Oczywiście najczęściej jest odwrotnie, więc dlatego mój
mechanizm jest OPCJONALNY (ewentualnie można zmienić na FALSE domyślną
wartość parametru).
> Zresztą Twój przykład realizacji Twojej metody jest już wystarczająco
> dobrym anty-wzorcem...
>
Dziękuję za uznanie. Też się już nie będę odzywał w tym wątku.
Nie mam siły przebrnąć przez cały wątek i zacząłem od końca - na oko nie
powinno to działać, bo nie możesz w procedurach klasowych odwoływać się
do Self... No chyba, że właśnie wynalazłem koło :P
jh
Możesz, tylko Self oznacza wtedy klasę a nie instancję obiektu. ;-)
--
z pozdrowieniami
Adam Siwoń
> Obiecałem, że nie będę się w tym wątku odzywał, i istotnie nie mam nic
> do powiedzenia ad meritum. Jestem natomiast pod wrażeniem kierunku, w
> którym wątek się rozwinął: neuron pytał o kwestię przeniesienia zmiennej
> reprezentującej formę z poziomu globalnego na lokalny (i tyle), a w
> wątku pojawiły się kwestie: class helperów (a propos, może ktoś
> zaproponować sensowny polski odpowiednik?),
Proszę, nie!
> metod klasowych, injekcji,
> ba: nawet modelowania, ktoś nawet proponował trickową podmianę Self'a.
> Gdyby w Delphi było metaprogramowanie (jak np. w Ruby) to wątek byłby
Da się zrobić :P
> pewnie z dziesięć razy większy.
Fajnie, prawda? :)
Ale to nie pierwszy taki przypadek - i mam nadzieję, że nie ostatni.
W sumie - to pewność mam ;-)
--
wloochacz
> wątku pojawiły się kwestie: class helperów (a propos, może ktoś
> zaproponować sensowny polski odpowiednik?),
klasyczny pomocnik ???
spaaaaać.....
--
pozdrawiam,
Przemysław Osmański, SoftSYSTEM
www.soft-system.pl
www.kochamjedzenie.pl - portal dla ludzi którzy kochają jedzenie
Zabawa słowami? Wchodzę w to!!!
Pomoc klasowa ;).
--
PaSkol
:-)
Tygrys
Klasownik :-)
--
pozdrawiam
Norbert
klasopomoc (od słynnego samopomoc)
lub
klasowspomagacz ;)
--
PaSkol
;)
Tygrys
A propos metaprogramowania - da się zrobić nawet w Delphi:
1. W swoim kodzie generujesz plik zawierający źródło DLL-ki
2. Wywołujesz kompilator i kompilujesz ten plik
3. Wykorzystujesz DLL-kę za pomocą LoadLibrary/GetProcAddress
4. Usuwasz plik DLL-ki, gdy niepotrzebna.
Niestety, w punkcie 2. potrzebny jest komponent, który nie jest
redistributable, więc nie na wiele się to przyda.
Może to i głupie, ale Prima Aprilis był tak niedawno ...
---------
A propos - w wydaniu .NET to wygląda inaczej, bo możesz sam tworzyć kod
zarządzany i go wykonywać, ale to inna bajka.
W delphi też można... Alokujesz pamięć jako blok kodu, odblokowujesz do
zapisu, zapisujesz bajty assemblera, blokujesz do zapisu i już możesz
wykonać taki kod.
W Delphi brakuje jedynie wsparcia języka to takich zabaw. Zresztą komu
by się chciało...
--
Arivald
>> A propos metaprogramowania - da się zrobić nawet w Delphi:
>>
>> 1. W swoim kodzie generujesz plik zawierający źródło DLL-ki
>> 2. Wywołujesz kompilator i kompilujesz ten plik
>> 3. Wykorzystujesz DLL-kę za pomocą LoadLibrary/GetProcAddress
>> 4. Usuwasz plik DLL-ki, gdy niepotrzebna.
>>
>> Niestety, w punkcie 2. potrzebny jest komponent, który nie jest
>> redistributable, więc nie na wiele się to przyda.
>>
>> Może to i głupie, ale Prima Aprilis był tak niedawno ...
>> ---------
>>
>> A propos - w wydaniu .NET to wygląda inaczej, bo możesz sam tworzyć kod
>> zarządzany i go wykonywać, ale to inna bajka.
>
> W delphi też można... Alokujesz pamięć jako blok kodu, odblokowujesz do
> zapisu, zapisujesz bajty assemblera, blokujesz do zapisu i już możesz
> wykonać taki kod.
>
> W Delphi brakuje jedynie wsparcia języka to takich zabaw. Zresztą komu
> by się chciało...
Do prawda - ale do takich zabaw, do jakich by to było przydatne to
proponowane rozwiązania są raczej z gatunku toooo havy...
Osobiście wykorzystuję mechanizm wstrzykiwania logiki oraz rozbudowany
model zdarzeń oparty o delegaty. Kod, który jest rejestrowany w tych
zdarzeniach ma wpływ na działanie innych obiektów programu - może go
również, niejako, zmienić.
Oczywiście nie jest to metaprogramowanie i nie jest tak uniwersalne, ale
do tego do czego ja to wykorzystuje - to mi wystarcza.
Problem tak naprawdę polegał na poprawnym zaprojektowaniu całej
architektury, która by to wszystko udźwignęła - bo naprawdę bardzo łatwo
wpaść tu w tzw. Event Hell...
Co do tworzenia kodu w locie; raczej idę w stronę języków skryptowych
osadzonych w aplikacji, a wygenerowany kod zapisany w skrypcie również
może być podłączony do zdarzeń obiektów aplikacji.
Można by tu nawet mówić o namiastce refleksji - oczywiście w ściśle
zdefiniowanym uniwersum obiektowym.
No bo ja jeszcze nie potrafię w Delphi utworzyć obiektu z klasy, która
jest zdefiniowana w locie i czytana np. z pliku - a nie ze skompilowanej
biblioteki.
--
wloochacz
Wiesz, wszystko zależy od potrzeb i stopnia determinacji :-)
Wieki temu, w zamierzchłych czasach DOSa, programów rezydentnych i kodowania
polskich znaków w Mazovii, popełniłem taki pakiet do polonizacji programów pod
nazwą "Polonica".
W pakiecie tym było do wyboru kilkadziesiąt programów dla różnych typów drukarek,
które analizowały dane wysyłane do drukarki i w odpowiednim momencie dokładały
do wydruku definicje polskich znaków przesyłając do drukarki odpowiednią matrycę
znaków z zachowaniem aktualnego kroju czcionki i przyjętego przez użytkownika
sposobu kodowania polskich znaków (Mazovia, Latin 2, itp.)
Ze względu na barierę 640 KB RAM taki program dla drukarki musiał być maksymalnie
mały, a jednocześnie musiał w sobie zawierać definicję polskich znaków dla kilku(nastu)
różnych krojów pisma i uwzględniać specyfikę różnych typów drukarek (9/24-igłowych,
atramentowych, laserowych).
I teraz możemy wrócić do metaprogramowania. Wszystkie te programy rezydentne
były niby napisane w assemblerze, ale źródłowy kod assemblera był tworzony przez
program napisany w Turbo Pacalu, który na podstawie listy rozkazów dostępnej
w danym modelu drukarki i matrycy czcionek zapisanych w osobnych plikach
generował plik *.asm, kompilował go i mógł go nawet od razu uruchomić instalując
w pamięci jako "rezydent".
Takie rozwiązanie pozwalało nie tylko na zachowanie jednolitego szkieletu dla wszystkich
programów rezydentnych, ale również, na umieszczenie w kodzie assemblera matryc
znaków w postaci skompresowanej, które były rozpakowywane dopiero przy wysyłaniu ich
do drukarki. Kompresja była wykonywana oczywiście w Turbo Pascalu, a do programu
assemblerowego trafiały już gotowe deklaracje danych wraz odpowiednio dobranymi
fragmentami kodu, który nimi zarządzał w zależności od listy rozkazów danej drukarki.
To tyle tych historycznych wspomnień z mojego (prawie) metaprogramowania ;-)
Do tego wystarczy chyba zsyntetysowanie obiektu RTTI opisującego klasę
(np. TFormClass dla TForm).
Kiedyś przeglądając Jedi widziałem tam kod do budowania RTTI od zera...
ale szczegółów nie pamiętam.
...
Jednak to nie Jedi.. miałem w zakładce:
http://koders.com/delphi/fidCAC223FB283AFE0A2B98683E4C060BC1A8DD3273.aspx?s=delphi#L41
Kod buduje "class of object" run-time. Ale ta wersja kodu ma 11 lat, i
działa w Delphi 3 i 4 ;-)
Podejrzewam że w nowszych też to się da zrobić, no ale trzeba sporo
wiedzy i informacji od Embracadero wyciągnąć. ;-)
Może samo Emracadero dało by się namówić na napisanie takiego kodu?
Myślę że to by się mogło przydać przy RPC czy WebSerwisach (budowanie
obiektów run-time).
--
Arivald
> [...]
>> No bo ja jeszcze nie potrafię w Delphi utworzyć obiektu z klasy, która
>> jest zdefiniowana w locie i czytana np. z pliku - a nie ze skompilowanej
>> biblioteki.
>
> Do tego wystarczy chyba zsyntetysowanie obiektu RTTI opisującego klasę
> (np. TFormClass dla TForm).
Tak - ale tu chodzi o stworzenia całkowicie nowej klasy od zera,
podobnie jak ją piszesz a potem kompilujesz.
> Kiedyś przeglądając Jedi widziałem tam kod do budowania RTTI od zera...
> ale szczegółów nie pamiętam.
> ...
> Jednak to nie Jedi.. miałem w zakładce:
> http://koders.com/delphi/fidCAC223FB283AFE0A2B98683E4C060BC1A8DD3273.aspx?s=delphi#L41
>
>
> Kod buduje "class of object" run-time. Ale ta wersja kodu ma 11 lat, i
> działa w Delphi 3 i 4 ;-)
>
> Podejrzewam że w nowszych też to się da zrobić, no ale trzeba sporo
> wiedzy i informacji od Embracadero wyciągnąć. ;-)
Taaa... i nawet wiem od kogo :D
W sumie tylko od jednego: Barry Kelly
Myślisz, że zadziała i mogę sobie np. w XMLu klase wyklikać?
Eee... fajnie by było :D
Sprawdzę - kiedyś :(
> Może samo Emracadero dało by się namówić na napisanie takiego kodu?
> Myślę że to by się mogło przydać przy RPC czy WebSerwisach (budowanie
> obiektów run-time).
Pewnie, że tak.
A poza tym pewnie i w setkach innych zastosowań...
BTW - pamiętasz dyskusję sprzed dwóch lat (chyba gdzieś tak) o formach
lokalnie modalnych?
Dzieliłeś się kodem i wskazywałeś na jego słabe strony.
Jakoś tak ostatnio wróciłem do tematu i wykorzystałem to:
http://andy.jgknet.de/blog/2010/12/using-fibers-for-tab-modal-forms/
On co prawda tam Frame wykorzystuje, ale to bez znaczenia.
Przepisałem sobie to lekko i wygląda na to, że działa dobrze - czyli
blokuje główny wątek i nie ma problemów z odkładaniem MessageLoop form
modalnych lokalnie na stosie.
Możesz Ci się przyda, a może coś zbaczysz w tym nie halo...
--
wloochacz
To się da zrobić... Ale to dużo pracy. I jakiś kompilator trzeba by mieć
wbudowany ;-)
Problemem mogło by być korzystanie z obiektów już skompilowanych w Exe,
bo do nich chyba nie ma wystarczająco dużo RTTI, informacji o funkcjach
publicznych, i chronionych dla dziedziczenia. Czy może nowe Delphi z
rozszerzonym RTTI to już ma?
Bo zrobienie klasy obiektu który tylko podmienia funkcje wirtualne czy
dynamiczne wydaje się być dość proste (o ile problem kompilacji się
rozwiąże).
Może wystarczyła by kompilacja skryptów Pascala JIT? (tak .NETowością
zalatuje ;-) )
> BTW - pamiętasz dyskusję sprzed dwóch lat (chyba gdzieś tak) o formach
> lokalnie modalnych?
Tak, tyle że u mnie to są modalne MDIchildy.
> Dzieliłeś się kodem i wskazywałeś na jego słabe strony.
> Jakoś tak ostatnio wróciłem do tematu i wykorzystałem to:
> http://andy.jgknet.de/blog/2010/12/using-fibers-for-tab-modal-forms/
> On co prawda tam Frame wykorzystuje, ale to bez znaczenia.
> Przepisałem sobie to lekko i wygląda na to, że działa dobrze - czyli
> blokuje główny wątek i nie ma problemów z odkładaniem MessageLoop form
> modalnych lokalnie na stosie.
> Możesz Ci się przyda, a może coś zbaczysz w tym nie halo...
Całkiem fjany pomysł. Właśnie się zapoznaję co to są te "fibers", ale
wygląda nieźle. Taka wielowątkowość w jednym wątku, czyli
cooperative-multitasking jak z Windows 3.1 ;-)
Ale przy jednowątkowym VCLu ta wada zaczyna być zaletą ;-)
--
Arivald
"Fibers" to ciekawa rzecz... Mozna się częściowo uwolnić od faktu ze
stos jest stosem.
Ale trochę niebezpieczna... Trzeba się naprawdę dobrze przed wyjątkami
zabezpieczyć, bo dojście do końca funkcji fiber oznacza automatycznie
zakończenie wątku... We wszystkich takich funkcjach trzeba pamiętać o
przełączeniu się na pierwszy fiber, ten zrobiony bezpośrednio z wątku.
--
Arivald