Autor Beitrag
Stephan.Woebbeking
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 97



BeitragVerfasst: Mo 15.04.13 08:45 
Hallo, ich habe eine Frage und bin grad ziemlich verwirrt, weil das Verhalten der IDE so gar nicht zu meinem Verständnis passt. Eines von beidem muss also korrigiert werden. ;)

Ich habe den folgenden Code:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
procedure TFormMain.MnuShowLicensesClick(Sender: TObject);
var
  grant: Boolean;
  store1, store2: TLicenseStore;
begin
  if ( Sender = MnuGrantLicenses ) then begin
    grant := True;
    store1 := TLicenseStore.Create( LICENSE_PW );
    store2 := TLicenseStore.Create( LICENSE_PW );
    FormLicenseStore.store := TLicenseStore.Create( LICENSE_PW );
  end else begin
    grant := False;
    FormLicenseStore.store := TLicenseStore.Instance;
  end;
  FormLicenseStore.allowGrant  := grant;
  FormLicenseStore.allowFileOp := grant;
  FormLicenseStore.ShowModal;
  if ( grant ) then
    FormLicenseStore.store.Free;
end;


Die Variablen store1 und store2 sind nur zum spielen hinzugefügt. Die Idee ist, dass TLicenseStore über eine Singleton Implementierung eine Instanz zur Verfügung stellt. Quasi die Instanz mit der ich "im Allgemeinen" in der Applikation arbeite. Allerdings brauche ich - eben in dieser Funktion - doch mal für eine begrenzte Zeit eine unabhängige Instanz. Die erzeuge ich lokal, nutze sie und werf sie dann wieder weg - siehe Code. Soweit auch alles einfach und gut (theoretisch).

Jetzt komme ich während der Laufzeit aber zu dem angehängten Bild. Dort erkennt man, dass der allgemeine Sourceteil (grant=False) durchlaufen wird. Und obwohl die Instanziierung von store1 nie durchlaufen wurde (Nur dieser eine Aufruf der Funktion nach Programmstart!), zeigt store1 auf gültige Werte. Und auch noch auf die meiner im TLicenseStore gespeicherten Instanz... ???? Jemand eine Idee?

Noch schlimmer: wenn ich den anderen Teil der Funktion nutze, arbeitet store1 immer noch mit der Singleton und löscht diese Systemweite am Ende - nicht etwa die temporäre Instanz??? Grauenhaft und ich hab derzeit keinen blassen Schimmer?

Achja, hier noch ein Auszug aus TLicenseStore:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
TLicenseStore = class
  private
    class var pLicStore: TLicenseStore;
    licenses:                 array of TLicenseFile;
    password:                 String;
    zipMaster:                TZipMaster19;
    function        GetCount: Integer;
    function        GetLicense( idx: Integer ): TLicenseFile;
    procedure       AddLicense( lic: TLicenseFile );
  public
    class function  Instance( password: String ) : TLicenseStore; overload;
    class function  Instance                     : TLicenseStore; overload;

    constructor     Create( password: String );
    destructor      Destroy; override;
    procedure       Clear;
    procedure       LoadLicenseFiles( folder: String );
    procedure       SaveLicenseFiles( folder: String );
    function        CheckPassword( password: String ): Boolean;
    procedure       SaveLicenseFile( folder: String; idx: Integer );
    function        CreateRequest( serial, customer1, customer2, computerSpecification: String; deviceType: TDeviceType ): Integer;
    function        FindSerial( serial: String ): Integer;
    function        CheckAccess( serial: String; deviceType: TDeviceType ): Boolean; overload;
    function        CheckAccess( serial: String; deviceType: TDeviceType; customer1: String ): Boolean; overload;
    procedure       GrantLicense( serial: String; licType: TLicType; retirement: TDateTime; transferable: Boolean );
    procedure       GrantUnlimitedLicense( serial: String; transferable: Boolean );
    procedure       GrantUnlimitedTransferableLicense( serial: String );
    property        LicenseFile[ idx: Integer ]: TLicenseFile read GetLicense; default;
    property        Count: Integer read GetCount;
    procedure       Sort( column: TLicSort );
end;


Danke,
Stephan

Add: Übrigens zeigt auch store2 auf EXAKT dieselbe Instanz soweit ich das erkennen kann...?
Add: Umbenennen der "Instance" Methoden bringt nichts.
Add: Aber: Wenn ich die pLicStore nicht als Klassenvariable sondern als globale Variable definiere, dann geht es???
Einloggen, um Attachments anzusehen!
Blup
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 175
Erhaltene Danke: 44



BeitragVerfasst: Di 16.04.13 08:38 
TLicenseStore darf kein Singelton sein, da in deiner Anwendung mehrere Instanzen benötigt werden.
Deshalb würde ich da nur die fachliche Logik implementieren.

Davon könnte man eine Klasse z.B. TGlobalLicenseStore ableiten, die zusätzlich die Funktionalität eines Singelton bereitstellt.
Diese merkt sich intern ob bereits eine Instanz erstellt wurde und gibt diese bei erneutem Aufruf des Konstruktors zurück.
Stephan.Woebbeking Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 97



BeitragVerfasst: Di 16.04.13 09:04 
Hi, danke für die Antwort,

ich verstehe allerdings noch immer nicht, wo das Problem entsteht. Mehrere Instanzen ok, dann lass uns das nicht mehr Singleton nennen, weil es ja kein "echtes" ist, das ist mir schon klar. Aber was spricht dagegen, eine "allgemeine" Instanz als Klassenvariable zu halten und trotzdem temporär "individuelle" Instanzen zuzulassen? Warum manipulieren diese individuellen Instanzen immer auch die allgemeine, das ist doch die Kernfrage?

Stephan
Blup
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 175
Erhaltene Danke: 44



BeitragVerfasst: Di 16.04.13 09:41 
Die Antwort ist recht einfach:
Die Klasse ist so implementiert, daß der Konstruktor nur beim ersten Aufruf eine Instanz erzeugt.
Bei jedem weiteren Aufruf wird nur die intern gespeicherte Instanz zurückgegeben.

Wenn ein andere Verhalten gewünscht wird, könnte man z.B. einen weiteren Konstruktor einführen, der jedes mal eine neue Instanz liefert, ohne die Klassenvariable zu beeinflussen.
Ich halte diesen Weg aber für ungünstiger.
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Di 16.04.13 14:29 
user profile iconBlup hat folgendes geschrieben Zum zitierten Posting springen:
Die Klasse ist so implementiert, daß der Konstruktor nur beim ersten Aufruf eine Instanz erzeugt.
Bei jedem weiteren Aufruf wird nur die intern gespeicherte Instanz zurückgegeben.
Eben gerade andersrum... Instance() gibt das Singleton zurück, Create() erstellt eine neue (würde ich aus dem geposteten Code vermuten).
Und das sollte so funktionieren, eigentlich. Bin mir mit class var allerdings nicht sicher, wie die genau funktionieren (also wessen private das ist). Ist das auch so, wenn du ein wirklich minimales Testprogramm baust?

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
Stephan.Woebbeking Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 97



BeitragVerfasst: Mi 17.04.13 10:08 
Hallo Martok, du hast es ziemlich gut erfasst... Ich sehe dort auch nirgendwo ein Problem, aber tatsächlich bekomme ich den Fehler in einer Rekonstruktion nicht hin. Vielleicht liegt der Fehler ganz wo anders; obwohl ich nicht den geringsten Ansatz sehe, wo das sein könnte...

Ich probier noch mit der Rekonstruktion, aber dort klappt alles so, wie es soll...

Stephan