AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Alternative zu Typecast => IInterface?

Ein Thema von bytecook · begonnen am 17. Nov 2016 · letzter Beitrag vom 17. Nov 2016
Antwort Antwort
Benutzerbild von bytecook
bytecook

Registriert seit: 6. Aug 2010
Ort: Dornbirn
151 Beiträge
 
Delphi 11 Alexandria
 
#1

Alternative zu Typecast => IInterface?

  Alt 17. Nov 2016, 09:40
Hallo Leute,

ich bin die letzten Wochen immer wieder hier im Forum auf Interfaces gestoßen, und frage mich, ob diese meinen Code sicherer/eleganter machen könnten...

Folgende Ausgangssituation:

Habe eine Basisklasse TSVFXCustomCmd. Dieser dient als Vorfahr verschiedener Klassen, die u.a unterschiedliche Routinen zur Verfügung stellen.

Die Klasse TSVFXVisualCmd deklariert beispielsweise eine Routine mit dem Namen Prozess(), die in den abgeleiteten Routinen überschrieben werden.

Alle diese Klassen von einer abgeleiteten Containerklasse vom TObjectList verwaltet (OwnsObject ist True). Keine Generics im Moment.

Klassenableitungen

TSVFXCustomCmd <= Basisklasse, verschiedene Properties
TSVFXVisualCmd <= abgeleitet von TSVFXCustomCmd, stellt verschiedene Basiskommandos zur Verfügung
TSVFXMoveTo <= abgeleitet von TSVFXVisualCmd, Pfadkommando
..
TSVFXQBezier <= abgeleitet von TSVFXVisualCmd, Pfadkommando
..
TSVFXCircle <= abgeleitet von TSVFXVisualCmd, Primitivekommando
..
TSVFXTransformCmd <= abgeleitet von TSVFXCustomCmd, stellt verschiedene Basiskommandos zur Verfügung
TSVFXScale <= abgeleitet von TSVFXTransformCmd
TSVFXRotate <= abgeleitet von TSVFXTransformCmd
..
TSVFXAnimateCmd <= abgeleitet von TSVFXCustomCmd, stellt verschiedene Basiskommandos zur Verfügung
..
TSVFXColorCmd <= abgeleitet von TSVFXCustomCmd, stellt verschiedene Basiskommandos zur Verfügung
..

Es kommen immer wieder neue Befehle hinzu, einige Routinen müssen jedesmal um Typecasts erweitert werden, das hätte ich mir gerne gespart.

Beim Abarbeiten der Objektliste werden objektspezifisch verschiedene Kommandos ausgeführt. Momentan vergleiche ich jedes Objekt mit einem Typecast gegen einen Objekttyp und starte dann die Routinen beispielsweise wie folgt:

Ausgangsszenario
Code:
 
 if (Self[i]) is TSVFXVisualCommand) then begin
   (** TSVFXMoveTo **)
   if Self[i] is TSVFXMoveTo then begin
     TSVFXMoveTo(Self[i]).Process (APath);
   end;
   ..
   ..
 end;
Wunschszenario
Code:
 
 if (Self[i]) is TSVFXVisualCommand) then
   TSVFXVisualCommand(Self[i])).Process (APath); <= abgeleiteter Befehl soll ausgeführt werden.
Diese Typecasts gegen KlassenTypen <TSVFXMoveTo(Self[i]).Process (APath);> hätte ich teilweise gerne vermieden. Bei einem Typecast gegen TSVFXVisualCommand wird verständlicherweise nicht die vom eigentlichen Objekt überschriebene Override Routine aufgerufen. Deklariere ich das Interface in der Klasse TSVFXVisualCommand, so muss ich das nach meinem derzeitigen Wissensstand auch dort implementieren, somit bringt dies nichts. Implementiere ich dieses Interface in den abgeleiteten Klassen, so lande ich wieder beim Typecast...

Höchstwahrscheinlich übersehe ich hier etwas wesentliches, bin generell auf dem Holzweg, oder Typecast ist und bleibt mein Ding...

Gruß,

Peter
Peter
Was mache ich, wenn ein Bär meine Frau angreift?
Nichts. Er hat sie angegriffen, soll er doch selber sehen, wie er sich verteidigt.
  Mit Zitat antworten Zitat
Der schöne Günther

Registriert seit: 6. Mär 2013
6.110 Beiträge
 
Delphi 10 Seattle Enterprise
 
#2

AW: Alternative zu Typecast => IInterface?

  Alt 17. Nov 2016, 10:00
Bei einem Typecast gegen TSVFXVisualCommand wird verständlicherweise nicht die vom eigentlichen Objekt überschriebene Override Routine aufgerufen.
Also entweder bin ich noch nicht lange genug wach, oder...

Ich verstehe nicht weshalb die ganzen Typecasts sein müssen. Du hast doch mal eine Oberklasse, das ist doch schon mal ordentlich was wert. Du kannst auf Unterklassen lustig typecasten, aber in der Implementierung wird immer die "unterste" aufgerufen:
Delphi-Quellcode:
program Project11;

{$APPTYPE CONSOLE}
{$R *.res}

type
   TBird = class
      public procedure makeNoise(); virtual;
   end;

   TDuck = class(TBird)
      public procedure makeNoise(); override;
   end;

   TCliffracer = class(TBird)
        public procedure makeNoise(); override;
    end;

procedure TCliffracer.makeNoise();
begin
   WriteLn('Skreeeeee');
end;

procedure TDuck.makeNoise;
begin
   WriteLn('Quack!');
end;

procedure TBird.makeNoise();
begin
   WriteLn( '(generic bird sounds)' );
end;

var
   bird: TBird;
begin
   bird := TDuck.Create();

   bird.makeNoise(); // Quack!
   TBird(bird).makeNoise(); // Quack!
   TDuck(bird).makeNoise(); // Quack!

   if (bird is TCliffracer) then
      TCliffracer(bird).makeNoise()
   else
      WriteLn('It''s not a cliffracer');

   readln;
end.
Was möchtest du jetzt mit Interfaces erreichen? Ich glaube mir müsste man das jetzt noch mal in einfachen Worten (am besten mit vielen Vokalen) erklären...
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.540 Beiträge
 
Delphi 11 Alexandria
 
#3

AW: Alternative zu Typecast => IInterface?

  Alt 17. Nov 2016, 10:01
Wieso solltest Du bei Verwendung von Interfaces wieder beim Typecast landen? Du musst doch lediglich ermitteln, ob die aktuelle Instanz ein bestimmtes Interface implementiert und das dann benutzen. Dafür musst Du noch nicht einmal eine bestimmte Klassenhierarchie einhalten, da der Verwender nur die Interfaces sieht und nicht die Klasse.
Delphi-Quellcode:
procedure TIrgendwas.Work(const Intf: IBasicInterface);
var
  FirstIntf: IFirstIntf;
  SecondIntf: ISecondIntf;
begin
  Intf.DoBasicWork;
  if Supports(Intf, IFirstIntf, FirstIntf) then
    FirstIntf.DoSomeWork;
  if Supports(Intf, ISecondIntf, SecondIntf) then
    SecondIntf.DoSomeOtherWork;
end;
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
Benutzerbild von bytecook
bytecook

Registriert seit: 6. Aug 2010
Ort: Dornbirn
151 Beiträge
 
Delphi 11 Alexandria
 
#4

AW: Alternative zu Typecast => IInterface?

  Alt 17. Nov 2016, 10:59
Wieso solltest Du bei Verwendung von Interfaces wieder beim Typecast landen? Du musst doch lediglich ermitteln, ob die aktuelle Instanz ein bestimmtes Interface implementiert und das dann benutzen. Dafür musst Du noch nicht einmal eine bestimmte Klassenhierarchie einhalten, da der Verwender nur die Interfaces sieht und nicht die Klasse.
Delphi-Quellcode:
procedure TIrgendwas.Work(const Intf: IBasicInterface);
var
  FirstIntf: IFirstIntf;
  SecondIntf: ISecondIntf;
begin
  Intf.DoBasicWork;
  if Supports(Intf, IFirstIntf, FirstIntf) then
    FirstIntf.DoSomeWork;
  if Supports(Intf, ISecondIntf, SecondIntf) then
    SecondIntf.DoSomeOtherWork;
end;
ha - if supports habe ich übersehen - das wars, danke!
Peter
Was mache ich, wenn ein Bär meine Frau angreift?
Nichts. Er hat sie angegriffen, soll er doch selber sehen, wie er sich verteidigt.
  Mit Zitat antworten Zitat
Benutzerbild von bytecook
bytecook

Registriert seit: 6. Aug 2010
Ort: Dornbirn
151 Beiträge
 
Delphi 11 Alexandria
 
#5

AW: Alternative zu Typecast => IInterface?

  Alt 17. Nov 2016, 11:15
Bei einem Typecast gegen TSVFXVisualCommand wird verständlicherweise nicht die vom eigentlichen Objekt überschriebene Override Routine aufgerufen.
Also entweder bin ich noch nicht lange genug wach, oder...

Ich verstehe nicht weshalb die ganzen Typecasts sein müssen. Du hast doch mal eine Oberklasse, das ist doch schon mal ordentlich was wert. Du kannst auf Unterklassen lustig typecasten, aber in der Implementierung wird immer die "unterste" aufgerufen:
Delphi-Quellcode:
program Project11;

{$APPTYPE CONSOLE}
{$R *.res}

type
   TBird = class
      public procedure makeNoise(); virtual;
   end;

   TDuck = class(TBird)
      public procedure makeNoise(); override;
   end;

   TCliffracer = class(TBird)
        public procedure makeNoise(); override;
    end;

procedure TCliffracer.makeNoise();
begin
   WriteLn('Skreeeeee');
end;

procedure TDuck.makeNoise;
begin
   WriteLn('Quack!');
end;

procedure TBird.makeNoise();
begin
   WriteLn( '(generic bird sounds)' );
end;

var
   bird: TBird;
begin
   bird := TDuck.Create();

   bird.makeNoise(); // Quack!
   TBird(bird).makeNoise(); // Quack!
   TDuck(bird).makeNoise(); // Quack!

   if (bird is TCliffracer) then
      TCliffracer(bird).makeNoise()
   else
      WriteLn('It''s not a cliffracer');

   readln;
end.
Was möchtest du jetzt mit Interfaces erreichen? Ich glaube mir müsste man das jetzt noch mal in einfachen Worten (am besten mit vielen Vokalen) erklären...
Weil ich die Kommandos in einer Objektliste (TSVFXCommands = Class(TObjectList)) halte, die als Items Objekte vom Typ TSVFXCustomCmd hält.
Je nach Objekttyp gibt es in der abgeleiteten Basisklasse unterschiedliche Befehle, die ausgeführt werden sollen.

Alle Ableitungen von TSVFXVisualCmd haben beispielsweise die Routine Process, alle Ableitungen von TSVFXAnimateCmd haben die Routine Animate
zur Verfügung. Je nach Parentklasse soll der Aufruf unterschieden werden. Wenn ich typengecasted TSVFXVisualCmd().Process aufrufe, wird jedoch explizit die Routine dieser Klasse aufgerufen, und nicht vom Nachfolger als überschrieben definierte Routine, weil ich ja nicht beispielsweise gegen TSVFXMoveTo caste. (Basisroutine ist als Virtual deklariert, Nachfolger überschreibt mittels Override).

Mit IInterface sollte ich nun das Problem eleganter umgehen können...
Peter
Was mache ich, wenn ein Bär meine Frau angreift?
Nichts. Er hat sie angegriffen, soll er doch selber sehen, wie er sich verteidigt.
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.336 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: Alternative zu Typecast => IInterface?

  Alt 17. Nov 2016, 11:27
Ich denke auch, wie vom Günther angedeutet, dass Du Deine Methoden nicht korrekt mit virtual und override deklariert hast.
(Dann sollte der Compiler Warnungen ausgeben im Sinne "Methode X der Klasse Y verbirgt Methode X der Basisklasse A" oder ähnliches)

Interfaces sind nützlich, bedingen aber auch einige Änderungen und zusätzlichen Aufwand.

Den kannst Du Dir evtl. sparen, wenn Du die Methoden der Basisklasse und der Ableitungen korrekt implementierst.

Zeig sonst evtl. mal Deine Klassendeklaration...
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)

Geändert von stahli (17. Nov 2016 um 11:29 Uhr)
  Mit Zitat antworten Zitat
Lemmy

Registriert seit: 8. Jun 2002
Ort: Berglen
2.366 Beiträge
 
Delphi 10.3 Rio
 
#7

AW: Alternative zu Typecast => IInterface?

  Alt 17. Nov 2016, 11:27
Mit IInterface sollte ich nun das Problem eleganter umgehen können...
nur zur Vorsicht gefragt: wie? IMHO hast Du einen Fehler in der Vererbungshierarchie und um den zu lösen brauchst Du nicht zwingend Interfaces.

Darüber hinaus würde ich dir empfehlen das Command-Pattern anzuschauen - bzw. wenn dir das zu abstrakt ist die TAction-Implementierung in Delphi. Wenn ich das richtig verstanden habe, willst / brauchst Du genau so was...
  Mit Zitat antworten Zitat
Benutzerbild von bytecook
bytecook

Registriert seit: 6. Aug 2010
Ort: Dornbirn
151 Beiträge
 
Delphi 11 Alexandria
 
#8

AW: Alternative zu Typecast => IInterface?

  Alt 17. Nov 2016, 12:39
Ich denke auch, wie vom Günther angedeutet, dass Du Deine Methoden nicht korrekt mit virtual und override deklariert hast.
(Dann sollte der Compiler Warnungen ausgeben im Sinne "Methode X der Klasse Y verbirgt Methode X der Basisklasse A" oder ähnliches)

Interfaces sind nützlich, bedingen aber auch einige Änderungen und zusätzlichen Aufwand.

Den kannst Du Dir evtl. sparen, wenn Du die Methoden der Basisklasse und der Ableitungen korrekt implementierst.

Zeig sonst evtl. mal Deine Klassendeklaration...
Oh mann, das Problem saß/sitzt wie zumeist vor dem Bildschirm. Ich habe, aus welchem Grunde auch immer, beim Kopieren / Aufteilen der Unterkomponenten das Virtual nicht durch Override ersetzt, und das Problem an einer ganz anderen Stelle vermutet... man sollte sich doch die Compilermeldungen genau durchsehen, heul...

Na zumindest blicke ich jetzt besser bei den Interfaces durch...
Peter
Was mache ich, wenn ein Bär meine Frau angreift?
Nichts. Er hat sie angegriffen, soll er doch selber sehen, wie er sich verteidigt.
  Mit Zitat antworten Zitat
Der schöne Günther

Registriert seit: 6. Mär 2013
6.110 Beiträge
 
Delphi 10 Seattle Enterprise
 
#9

AW: Alternative zu Typecast => IInterface?

  Alt 17. Nov 2016, 12:42
Aber Jünge!

Du müsstest doch eine Compiler-Warnung bekommen
Delphi-Quellcode:
type
   TBase = class
      public procedure stuff(); virtual;
   end;

   TSub = class(TBase)
      public procedure stuff(); virtual;
   end;
in Form von
Zitat:
[dcc32 Warnung] Project15.dpr(15): W1010 Methode 'stuff' verbirgt virtuelle Methode vom Basistyp 'TBase'

Oder?
  Mit Zitat antworten Zitat
Benutzerbild von bytecook
bytecook

Registriert seit: 6. Aug 2010
Ort: Dornbirn
151 Beiträge
 
Delphi 11 Alexandria
 
#10

AW: Alternative zu Typecast => IInterface?

  Alt 17. Nov 2016, 12:54
Aber Jünge!

Du müsstest doch eine Compiler-Warnung bekommen
Delphi-Quellcode:
type
   TBase = class
      public procedure stuff(); virtual;
   end;

   TSub = class(TBase)
      public procedure stuff(); virtual;
   end;
in Form von
Zitat:
[dcc32 Warnung] Project15.dpr(15): W1010 Methode 'stuff' verbirgt virtuelle Methode vom Basistyp 'TBase'

Oder?
es compilierte ...

Wie gesagt hatte ich mir die Warnungen nicht angesehen, dort stands ja drinnen...

[dcc32 Warnung] SVFXCommands.pas(189): W1010 Methode 'Process' verbirgt virtuelle Methode vom Basistyp 'TSVFXVisualCmd'

Code:
  (******************************************************************************)
  (******************************************************************************)
  (** TSVFXVisualCmd                                                          **)
  (******************************************************************************)
  (******************************************************************************)
  TSVFXVisualCmd = Class (TSVFXCustomCmd)
  private
    FGroupIndex                                              : UInt32;
    FConstraints                                             : TSVFXSizeConstraints;
    FShift                                                   : TFloatPoint;
    FConstraintValue                                         : TFloatPoint;

  protected
    procedure DoConstraintsChanged (Sender : TObject); Virtual;
    function GetConstrainedSize  (Const Value: TFloatPoint) : TFloatPoint; Virtual;
    function GetPoint            () : TPointInfo; Virtual; Abstract;
    function GetPreviousPoint    () : TPointInfo; Virtual;
    function GetSize : TFloatPoint; Virtual;
    function Process (APath : TFlattenedPath) : Boolean; Virtual; Abstract;

    property GroupIndex : UInt32
             read FGroupIndex
             write FGroupIndex;

    property Constraints : TSVFXSizeConstraints
             read fConstraints
             write fConstraints;
  public
    Constructor Create (AOwner : TSVFXCommands; Const ADescription : String); Override;
    Destructor Destroy; Override;

    procedure UpdateShift (Const Value : TFloatPoint); Virtual; Abstract;
    procedure UpdateValues; Virtual; Abstract;

    (** Der Befehl UpdateValues aktualisiert die "Arbeitsvariablen_", die u.a zur Pfadbildung   **)
    (** verwendet werden.                                                                       **)
    function ValuesAsText () : String; Virtual;

    property ConstraintValue : TFloatPoint
             read fConstraintValue;

    property Size : TFloatPoint
             read GetSize;

  end;


  (******************************************************************************)
  (******************************************************************************)
  (** TSVFXMoveTo - TSVFXCommand                                              **)
  (** Bewegt den Stift an die Position Startpoint                             **)
  (******************************************************************************)
  (******************************************************************************)
  TSVFXMoveTo = Class (TSVFXVisualCmd)
  private
    fPoint                                                   : TFloatPoint;
    fPoint_                                                   : TFloatPoint;
    FDirections                                              : TSVFXDirections;
    procedure SetPoint (Const Value : TFloatPoint);
  protected
    procedure AssignTo (Dest : TPersistent); Override;
    function Read1000 (AStream : TStream) : Integer; Override;
    function GetPoint : TPointInfo; Override;
    function GetPreviousPoint : TPointInfo; Override;
  public
    Constructor Create(AOwner : TSVFXCommands; Const ADescription : String); Override;

    function LoadFromStream (AStream : TStream) : Integer; Override;
    function SaveToStream  (AStream : TStream) : Integer; Override;
    function ValuesAsText  () : String; Override;
    procedure UpdateShift   (Const Value : TFloatPoint); Override;
    procedure UpdateValues  (); Override;
    function Process       (APath : TFlattenedPath) : Boolean; virtual;

    property Point : TFloatPoint
             read fPoint
             write SetPoint;

    property Directions : TSVFXDirections
             read FDirections
             write FDirections;

    property Flag;
    property GroupIndex;
  end;
Peter
Was mache ich, wenn ein Bär meine Frau angreift?
Nichts. Er hat sie angegriffen, soll er doch selber sehen, wie er sich verteidigt.
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:26 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz