Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Attribute überschreiben (https://www.delphipraxis.net/186100-attribute-ueberschreiben.html)

Rainer Wolff 3. Aug 2015 14:56

Delphi-Version: 5

Attribute überschreiben
 
Ich habe in einem Projekt eine Business-Klasse, die mit Attributen dekoriert ist, um daraus automatisch ein Eingabeformular zu generieren (Beschriftung, PosX, PosY...).
Beispiel:
Code:
[Numeric('Bahngeschwindigkeit','',134,59)]
Diese Klasse überschreibe ich, um zusätzliche Properties einzufügen.
Gibt es eine Möglichkeit, in der abgeleiteten Klasse auch Attribute der Basisklasse zu überschreiben (z.b. eine anderssprachige Beschriftung, andere PosX...)

Vielen Dank

Der schöne Günther 3. Aug 2015 15:33

AW: Attribute überschreiben
 
Hätte man doch einfach mal ausprobieren können, oder?

Delphi-Quellcode:
program Project2;

{$APPTYPE CONSOLE}

{$R *.res}

uses System.SysUtils, System.Rtti;

type
   NumericAttribute = class(System.TCustomAttribute)
      public var
         someNumber:   Integer;
      public
         constructor Create(const someNumber: Integer);
   end;

   [Numeric(42)]
   TBase = class(TObject)
      // Stub
   end;

   [Numeric(99)]
   TSub = class(TBase)
      // TStub
    end;

{ TNumericAttribute }

constructor NumericAttribute.Create(const someNumber: Integer);
begin
   inherited Create();
   self.someNumber := someNumber;
end;

procedure printNumericInfo(const ofType: TRttiType);
var
   attribute: TCustomAttribute;
   numeric: NumericAttribute;
begin
   for attribute in ofType.GetAttributes() do begin
      if not (attribute is NumericAttribute) then Continue;
         numeric := attribute as NumericAttribute;
         Write( ofType.ToString() );
         Write(' has a numeric value of ');
         WriteLn(numeric.someNumber);
   end;
end;

procedure justRttiThings();
var
   ctx: TRttiContext;
   base: TBase;
   sub: TSub;

begin
   ctx := TRttiContext.Create();
   base := TBase.Create();
   sub := TSub.Create();

   printNumericInfo( ctx.GetType(base.ClassType) );
   printNumericInfo( ctx.GetType(sub.ClassType) );
end;

begin
  try
   justRttiThings();
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

  readln;
end.
ergibt bei mir
Code:
TBase has a numeric value of 42
TSub has a numeric value of 99

Oder habe ich das falsch verstanden?

Rainer Wolff 3. Aug 2015 15:56

AW: Attribute überschreiben
 
Ausprobiert hab ich das anhand meiner Klassen schon, da kam aber was anderes bei raus, weil die Attribute auch etwas anders sitzen:

Code:
TBaseClass = class(TControl)
published
  [Checkbox('Ich bin die Beschriftung',10,10)]
  property Eigenschaft1: Boolean read ... write...;
end

TChildclass = class(TBaseClass)
published
  [Checkbox('Now i am an english caption at different position',20,20)]
  property Eigenschaft1: Boolean;
  [Checkbox('I have a second property',30,30]
  property Eigenschaft2: Boolean read... write...;
end;

procedure ErstelleTForm();
var
  Liste: TList<TControl>;
  element: TControl;
begin
  // so in etwa
  Liste.Add(TBaseClass.Create);
  Liste.Add(TChildclass.Create);
  for element in liste do
  begin
    für alle Propertys in Klasse // mit RTTI
    wenn Attribut(Checkbox)
      Erstelle TCheckbox in Formular an PosX, PosY mit Caption (und mach gleich ein Databinding zwischen Checkbox und property)
  end;
end
und da taucht halt dann bei Eigenschaft1 das Attribut aus der Basisklasse auf, das ich gerne ersetzt hätte.

Dejan Vu 3. Aug 2015 16:37

AW: Attribute überschreiben
 
Grundsätzlich ist es kein gutes OOP, eine Eigenschaft mit einer konkreten Bedeutung in einer Kindklasse zu überschreiben und ihm dabei eine andere Bedeutung zu geben ('Liskov Prinzip', das 'L' in SOLID).

Vielleicht solltest Du eine Basisklasse ohne Attribute einführen und die Attribute erst in den unterschiedlichen Ableitungen setzen.

Rainer Wolff 4. Aug 2015 06:55

AW: Attribute überschreiben
 
Grundsätzlich ist es ja auch nicht mein Willen, einer Eigenschaft eine andere Bedeutung zu geben. Es geht ja nur darum, dem Feld eine andere (englische) Beschriftung und eine andere Position zu geben.

Die Basisklasse funktioniert natürlich, da ich die Klasse in verschiedenen Projekten aber leider schon so verbaut habe, wollte ich mir die Änderungen am liebsten sparen.

Sir Rufo 4. Aug 2015 07:47

AW: Attribute überschreiben
 
Willst du wirklich für jede Sprache eine eigene Klasse erstellen?

Nimm doch einfach eine Klasse mit den Attributen und beim Auslesen der Attribute holst du dir die Werte, die zu der aktuellen Sprache hinterlegt sind.

Findest du keinen Wert, dann wird eben der Wert genommen, der dort hinterlegt ist.

BUG 4. Aug 2015 08:19

AW: Attribute überschreiben
 
Zitat:

Zitat von Sir Rufo (Beitrag 1310864)
Nimm doch einfach eine Klasse mit den Attributen und beim Auslesen der Attribute holst du dir die Werte, die zu der aktuellen Sprache hinterlegt sind.

Wie es halt die üblichen Verdächtigen so machen ...

Per Hand im Code die Position der GUI-Elemente zu bestimmen finde ich auch etwas gruselig.

frankyboy1974 4. Aug 2015 10:15

AW: Attribute überschreiben
 
Hallo,

du möchtest also einen String in dem steht "Ich bin der Martin" und dann überschreibst du diesen String und nun steht da drin "Nee ich heisse doch Detlev". Verstehe ich dass richtig???? Also in C++ kannst du auch Operatoren überladen.

mfg

frank

Rainer Wolff 4. Aug 2015 11:44

AW: Attribute überschreiben
 
Zitat:

Zitat von frankyboy1974 (Beitrag 1310891)
Hallo,

du möchtest also einen String in dem steht "Ich bin der Martin" und dann überschreibst du diesen String und nun steht da drin "Nee ich heisse doch Detlev". Verstehe ich dass richtig???? Also in C++ kannst du auch Operatoren überladen.

mfg

frank

Nein! Im Grund will ich dynamisch aus einer Businessklasse ein Edit-Formular erstellen. Diese Businessklasse kommt mit leichten Variationen (und gewissen Eigenschaften, die grundsätzlich immer vorhanden sind) in vielen verschiedenen Projekten zum Einsatz.

Ich will nur dieses Eingabeformular etwas individueller gestalten, konkret, weil ich jetzt die Variante englische Sprache brauche. Das Ding muss auch nicht mehrsprachig werden, die verschiedenen Versionen gehen halt an verschiedene Kunden in jeweils einer Landessprache.

Ich hatte auch nicht nach Möglichkeiten gesucht, das ganze anders zu programmieren, sondern wollte nur konkret wissen, ob ich diese Attribute variieren kann. Offensichtlich geht das nicht, dann werde ich wohl oder übel zu der Variante Basisklasse ohne Attribute und abgeleitete Klassen mit Attributen wechseln, was halt Änderungen an den bestehenden Projekten mitbringt, was ich eigentlich vermeiden wollte.

Danke für die Vorschläge

BUG 4. Aug 2015 12:05

AW: Attribute überschreiben
 
Zitat:

Zitat von frankyboy1974 (Beitrag 1310891)
Verstehe ich dass richtig????

Guck dir doch das Codebeispiel an, es geht im Attribute von Eigenschaften.

Zitat:

Zitat von frankyboy1974 (Beitrag 1310891)
Also in C++ kannst du auch Operatoren überladen.

Das ist nicht falsch, hilft hier aber wenig :mrgreen:


Wenn es überhaupt funktioniert, dann steckt das Problem vermutlich irgendwo in
Delphi-Quellcode:
für alle Propertys in Klasse // mit RTTI
.

Dejan Vu 4. Aug 2015 17:39

AW: Attribute überschreiben
 
Zitat:

Zitat von Rainer Wolff (Beitrag 1310913)
Im Grund will ich dynamisch aus einer Businessklasse ein Edit-Formular erstellen. Diese Businessklasse kommt mit leichten Variationen (und gewissen Eigenschaften, die grundsätzlich immer vorhanden sind) in vielen verschiedenen Projekten zum Einsatz.

Aha! Ein legitimes Beispiel.

Dann würde ich trotzdem den Weg gehen, eine Basisklasse ohne Attribute zu erzeugen. Zusätzlich kannst Du in deiner Library noch die Standardausprägung vorhalten. Bei Ausnahmen würde ich aber wieder von der Basisklasse ableiten. Weiterhin würde ich unbedingt Standardbeschriftungen als Konstanten ablegen.

Was die Mehrsprachigkeit anbelangt, würde ich zu einem guten Lokalisierungstool greifen und nicht mit den Attributen rumwerkeln. Dann musst Du nämlich bei jeder Sprachanpassung (Tippfehler o.ä.) ein neues Release bauen. Das wird irgendwann lästig. Vor allen Dingen, wenn Du (weiß ich ja nicht), noch eine Testabteilung hast, die jedes neue *Release* erst einmal 10 Tage durchtesten muss.

Sir Rufo 4. Aug 2015 18:04

AW: Attribute überschreiben
 
Am BusinessObject ist das aber eher falsch platziert, da man sehr oft noch zusätzliche Daten benötigt (Items in der ComboBox) oder die Daten vorher umwandeln, aufteilen, ... muss, damit diese vernünftig angezeigt werden können.

Will man das mit aller Gewalt, dann überfrachtet man das BO mit Dingen, die da eigentlich nicht hingehören. Da ist es immer ratsam eine eigene Klasse zu nehmen, die dann das BO kapselt und diesen ganzen Konvertierungs-Schnickschnack übernimmt.

Diese Klasse kann man dann auch zuverlässig auf eine Handvoll Datentypen einschränken, bzw. spezielle Datentypen vorsehen, die sich gut zur Präsentation eignen.

Dejan Vu 5. Aug 2015 06:07

AW: Attribute überschreiben
 
Richtig, eigentlich ist es das Viewmodel, wo solche Attribute angebracht sind. Das verlagert das Problem aber nur.

Stevie 5. Aug 2015 06:37

AW: Attribute überschreiben
 
Zitat:

Zitat von Dejan Vu (Beitrag 1310813)
Grundsätzlich ist es kein gutes OOP, eine Eigenschaft mit einer konkreten Bedeutung in einer Kindklasse zu überschreiben und ihm dabei eine andere Bedeutung zu geben ('Liskov Prinzip', das 'L' in SOLID).

Das Ändern einer Caption oder einer Eigenschaft von 10 auf 30 verletzt wohl kaum das LSP.

Einfach merken, welche Eigenschaften man überschrieben hat:

Delphi-Quellcode:
program Project104;

{$APPTYPE CONSOLE}

uses
  Controls,
  Generics.Collections,
  Rtti,
  SysUtils;

type
  CheckboxAttribute = class(TCustomAttribute)
    constructor Create(const caption: string; X, Y: Integer);
  end;

constructor CheckboxAttribute.Create(const caption: string; X, Y: Integer);
begin
end;

type
  TBaseClass = class(TControl)
  private
    FEigenschaft1: Boolean;
  published
    [Checkbox('Ich bin die Beschriftung',10,10)]
    property Eigenschaft1: Boolean read FEigenschaft1 write FEigenschaft1;
  end;

  TChildclass = class(TBaseClass)
  private
    FEigenschaft2: Boolean;
  published
    [Checkbox('Now i am an english caption at different position',20,20)]
    property Eigenschaft1;
    [Checkbox('I have a second property',30,30)]
    property Eigenschaft2: Boolean read FEigenschaft2 write FEigenschaft2;
  end;

var
  props: TDictionary<string,Boolean>;
  ctx: TRttiContext;
  t: TRttiType;
  p: TRttiProperty;
  a: TCustomAttribute;
begin
  props := TDictionary<string,Boolean>.Create;
  t := ctx.GetType(TChildclass);
  for p in t.GetProperties do
  begin
    if props.ContainsKey(p.Name) then
      Continue
    else
      props.Add(p.Name,True);
    for a in p.GetAttributes do
      if a is CheckboxAttribute then
        Writeln(p.Parent.Name + ' ' + p.ToString);
  end;
  Readln;
end.

Dejan Vu 5. Aug 2015 07:22

AW: Attribute überschreiben
 
Wieso nicht? Ich überschreibe/ändere das ursprüngliche Bedeutung. Ob dies nun zu einem veränderten Verhalten oder Darstellung (was ist daran kein Verhalten?) führt, ist -streng genommen- irrelevant.

Gefällt Dir das?
Delphi-Quellcode:
Type
  TBaseClass = class
    Function Foo : String; Virtual;
  end;

  TDerivedClass = class (TBaseClass)
    Function Foo : String; Override;
  end;

Function TBaseClass.Foo: String;
Begin
  Result := 'Foo';
End;

Function TDerivedClass.Foo : String;
Begin
  Inherited; // Ob mit oder ohne, wurscht.
  Result := 'Bar';
End;
Mir jedenfalls nicht. Ist aber Geschmackssache.

Stevie 5. Aug 2015 08:11

AW: Attribute überschreiben
 
Ich glaube, du hast das LSP falsch verstanden - ich zitiere mal Wikipedia:
"Es besagt, dass ein Programm, das Objekte einer Basisklasse T verwendet, auch mit Objekten der davon abgeleiteten Klasse S korrekt funktionieren muss, ohne dabei das Programm zu verändern."

Die Anzeige einer anderen Caption oder Position einer Checkbox ist somit keine Verletzung.

Dein Beispiel ist Nonsense denn hier ergibt sich keine Funktionsveränderung sondern bloß ein anderer Rückgabewert. Eine veränderte Funktionsweise ergäbe sich dadurch, dass irgendwo jemand auf Foo oder Bar abprüft und dementsprechend was anderes ausführt. Da man aber nicht die Spezifikation der Funktion Foo kennt (was soll die machen? - und nein, die soll "Foo" zurückliefern ist wohl kaum eine realistische Spec, dann hätte man sie nicht virtual gemacht), kann hier keiner Sagen, ob LSP verletzt wurde oder nicht.


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:41 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